// P4ListBrowse.cpp : implementation file
//

#include "stdafx.h"
#include "p4win.h"
#include "MainFrm.h"
#include "P4Object.h"
#include "P4ListBrowse.h"
#include "StringUtil.h"
#include "RegKeyEx.h"
#include "cmd_describe.h"
#include "hlp\p4win.hh"
#include "P4ImageList.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


static LPCTSTR sRegKey = _T("Software\\Perforce\\P4Win\\Layout\\");
static LPCTSTR sRegValue_ColumnWidths = _T("Column Widths");
static LPCTSTR sRegValue_SortColumns = _T("Sort Columns");

#define UPDATE_STATUS(x) ((CMainFrame *)AfxGetMainWnd())->UpdateStatus(x)
int CALLBACK P4ListBrowseSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
#define IMG_INDEX(x) (x-IDB_PERFORCE)


// Module global for use in sort callback
CP4ListBrowse *pDlg;
int viewType;


/////////////////////////////////////////////////////////////////////////////
// CP4ListBrowse dialog


CP4ListBrowse::CP4ListBrowse(CWnd* pParent, BOOL bWiz/*=FALSE*/, BOOL bBranchInteg/*=FALSE*/)
	: CDialog(CP4ListBrowse::IDD, pParent)
{
	//{{AFX_DATA_INIT(CP4ListBrowse)
	//}}AFX_DATA_INIT
	viewType = m_viewType;
	m_pParent = pParent;
	m_Wiz = bWiz;
	m_BranchInteg = bBranchInteg;
	m_SortAscending = m_FilterByHost = m_IsFiltered = FALSE;
	m_LastSortColumn= 0;
	pDlg=this;
	m_InitRect.SetRect(0,0,100,100);
	m_WinPos.SetWindow( this, bBranchInteg ? _T("IntegDlg") : _T("P4ListBrowse") );
	for (int i = -1; ++i < MAX_SORT_COLUMNS; )
		m_SortColumns[i] = 0;
}


void CP4ListBrowse::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CP4ListBrowse)
	DDX_Control(pDX, IDC_P4LIST, m_P4ListCtrl);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CP4ListBrowse, CDialog)
	//{{AFX_MSG_MAP(CP4ListBrowse)
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_P4LIST, OnColumnclickP4list)
	ON_NOTIFY(NM_DBLCLK, IDC_P4LIST, OnDblclickP4list)
	ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
	ON_BN_CLICKED(IDC_DESCRIBE, OnDescribe)
	ON_BN_CLICKED(IDC_BACK, OnBack)
//	ON_BN_CLICKED(IDHELP, OnHelp)
//	ON_WM_HELPINFO()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CP4ListBrowse message handlers

