////////////////////////////////////////////////////////////////////////////////
// Plainamp, Open source Winamp core
// 
// Copyright © 2005  Sebastian Pipping <webmaster@hartwork.org>
// 
// -->  http://www.hartwork.org
// 
// This source code is released under the GNU General Public License (GPL).
// See GPL.txt for details. Any non-GPL usage is strictly forbidden.
////////////////////////////////////////////////////////////////////////////////


#include "Prefs.h"
#include "Util.h"
#include "Font.h"
#include "Console.h"
#include <map>

using namespace std;


#define CLASSNAME_PREFS  TEXT( "CLASSNAME_PREFS" )
#define TITLE_PREFS      TEXT( "Preferences" )


#define PAGE_WIDTH       409
#define PAGE_HEIGHT      400


#define GAP_LEFT         4
#define GAP_RIGHT        5
#define GAP_TOP          4
#define GAP_BOTTOM       5
#define SPLITTER_WIDTH   6
#define SPLITTER_HEIGHT  6

#define BOTTOM_SPACE     36

#define BUTTON_WIDTH     80


#define PREFS_WIDTH      606
#define PREFS_HEIGHT     ( GAP_TOP + PAGE_HEIGHT + BOTTOM_SPACE )



struct PrefRecCompare
{
	bool operator()( const prefsDlgRec * a, const prefsDlgRec * b ) const
	{
		if( a->hInst < b->hInst ) return true;
		if( a->dlgID < b->dlgID ) return true;
		return strcmp( a->name, b->name ) < 0;
	}
};


map<prefsDlgRec *, HTREEITEM, PrefRecCompare> rec_to_item;


struct AllWeNeed
{
	prefsDlgRec	* PageData;
	HWND hwnd;
};



HWND WindowPrefs = NULL;
HWND WindowPage = NULL;
HWND WindowTree = NULL;
HWND ButtonClose = NULL;

HTREEITEM root = NULL;

HWND hCurrentPage = NULL;
// bool bKeepFocus = false;


LRESULT CALLBACK WndprocPrefs( HWND hwnd, UINT message, WPARAM wp, LPARAM lp );
LRESULT CALLBACK WndprocTree( HWND hwnd, UINT message, WPARAM wp, LPARAM lp );
WNDPROC WndprocTreeBackup = NULL;


