#include "stdafx.h"
#include "VideoAdvListCtrl.h"
#include "SystemConfiguration.h"
#include "Helpers.h"

#include <vector>

// include resource IDs of english version, for all other languages the IDs MUST BE THE SAME!!!
#include "..\FarCryConfigEng\resource.h"
#include ".\videoadvlistctrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif



enum EColumnIDs
{
	CID_CVARKEY,
	CID_CVARVALUE
};

const int c_iDrawTextXOffset( 5 );

//const COLORREF c_colHilight( GetSysColor( COLOR_HIGHLIGHT ) );
//const COLORREF c_colWindow( GetSysColor( COLOR_WINDOW ) );
//const COLORREF c_colWindowText( GetSysColor( COLOR_WINDOWTEXT ) );

// hardcode colors for ctrl because we want to have color coding for cvars and that shouldn't go 
// in the way of the given windows color scheme
const COLORREF c_colHilight( RGB( 0, 0, 128 ) );
const COLORREF c_colWindow( RGB( 255, 255, 255 ) );
const COLORREF c_colWindowText( RGB( 0, 0, 0 ) );
const COLORREF c_colColorSyntaxString( RGB( 192, 0, 0 ) );
const COLORREF c_colColorSyntaxInt( RGB( 0, 128, 0 ) );
const COLORREF c_colColorSyntaxDouble( RGB( 0, 0, 192 ) );



IMPLEMENT_DYNAMIC( CVideoAdvListCtrl, CListCtrl )



CVideoAdvListCtrl::CVideoAdvListCtrl( const CVarVector& colCVars, CSystemConfiguration& kSysConfig, CEdit& kHelpEditCtrl )
: CListCtrl()
, m_kInplaceEdit()
, m_colCVars()
, m_kSysConfig( kSysConfig )
, m_kHelpEditCtrl( kHelpEditCtrl )
{
	m_colCVars = colCVars;
	for( CVarVector::size_type i( 0 ); i < m_colCVars.size(); ++i )
	{
		m_colCVars[ i ].m_strValue = m_kSysConfig.GetValue( m_colCVars[ i ].m_strKey );
	}	
}



CVideoAdvListCtrl::~CVideoAdvListCtrl()
{
	for( CVarVector::size_type i( 0 ); i < m_colCVars.size(); ++i )
	{
		delete m_colCVars[ i ].m_pkDataValidator;
	}	
}



BEGIN_MESSAGE_MAP( CVideoAdvListCtrl, CListCtrl )
	ON_NOTIFY_REFLECT( NM_DBLCLK, OnNMDblclk )
	ON_NOTIFY_REFLECT( NM_CLICK, OnNMClick )
	ON_NOTIFY_REFLECT( LVN_ITEMCHANGED, OnLvnItemchanged )
END_MESSAGE_MAP()



void 
CVideoAdvListCtrl::Initialize()
{
	// init control
	InsertColumn( CID_CVARKEY, LoadString( IDS_CVARKEY ), LVCFMT_LEFT, 235 );
	InsertColumn( CID_CVARVALUE, LoadString( IDS_CVARVALUE ), LVCFMT_LEFT, 150 );
	SetExtendedStyle( LVS_EX_GRIDLINES );

	// insert cvars into list ctrl
	for( CVarVector::size_type i( 0 ); i < m_colCVars.size(); ++i )
	{
		LVITEM sItem;
		sItem.mask = 0;
		sItem.iSubItem = 0;
		sItem.iItem = GetItemCount();

		InsertItem( &sItem );
	}	
}



void 
CVideoAdvListCtrl::UpdateSystemConfig()
{
	for( CVarVector::size_type i( 0 ); i < m_colCVars.size(); ++i )
	{
		m_kSysConfig.SetValue( m_colCVars[ i ].m_strKey, m_colCVars[ i ].m_strValue );
	}
}



void 
CVideoAdvListCtrl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
	ASSERT( 0 != lpDrawItemStruct );

	CDC* pDC( CDC::FromHandle( lpDrawItemStruct->hDC ) );
	ASSERT( 0 != pDC );

	bool bIsFocused( ( 0 != ( lpDrawItemStruct->itemState &= ODS_SELECTED ) ) ? true : false );
	int iItem( lpDrawItemStruct->itemID );

	PrepareDC( pDC, bIsFocused, m_colCVars[ iItem ].m_pkDataValidator->GetType() );
	DrawCVarKeyColumn( pDC, iItem );
	DrawCVarValueColumn( pDC, iItem );
}