BOOL CP4ListBrowse::OnInitDialog() 
{
	CDialog::OnInitDialog();
	// Record the initial window size, then see if there is a registry preference
	GetWindowRect(&m_InitRect);
	m_LastRect = m_InitRect;
	if (!m_Wiz)
		m_WinPos.RestoreWindowPosition();

	CString str;

	SetWindowText(m_caption);
	SetFont(m_Font);
	
	// Modify the list control style so that the entire selected row is highlighted
	DWORD dwStyle = ::SendMessage(m_P4ListCtrl.m_hWnd,LVM_GETEXTENDEDLISTVIEWSTYLE,0,0);
	dwStyle |= LVS_EX_FULLROWSELECT;
	::SendMessage(m_P4ListCtrl.m_hWnd,LVM_SETEXTENDEDLISTVIEWSTYLE,0,dwStyle);

	// Make sure list control shows selection when not the focused control
	m_P4ListCtrl.ModifyStyle(0, LVS_SHOWSELALWAYS, 0);

	// Set the imagelist
	m_P4ListCtrl.SetImageList(TheApp()->GetImageList(), LVSIL_SMALL);

	if (m_Wiz)
	{
		GetDlgItem(IDC_BACK)->ShowWindow(SW_SHOW);
		GetDlgItem(IDC_BACK)->EnableWindow(TRUE);
		GetDlgItem(IDOK)->SetWindowText(LoadStringResource(IDS_FINISH));
		m_Hostname = GET_P4REGPTR()->GetHostname();
		m_FilterByHost = TRUE;
		if (!m_IsFiltered)
		{
			GetDlgItem(IDC_REFRESH)->EnableWindow(FALSE);
			GetDlgItem(IDC_REFRESH)->ShowWindow(SW_HIDE);
		}
	}
	else if (m_BranchInteg)
	{
		GetDlgItem(IDC_BACK)->ShowWindow(SW_SHOW);
		GetDlgItem(IDC_BACK)->EnableWindow(FALSE);
		GetDlgItem(IDOK)->SetWindowText(LoadStringResource(IDS_NEXT));
	}

	if (m_IsFiltered)
		GetDlgItem(IDC_REFRESH)->SetWindowText(LoadStringResource(IDS_CLEARFILTERR));

	// Get original size of control
	CRect rect;
	m_P4ListCtrl.GetWindowRect(&rect);

	int colwidth[MAX_P4OBJECTS_COLUMNS]={90,90,90,90,90,90,90,90,90,90};

	// make sure OnSize gets called to reposition controls
	// if restored position is default, this won't happen unless
	// we force it
	GetClientRect(&rect);
	SendMessage(WM_SIZE, 0, MAKELONG(rect.Width(), rect.Height()));

	// Get new size of control after resized as specified in the registry
	m_P4ListCtrl.GetWindowRect(&rect);

	// Get any saved column widths from registry
	RestoreSavedWidths(colwidth, MAX_P4OBJECTS_COLUMNS);

	// Make sure no column completely disappeared (because you can't get it back then)
	for (int i=-1; ++i < MAX_P4OBJECTS_COLUMNS; )
	{
		if (colwidth[i] < 5)
			colwidth[i] = 5;
	}
	// Use the same font as the calling window
	m_P4ListCtrl.SetFont(m_Font);

	// Insert the columns 
	int maxcols = m_ColNames->GetSize();
	int width=GetSystemMetrics(SM_CXVSCROLL);;
	int retval;
	LV_COLUMN lvCol;
	for(int subItem=0; subItem < maxcols; subItem++)
	{
		lvCol.mask= LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT |LVCF_WIDTH;
		lvCol.fmt=LVCFMT_LEFT;
		lvCol.pszText=( LPTSTR )( LPCTSTR ) m_ColNames->GetAt( subItem );
		lvCol.iSubItem=subItem;
		if(subItem < maxcols-1)
		{
			lvCol.cx=colwidth[subItem];
			width+=lvCol.cx;
		}
		else
			lvCol.cx=max(colwidth[subItem], rect.Width() - width - 4);  // expand last column to fill window
		retval=m_P4ListCtrl.InsertColumn(subItem, &lvCol);
	}
 
	AddTheListData();

	EnumChildWindows(AfxGetMainWnd()->m_hWnd, ChildSetRedraw, TRUE);
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CP4ListBrowse::AddTheListData()
{
	// Add the data
	int maxcols = m_ColNames->GetSize();
	CP4Object *p4Object;
	LV_ITEM lvItem;
	int iActualItem = -1;
	CString txt;
	POSITION pos= m_pP4List->GetHeadPosition();
	int iItem;
	for(iItem=0; iItem < m_pP4List->GetCount(); iItem++)
	{
		p4Object= (CP4Object *) m_pP4List->GetNext(pos);

		if (m_FilterByHost)
		{
			txt = p4Object->GetField(2);
			if (!txt.IsEmpty() && txt.CompareNoCase(m_Hostname))
				continue;
		}
		
		for(int subItem=0; subItem < maxcols; subItem++)
		{
			lvItem.mask=LVIF_TEXT | 
					((subItem==0) ? LVIF_IMAGE : 0) |
					((subItem==0) ? LVIF_PARAM : 0);
			lvItem.iItem= (subItem==0) ? iItem : iActualItem;
            ASSERT(lvItem.iItem != -1);
			lvItem.iSubItem= subItem;
			lvItem.iImage = m_iImage;
			lvItem.lParam=(LPARAM) p4Object;
			txt=PadCRs(p4Object->GetField(subItem));
			lvItem.pszText = const_cast<LPTSTR>( (LPCTSTR ) txt);

			if(subItem==0)
				iActualItem=m_P4ListCtrl.InsertItem(&lvItem);
			else
				m_P4ListCtrl.SetItem(&lvItem);
		}
	}

	if(m_pP4List->GetCount())
	{
		// Sort the P4Objects list the same way the P4ListView is sorted
		viewType = m_viewType;
		m_P4ListCtrl.SortItems( P4ListBrowseSort, m_LastSortColumn );

		// Find the p4Object currently selected in the P4Objects pane
		iItem=0;
		if (!m_CurrP4Object->IsEmpty())
		{
			TCHAR cur[ LISTVIEWNAMEBUFSIZE + 1 ];
			TCHAR str[ LISTVIEWNAMEBUFSIZE + 1 ];

			lstrcpy(cur, m_CurrP4Object->GetBuffer(m_CurrP4Object->GetLength()+1));
			m_CurrP4Object->ReleaseBuffer(-1);
			for (int cnt = ListView_GetItemCount( m_P4ListCtrl.m_hWnd ); iItem < cnt; iItem++ )
			{
				ListView_GetItemText( m_P4ListCtrl.m_hWnd, iItem, 0, str, LISTVIEWNAMEBUFSIZE );
				if( !Compare(cur, str) )
					break;
			}
			if (iItem >= ListView_GetItemCount( m_P4ListCtrl.m_hWnd ))
				iItem = 0;
		}

		// Make sure the same p4Object is selected and visible
		m_P4ListCtrl.SetItemState(iItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
		m_P4ListCtrl.EnsureVisible( iItem, FALSE );
	}

	// And finally, set focus to the list control so that the first 'down'
	// keystroke can be used to scroll down
	m_P4ListCtrl.SetFocus();
}

void CP4ListBrowse::SetP4ObjectList(CObList *P4list)
{
	ASSERT_KINDOF(CObList,P4list);
	m_pP4List= P4list;
}

void CP4ListBrowse::SetP4ObjectCols(CStringArray *P4Cols)
{
	m_ColNames= P4Cols;
}

void CP4ListBrowse::SetP4ObjectCurr(CString *P4Name)
{
	m_CurrP4Object= P4Name;
}

void CP4ListBrowse::OnOK() 
{
	if (!m_Wiz)
		m_WinPos.SaveWindowPosition();

	int nItem=m_P4ListCtrl.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED );
	if (nItem != -1)
	{
		TCHAR str[ LISTVIEWNAMEBUFSIZE + 1 ];
		ListView_GetItemText( m_P4ListCtrl.m_hWnd, nItem, 0, str, LISTVIEWNAMEBUFSIZE );
		m_SelectedP4Object = str;
	}
	else
		m_SelectedP4Object.Empty();
	
	EndDialog(IDOK);
}

void CP4ListBrowse::OnCancel() 
{
	if (!m_Wiz)
		m_WinPos.SaveWindowPosition();	
	CDialog::OnCancel();
}

void CP4ListBrowse::OnBack() 
{
	EnumChildWindows(AfxGetMainWnd()->m_hWnd, ChildSetRedraw, FALSE);
	EndDialog(IDC_BACK);
}

void CP4ListBrowse::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	
	// Slide the OK and Cancel buttons to the bottom of dlg
	CWnd *pRefresh = GetDlgItem(IDC_REFRESH);
	CWnd *pBack = GetDlgItem(IDC_BACK);
	CWnd *pOK   = GetDlgItem(IDOK);
	CWnd *pCancel = GetDlgItem(IDCANCEL);
	CWnd *pDescribe = GetDlgItem(IDC_DESCRIBE);

	if(!pOK)
		return;

	CRect rect;
	GetWindowRect(&rect);
	int dx = rect.Width() - m_LastRect.Width();
	int dy = rect.Height() - m_LastRect.Height();
	// Save the new size
	m_LastRect = rect;

	// Move down
	pRefresh->GetWindowRect(&rect);
	ScreenToClient(rect);
	pRefresh->SetWindowPos(NULL, rect.left, rect.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

	// Move down and slide right
	pBack->GetWindowRect(&rect);
	ScreenToClient(rect);
	pBack->SetWindowPos(NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

	pOK->GetWindowRect(&rect);
	ScreenToClient(rect);
	pOK->SetWindowPos(NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

	pCancel->GetWindowRect(&rect);
	ScreenToClient(rect);
	pCancel->SetWindowPos(NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

	pDescribe->GetWindowRect(&rect);
	ScreenToClient(rect);
	pDescribe->SetWindowPos(NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

	// Increase the size of the list both horiz and vert
	CWnd *pList = GetDlgItem(IDC_P4LIST);
	pList->GetWindowRect(&rect);
	pList->SetWindowPos(NULL, 0, 0, rect.right - rect.left + dx, 
								    rect.bottom - rect.top + dy, SWP_NOMOVE | SWP_NOZORDER);
	RedrawWindow();
}

void CP4ListBrowse::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
	lpMMI->ptMinTrackSize.x= m_InitRect.Width();
	lpMMI->ptMinTrackSize.y= m_InitRect.Height();
}

void CP4ListBrowse::OnColumnclickP4list(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	
    if(pNMListView->iSubItem != m_LastSortColumn)
		m_LastSortColumn=pNMListView->iSubItem;
	else
		m_SortAscending= !m_SortAscending;

	viewType = m_viewType;
	if(pNMListView->iItem == -1)
		m_P4ListCtrl.SortItems( P4ListBrowseSort,(DWORD)pNMListView->iSubItem);
	
	*pResult = 0;
}

// Since a sort column can only appear once in our saved column array,
// given the current column, we can determine the next column.
// Note that incoming 'colnbr' is a non-negative 0-relative number and must be incremented
// The return value is a signed 1-relative number; negative for descending, positive for ascending
int CP4ListBrowse::NextSortColumn(int lastcol)
{
	if (lastcol == 0)
		return 0;		// 0 => no next column
	lastcol++;
	for (int i = -1; ++i < (MAX_SORT_COLUMNS-1); )
	{
		if (abs(m_SortColumns[i]) == lastcol)
			return m_SortColumns[i+1];
	}
	return 0;			// 0 => no next column
}

int CALLBACK P4ListBrowseSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	int rc;
	int nextcol;
	CString txt1;
	CString txt2;

	if (viewType == P4CHANGE_SPEC && !lParamSort)
	{
		txt1.Format(_T("%09d"), _tstoi(((CP4Object const *)lParam1)->GetField(lParamSort)));
		txt2.Format(_T("%09d"), _tstoi(((CP4Object const *)lParam2)->GetField(lParamSort)));
	}
	else
	{
		txt1= ((CP4Object const *)lParam1)->GetField(lParamSort);
		txt2= ((CP4Object const *)lParam2)->GetField(lParamSort);
	}

	if(pDlg->IsSortAscending())
		rc = txt1.CompareNoCase(txt2);
	else
		rc = txt2.CompareNoCase(txt1);

	// check for duplicate keys; if so, sort on next sort columns
	if (!rc && lParamSort && ((nextcol = pDlg->NextSortColumn(lParamSort)) != 0))
	{
		// nextcol now contains a value like 1 for col-0-ascending or like -1 for col-0-decending
		BOOL saveSortAscending = pDlg->IsSortAscending();
		if (nextcol < 0)
		{
			nextcol = 0 - nextcol;
			pDlg->SetSortAscending(FALSE);
		}
		else
			pDlg->SetSortAscending(TRUE);
		rc = P4ListBrowseSort(lParam1, lParam2, (LPARAM)(nextcol-1));
		pDlg->SetSortAscending(saveSortAscending);
	}
	return rc;
}

// Check the registry to see if we have recorded the
// column widths lastused for the p4Object pane's list view.
void CP4ListBrowse::RestoreSavedWidths(int *width, int numcols)
{
    CRegKeyEx key;

    CString theKey = sRegKey + m_SubKey;
    if(ERROR_SUCCESS == key.Open(HKEY_CURRENT_USER, theKey, KEY_READ))
    {
        CString result = key.QueryValueString(sRegValue_ColumnWidths);
		CString sortCols = key.QueryValueString(sRegValue_SortColumns);
        if(!result.IsEmpty())
        {
			//		things can go wrong with the registry setting of the 
			//		widths. Use the defaults if the entry is all zeroes.
			//
			if ( result != _T("0,0,0,0,0,0,0,0,0,0") )
				for(int i=0; i< numcols; i++)
					width[i]= GetPositiveNumber(result);
        }
        if(!sortCols.IsEmpty())
        {
			for(int i=0; i< MAX_SORT_COLUMNS; i++)
            {
				m_SortColumns[i]= GetANumber(sortCols);
                if(!i && !m_SortColumns[i])
                    m_SortColumns[i] = 1;
            }
			m_LastSortColumn= abs(m_SortColumns[0]) - 1; // SortColumns are signed & 1-relative
			m_SortAscending = m_SortColumns[0] >= 0 ? TRUE : FALSE;
        }
    }
}

void CP4ListBrowse::OnRefresh() 
{
	EndDialog(IDC_REFRESH);
}

void CP4ListBrowse::OnDblclickP4list(NMHDR* pNMHDR, LRESULT* pResult) 
{
	*pResult = 0;
	OnOK();
}

void CP4ListBrowse::OnDescribe( void )
{
	int nItem=m_P4ListCtrl.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED );
	if (nItem != -1)
	{
		TCHAR str[ LISTVIEWNAMEBUFSIZE + 1 ];
		ListView_GetItemText( m_P4ListCtrl.m_hWnd, nItem, 0, str, LISTVIEWNAMEBUFSIZE );
		m_SelectedP4Object = str;
		if ( m_SelectedP4Object.IsEmpty( ) )
			return;
	}
	else
		return;

	CCmd_Describe *pCmd = new CCmd_Describe;
	pCmd->Init( m_pParent->m_hWnd, RUN_ASYNC );
	pCmd->SetCaller(this);
	pCmd->SetListCtrl(&m_P4ListCtrl);
	if( pCmd->Run( m_viewType, m_SelectedP4Object ) )
	{
		MainFrame()->UpdateStatus( LoadStringResource(IDS_FETCHING_SPEC) );	
		return;
	}
	else
	{
		delete pCmd;
		return;
	}
}

#ifdef	WANTTOFILTERCLIENTSBYHOSTNAME
void CP4ListBrowse::OnFilter() 
{
	m_FilterByHost = !m_FilterByHost;
	m_P4ListCtrl.DeleteAllItems();
	AddTheListData();
}
#endif

#ifdef	HELPWANTEDFORP4LISTBROPWSE
void CP4ListBrowse::OnHelp() 
{
	AfxGetApp()->WinHelp(0);
}

BOOL CP4ListBrowse::OnHelpInfo(HELPINFO* pHelpInfo) 
{
	OnHelp();
	return TRUE;
}
#endif