////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool Prefs::Create()
{
	if( WindowPrefs ) return false;
	
	// Register class
	WNDCLASS wc = {
		0,                                     // UINT style
		WndprocPrefs,                          // WNDPROC lpfnWndProc
    	0,                                     // int cbClsExtra
    	0,                                     // int cbWndExtra
    	g_hInstance,                           // HINSTANCE hInstance
    	NULL,                                  // HICON hIcon
    	LoadCursor( NULL, IDC_ARROW ),         // HCURSOR hCursor
    	( HBRUSH )COLOR_WINDOW,                // HBRUSH hbrBackground
    	NULL,                                  // LPCTSTR lpszMenuName
    	CLASSNAME_PREFS                        // LPCTSTR lpszClassName
	};
		
	if( !RegisterClass( &wc ) ) return false;
	
	const int cxScreen = GetSystemMetrics( SM_CXFULLSCREEN );
	const int cyScreen = GetSystemMetrics( SM_CYFULLSCREEN );

	// Create window
	WindowPrefs = CreateWindowEx(
		WS_EX_WINDOWEDGE |                // DWORD dwExStyle
			WS_EX_TOOLWINDOW,             //
		CLASSNAME_PREFS,                  // LPCTSTR lpClassName
		TITLE_PREFS,                      // LPCTSTR lpWindowName
		WS_OVERLAPPED |                   // DWORD dwStyle
//			WS_VISIBLE |                  //
			WS_CLIPCHILDREN |             //
			WS_SYSMENU,                   //
		( cxScreen - PREFS_WIDTH ) / 2,   // int x
		( cyScreen - PREFS_HEIGHT ) / 2,  // int y
		PREFS_WIDTH,                      // int nWidth
		PREFS_HEIGHT,                     // int nHeight
		NULL,                             // HWND hWndParent
		NULL,                             // HMENU hMenu
		g_hInstance,                      // HINSTANCE hInstance
		NULL                              // LPVOID lpParam
	);
	

	if( !WindowPrefs )
	{
		UnregisterClass( CLASSNAME_PREFS, g_hInstance );
		return false;
	}


	RECT r;
	GetClientRect( WindowPrefs, &r );


////////////////////////////////////////////////////////////////////////////////
	const int iWidth = PREFS_WIDTH  * 2 - r.right;
	const int iHeight = PREFS_HEIGHT * 2 - r.bottom;
	SetWindowPos(
		WindowPrefs,
		NULL,
		( cxScreen - iWidth ) / 2,
		( cyScreen - iHeight ) / 2,
		iWidth,
		iHeight,
		SWP_NOZORDER
	);
	GetClientRect( WindowPrefs, &r );
////////////////////////////////////////////////////////////////////////////////


	LoadCommonControls();

	const int iTreeWidth = ( r.right - r.left ) - PAGE_WIDTH - GAP_LEFT - GAP_RIGHT - SPLITTER_WIDTH;

	WindowTree = CreateWindowEx(
		WS_EX_CLIENTEDGE,                     // DWORD dwExStyle
		WC_TREEVIEW,                          // LPCTSTR lpClassName
		NULL,                                 // LPCTSTR lpWindowName
		WS_VSCROLL |                          // DWORD dwStyle
			WS_VISIBLE |                      //
			WS_CHILD |                        //
			WS_TABSTOP |                      //
			TVS_HASLINES |                    //
			TVS_LINESATROOT |                 //
			TVS_HASBUTTONS |                  //
			TVS_SHOWSELALWAYS |               //
			TVS_DISABLEDRAGDROP |             //
			TVS_NONEVENHEIGHT,                //
		GAP_LEFT,                             // int x
		GAP_TOP,                              // int y
		iTreeWidth,                           // int nWidth
		PAGE_HEIGHT,                          // int nHeight
		WindowPrefs,                          // HWND hWndParent
		NULL,                                 // HMENU hMenu
		g_hInstance,                          // HINSTANCE hInstance
		NULL                                  // LPVOID lpParam
	);
	
	if( !WindowTree )
	{
		; //...
	}
	
	Font::Apply( WindowTree );
	
	// Exchange window procedure
	WndprocTreeBackup = ( WNDPROC )GetWindowLong( WindowTree, GWL_WNDPROC );
	if( WndprocTreeBackup != NULL )
	{
		SetWindowLong( WindowTree, GWL_WNDPROC, ( LONG )WndprocTree );
	}
	
	
	const int iPageLeft = GAP_LEFT + iTreeWidth + SPLITTER_WIDTH;
	
	WindowPage = CreateWindowEx(
		0,                                    // DWORD dwExStyle
		TEXT( "Static" ),                     // LPCTSTR lpClassName
		TEXT( "Nothing to see here" ),        // LPCTSTR lpWindowName
		WS_TABSTOP |                          // DWORD dwStyle
			WS_VISIBLE |                      //
			WS_CHILD |                        //
			SS_CENTER |                       //
			SS_CENTERIMAGE,                   //
		iPageLeft,                            // int x
		GAP_TOP,                              // int y
		PAGE_WIDTH,                           // int nWidth
		PAGE_HEIGHT,                          // int nHeight
		WindowPrefs,                          // HWND hWndParent
		NULL,                                 // HMENU hMenu
		g_hInstance,                          // HINSTANCE hInstance
		NULL                                  // LPVOID lpParam
	);

	if( !WindowPage )
	{
		; //...
	}

	Font::Apply( WindowPage );


	const int iButtonLeft = ( r.right - r.left ) - GAP_RIGHT - BUTTON_WIDTH - 1;
	const int iButtonTop = ( r.bottom - r.top ) - BOTTOM_SPACE + SPLITTER_HEIGHT;


	ButtonClose = CreateWindowEx(
		0,                                    // DWORD dwExStyle
		TEXT( "Button" ),                     // LPCTSTR lpClassName
		TEXT( "Close" ),                      // LPCTSTR lpWindowName
		WS_TABSTOP |                          // DWORD dwStyle
			WS_VISIBLE |                      //
			WS_CHILD |                        //
			BS_PUSHBUTTON |                   //
			BS_TEXT |                         //
			BS_VCENTER,                       //
		iButtonLeft,                          // int x
		iButtonTop,                           // int y
		BUTTON_WIDTH,                         // int nWidth
		BOTTOM_SPACE - SPLITTER_HEIGHT - GAP_BOTTOM,  // int nHeight
		WindowPrefs,                          // HWND hWndParent
		NULL,                                 // HMENU hMenu
		g_hInstance,                          // HINSTANCE hInstance
		NULL                                  // LPVOID lpParam
	);
	
	Font::Apply( ButtonClose );
	
	AllWeNeed * awn_root = new AllWeNeed;
//	memset( &awn_root->PageData, 0, sizeof( prefsDlgRec ) );
	awn_root->PageData = NULL;
	awn_root->hwnd = NULL;

	TV_INSERTSTRUCT tvi = {
		root,                                     // HTREEITEM hParent
		TVI_SORT,                                 // HTREEITEM hInsertAfter
		{
			TVIF_PARAM | TVIF_STATE | TVIF_TEXT,  // UINT mask
			NULL,                                 // HTREEITEM hItem
			TVIS_EXPANDED | TVIS_SELECTED,        // UINT state
			0,                                    // UINT stateMask
			TEXT( "General" ),                    // LPSTR pszText
			8,                                    // int cchTextMax
			0,                                    // int iImage
			0,                                    // int iSelectedImage
			0,                                    // int cChildren
			( LPARAM )awn_root                    // LPARAM lParam
		}
	};
	
	root = TreeView_InsertItem( WindowTree, &tvi );
	return true;
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool Prefs::Destroy()
{
	if( !WindowPrefs ) return false;
	
	DestroyWindow( WindowPrefs );
	UnregisterClass( CLASSNAME_PREFS, g_hInstance );
	WindowPrefs = NULL;
	
	DestroyWindow( WindowTree );	
	WindowTree = NULL;

	DestroyWindow( WindowPage );
	WindowPage = NULL;

	DestroyWindow( ButtonClose );
	ButtonClose = NULL;

	return true;
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool Prefs_Show( HTREEITEM item )
{
	if( !WindowPrefs ) return false;

	// Select and load associated page
	TreeView_SelectItem( WindowTree, item );

	if( !IsWindowVisible( WindowPrefs ) )
	{
		ShowWindow( WindowPrefs, SW_SHOW );
	}
	
	SetActiveWindow( WindowPrefs );
	SetFocus( WindowTree );
	
	return true;
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool Prefs::Show()
{
	return Prefs_Show( root );
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool Prefs::Show( prefsDlgRec * PageData )
{
	map<prefsDlgRec *, HTREEITEM, PrefRecCompare>::iterator iter = rec_to_item.find( PageData );
	if( iter != rec_to_item.end() )
	{
		return Prefs_Show( iter->second );
	}
	else
	{
		return Prefs_Show( root );
	}
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool Prefs::AddPage( prefsDlgRec * PageData )
{
	// TODO unicode!
	if( !WindowPrefs ) return false;
	
	// Backup
	char * NameBackup = new char[ strlen( PageData->name ) + 1 ];
	strcpy( NameBackup, PageData->name );
	prefsDlgRec * PageDataBackup = new prefsDlgRec;
	memcpy( PageDataBackup, PageData, sizeof( prefsDlgRec ) );
	PageDataBackup->name = NameBackup;
	
	AllWeNeed * awn = new AllWeNeed;
	awn->PageData  = PageDataBackup;
	awn->hwnd      = NULL;
	
	TV_INSERTSTRUCT tvi = {
		root,                                            // HTREEITEM hParent
		TVI_SORT,                                        // HTREEITEM hInsertAfter
		{                                                //
			TVIF_PARAM | TVIF_STATE | TVIF_TEXT,         // UINT mask
			NULL,                                        // HTREEITEM hItem
			TVIS_EXPANDED | TVIS_SELECTED,               // UINT state
			0,                                           // UINT stateMask
			PageDataBackup->name,                        // LPSTR pszText
			( int )strlen( PageDataBackup->name ) + 1,   // int cchTextMax
			0,                                           // int iImage
			0,                                           // int iSelectedImage
			0,                                           // int cChildren
			( LPARAM )awn                                // LPARAM lParam
		}
	};
	
	HTREEITEM new_item = TreeView_InsertItem( WindowTree, &tvi );
	
	TreeView_Expand( WindowTree, root, TVE_EXPAND );
	
	rec_to_item.insert( pair<prefsDlgRec *, HTREEITEM>( PageDataBackup, new_item ) );
	return true;
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndprocPrefs( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
{
	switch( message )
	{
	case WM_NOTIFY:
		{
			NMHDR * nmhdr = ( NMHDR * )lp;
			if( ( nmhdr->hwndFrom == WindowTree ) && ( nmhdr->code == TVN_SELCHANGING ) )
			{
				NMTREEVIEW * nmtv = ( NMTREEVIEW * )lp;
				TVITEM * OldPage = &nmtv->itemOld;
				TVITEM * NewPage = &nmtv->itemNew;
				
				// Destroy old window
				AllWeNeed * old_awn = ( AllWeNeed * )OldPage->lParam;
				if( old_awn && old_awn->hwnd && IsWindow( old_awn->hwnd ) )
				{
					DestroyWindow( old_awn->hwnd );
					old_awn->hwnd = NULL;
				}
				
				// Create new window
				AllWeNeed * new_awn = ( AllWeNeed * )NewPage->lParam;
				if( new_awn )
				{
					prefsDlgRec * PageData = new_awn->PageData;
					if( PageData && PageData->hInst ) // root has NULL here
					{
						if( !PageData->proc )
						{
							MessageBox( 0, TEXT( "proc NULL" ), TEXT( "" ), 0 );
							PageData->proc = ( void * )WndprocPrefs;
						}
						
/*
						RECT r;
						GetWindowRect( WindowPage, &r );
						const int iWidth = r.right - r.left;
						const int iHeight = r.bottom - r.top;
						POINT p = { r.left, r.top };
						ScreenToClient( WindowPrefs, &p );
						MoveWindow( WindowPage, p.x, p.y, iWidth - 10, iHeight - 10, FALSE );
*/
//						bKeepFocus = true;

						HWND hPage = CreateDialog(
							PageData->hInst,             // HINSTANCE hInstance,
							( LPCTSTR )PageData->dlgID,  // LPCTSTR lpTemplate,
							WindowPage,                  // HWND hWndParent,
							( DLGPROC )PageData->proc    // DLGPROC lpDialogFunc
						);
						new_awn->hwnd = hPage;
						
//						MoveWindow( WindowPage, p.x, p.y, iWidth, iHeight, FALSE );

						ShowWindow( hPage, SW_SHOW );
						UpdateWindow( hPage );
						SetFocus( WindowTree );
/*
						SetActiveWindow( hPage );
						SetActiveWindow( hwnd );
*/						
						hCurrentPage = hPage;
//						bKeepFocus = false;
					}
/*
					else
					{
						MessageBox( 0, TEXT( "hInst NULL" ), TEXT( "" ), 0 );
					}
*/
				}
				else
				{
					MessageBox( 0, TEXT( "awn NULL" ), TEXT( "" ), 0 );
				}
			}
			break;
		}

	case WM_COMMAND:
		switch( HIWORD( wp ) )
		{
		case BN_CLICKED:
			if( ( HWND )lp == ButtonClose )
			{
				PostMessage( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
			}
			break;
		}
		break;

	case WM_SYSCOMMAND:
		if( ( wp & 0xFFF0 ) == SC_CLOSE )
		{
			ShowWindow( hwnd, SW_HIDE );

			// Destroy current page so the settings are saved
			// (currently be selecting the empty page
			TreeView_SelectItem( WindowTree, root );

/*		
			if( hCurrentPage && IsWindow( hCurrentPage ) )
			{
				DestroyWindow( hCurrentPage );
				hCurrentPage = NULL;
			}
*/			
			return 0;
		}
		break;
		
	case WM_KEYDOWN:
		switch( wp )
		{
		case VK_ESCAPE:
			PostMessage( WindowPrefs, WM_SYSCOMMAND, SC_CLOSE, 0 );
			break;
	
		}
		break;
/*		
	case WM_KILLFOCUS:
		if( bKeepFocus )
		{
			return 1;
		}
		break;
*/
	}

	return DefWindowProc( hwnd, message, wp, lp );
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndprocTree( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
{
	switch( message )
	{
	case WM_KEYDOWN:
		switch( wp )
		{
		case VK_ESCAPE:
			PostMessage( WindowPrefs, WM_SYSCOMMAND, SC_CLOSE, 0 );
			break;
	
		}
		break;
		
	}
	return CallWindowProc( WndprocTreeBackup, hwnd, message, wp, lp );
}