void
CVideoAdvListCtrl::PrepareDC( CDC* pDC, bool bSelected, SCVar::EDataType eDataType )
{
	pDC->SetBkMode( TRANSPARENT );
	if( false != bSelected )
	{
		pDC->SetBkColor( c_colHilight );
		pDC->SetTextColor( c_colWindow );
	}
	else
	{
		pDC->SetBkColor( c_colWindow );
		switch( eDataType )
		{
		case SCVar::STRING:
			{
				pDC->SetTextColor( c_colColorSyntaxString );
				break;
			}
		case SCVar::INT:
			{
				pDC->SetTextColor( c_colColorSyntaxInt );
				break;
			}
		case SCVar::DOUBLE:
			{
				pDC->SetTextColor( c_colColorSyntaxDouble );
				break;
			}
		default:
			{
				pDC->SetTextColor( c_colWindowText );
				break;
			}
		}		
	}
}



void 
CVideoAdvListCtrl::DrawCVarKeyColumn( CDC *pDC, int iItem )
{
	ASSERT( 0 != pDC );
	
	CRect kItemRect;
	GetItemRect( iItem, &kItemRect, LVIR_BOUNDS );

	pDC->FillSolidRect( kItemRect, pDC->GetBkColor() );
	kItemRect.InflateRect( -c_iDrawTextXOffset, 0, 0, 0 );
	CString strText( m_colCVars[ iItem ].m_strKey.c_str() );
	pDC->DrawText( strText, kItemRect, DT_LEFT | DT_VCENTER );
}



void 
CVideoAdvListCtrl::DrawCVarValueColumn( CDC *pDC, int iItem )
{
	ASSERT( 0 != pDC );

	CRect kItemRect;
	GetSubItemRect( iItem, CID_CVARVALUE, LVIR_BOUNDS, kItemRect );

	pDC->FillSolidRect( kItemRect, pDC->GetBkColor() );
	kItemRect.InflateRect( -c_iDrawTextXOffset, 0, 0, 0 );
	CString strText( m_colCVars[ iItem ].m_strValue.c_str() );
	pDC->DrawText( strText, kItemRect, DT_LEFT | DT_VCENTER );
}



void 
CVideoAdvListCtrl::OnNMDblclk( NMHDR *pNMHDR, LRESULT *pResult )
{
	CPoint pt;
	GetCursorPos( &pt );
	ScreenToClient( &pt );

	LVHITTESTINFO sHitInfo;
	sHitInfo.pt = pt;
	sHitInfo.flags = LVHT_ONITEM;
	SubItemHitTest( &sHitInfo );

	if( sHitInfo.iItem >= 0 )
	{
		switch( sHitInfo.iSubItem )
		{
		case CID_CVARVALUE:
			{
				EditInplace( sHitInfo.iItem, sHitInfo.iSubItem );
				break;
			}
		default:
			{
			}
		}
		Update( sHitInfo.iItem );
	}
	*pResult = 0;
}



void
CVideoAdvListCtrl::OnNMClick( NMHDR *pNMHDR, LRESULT *pResult )
{
	CPoint pt;
	GetCursorPos( &pt );
	ScreenToClient( &pt );

	LVHITTESTINFO sHitInfo;
	sHitInfo.pt = pt;
	sHitInfo.flags = LVHT_ONITEM;
	SubItemHitTest( &sHitInfo );

	UpdateHelp( sHitInfo.iItem );

	*pResult = 0;
}



void 
CVideoAdvListCtrl::OnLvnItemchanged( NMHDR *pNMHDR, LRESULT *pResult )
{
	LPNMLISTVIEW pNMLV( reinterpret_cast< LPNMLISTVIEW >( pNMHDR ) );
	UpdateHelp( pNMLV->iItem );
	*pResult = 0;
}



void 
CVideoAdvListCtrl::EditInplace( int iItem, int iSubItem )
{
	m_kInplaceEdit.SetInplaceInfo( &m_colCVars[ iItem ], iSubItem );
	m_kInplaceEdit.SetParentListCtrl( this );

	CRect kItemRect;    
	if( 0 == iSubItem )
	{
		GetItemRect( iItem, &kItemRect, LVIR_LABEL );
	}
	else
	{
		GetSubItemRect( iItem, iSubItem, LVIR_LABEL, kItemRect );
	}

	kItemRect.InflateRect( -1, 0, 0, -1 );
	m_kInplaceEdit.Create( WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, kItemRect, this, 0 );

	CFont* pkFont( GetFont() );
	m_kInplaceEdit.SetFont( pkFont, FALSE );

	m_kInplaceEdit.SetFocus();
}



void 
CVideoAdvListCtrl::UpdateHelp( int iItem )
{
	// select entire edit ctrl content and replace it
	m_kHelpEditCtrl.SetSel( 0, -1 ); 
	if( -1 == iItem || 0 == m_colCVars[ iItem ].m_uiHelpID )
	{
		m_kHelpEditCtrl.ReplaceSel( _T( "" ) );
	}
	else
	{
		m_kHelpEditCtrl.ReplaceSel( LoadString( m_colCVars[ iItem ].m_uiHelpID ) );
	}
	// set cursor to beginning of these (edit will will scroll up if necessary)
	m_kHelpEditCtrl.SetSel( 0, 0 ); 
}



//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////



IMPLEMENT_DYNAMIC( CInplaceEdit, CEdit )



CInplaceEdit::CInplaceEdit()
: m_pCVar( 0 )
, m_iToEdit( 0 )
, m_pkVideoAdvListCtrl( 0 )
{
}



CInplaceEdit::~CInplaceEdit()
{
}



BEGIN_MESSAGE_MAP( CInplaceEdit, CEdit )
	ON_CONTROL_REFLECT( EN_KILLFOCUS, OnEnKillFocus )
	ON_WM_GETDLGCODE()
	ON_WM_CREATE()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()



void 
CInplaceEdit::SetInplaceInfo( SCVar* pCVar, int iToEdit )
{
	m_pCVar = pCVar;
	m_iToEdit = iToEdit;
}



void 
CInplaceEdit::OnEnKillFocus()
{
	DoEditValidation();
	DestroyWindow();
}



UINT 
CInplaceEdit::OnGetDlgCode()
{
	return( DLGC_WANTALLKEYS );
}



int 
CInplaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if( -1 == CEdit::OnCreate( lpCreateStruct ) )
	{
		return( -1 );
	}

	CString strInitialText;
	switch( m_iToEdit )
	{
	case CID_CVARKEY:
		{
			ASSERT( 0 ); // cannot edit variable key
			break;
		}
	case CID_CVARVALUE:
		{
			strInitialText = m_pCVar->m_strValue.c_str();
			break;
		}
	default:
		{
			ASSERT( 0 ); // invalid mapping
		}
	}

	SetWindowText( strInitialText );
	SetSel( 0, -1 );

	return( 0 );
}



void 
CInplaceEdit::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
	switch( nChar )
	{
	case VK_RETURN:
		{
			DoEditValidation();
			DestroyWindow();
			break;
		}
	case VK_ESCAPE:
		{
			DestroyWindow();
			break;
		}
	default:
		{
			CEdit::OnKeyDown( nChar, nRepCnt, nFlags );
		}
	}
}


void 
CInplaceEdit::DoEditValidation()
{
	switch( m_iToEdit )
	{
	case CID_CVARKEY:
		{
			ASSERT( 0 ); // cannot edit variable key
			break;
		}
	case CID_CVARVALUE:
		{
			CString strText;
			GetWindowText( strText );
			if( false != m_pCVar->m_pkDataValidator->Validate( strText ) )
			{
#ifdef UNICODE
				size_t length( strText.GetLength() );
				std::vector< char > cStr( length + 1 );
				wcstombs( &cStr[ 0 ], strText, length );
				cStr[ length ] = 0;
				m_pCVar->m_strValue = &cStr[ 0 ];
#else
				m_pCVar->m_strValue = strText;
#endif

			}
			break;
		}
	default:
		{
			ASSERT( 0 ); // invalid mapping
			break;
		}
	}
}



void 
CInplaceEdit::SetParentListCtrl( CVideoAdvListCtrl* pkParent )
{
	ASSERT( 0 != pkParent );
	m_pkVideoAdvListCtrl = pkParent;
}

