////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////
// Documentation
// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ

/* ---- The Seekbar ----
   Rebar.cpp handle the progress (playback position) bar. WndprocMain() is called every
   second during playback. And several times a second when the cursor is moving over the
   main window. If WM_TIMER called WndprocMain it calls Playback::UpdateSeek(); */


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


#include "Main.h"
#include "GlobalVersion.h"
#include "Playlist.h"
#include "Console.h"
#include "Status.h"
#include "Rebar.h"
#include "Playback.h"
#include "PluginManager.h"
#include "DspModule.h"
#include "VisModule.h"
#include "InputPlugin.h"
#include "OutputPlugin.h"
#include "InputPlugin.h"
#include "AddDirectory.h"
#include "AddFiles.h"
#include "Winamp.h"
#include "Winamp/wa_ipc.h"
#include "Config.h"
#include <shellapi.h>

#define CLASSNAME_MAIN      TEXT( "Winamp v1.x" )
#define MAIN_TITLE          PLAINAMP_LONG_TITLE

#define MAIN_WIDTH          731
#define MAIN_HEIGHT         562



HWND WindowMain = NULL; // extern
HMENU main_context_menu = NULL; // extern
HMENU play_context_menu = NULL;
HMENU opts_context_menu = NULL;
HMENU playback_context_menu = NULL;



LRESULT CALLBACK WndprocMain( HWND hwnd, UINT message, WPARAM wp, LPARAM lp );



WINDOWPLACEMENT WinPlaceMain;

void WinPlaceMainCallback( ConfVar * var )
{
	if( !IsWindow( WindowMain ) ) return;
	GetWindowPlacement( WindowMain, &WinPlaceMain );
}

const int cxScreen = GetSystemMetrics( SM_CXFULLSCREEN );
const int cyScreen = GetSystemMetrics( SM_CYFULLSCREEN );

RECT rMainDefault = {
	( cxScreen - MAIN_WIDTH ) / 2,
	( cyScreen - MAIN_HEIGHT ) / 2,
	( cxScreen - MAIN_WIDTH ) / 2 + MAIN_WIDTH,
	( cyScreen - MAIN_HEIGHT ) / 2 + MAIN_HEIGHT
};

ConfWinPlaceCallback cwpcWinPlaceMain(
	&WinPlaceMain,
	TEXT( "WinPlaceMain" ),
	&rMainDefault,
	WinPlaceMainCallback
);


bool bMinimizeToTray;
ConfBool cbMinimizeToTray( &bMinimizeToTray, TEXT( "MinimizeToTray" ), CONF_MODE_PUBLIC, true );



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
bool BuildMainWindow()
{

	//#ifndef NOGUI

		// =======================================================================================
		/* Disabling this window creation cause continuous "Error setting DirectSound cooperative level"
		   messages for some reason. So I leave it here for now. */

		// Register class
		WNDCLASS wc = {
			0,                                             // UINT style
			WndprocMain,                                   // WNDPROC lpfnWndProc
    		0,                                             // int cbClsExtra
    		0,                                             // int cbWndExtra
    		g_hInstance,                                   // HINSTANCE hInstance
    		LoadIcon( g_hInstance, TEXT( "IDI_ICON1" ) ),  // HICON hIcon
    		LoadCursor( NULL, IDC_ARROW ),                 // HCURSOR hCursor
    		( HBRUSH )COLOR_WINDOW,                        // HBRUSH hbrBackground
    		NULL,                                          // LPCTSTR lpszMenuName
    		CLASSNAME_MAIN                                 // LPCTSTR lpszClassName
		};
			
		if( !RegisterClass( &wc ) ) return false;


		// Create WindowMain
		WindowMain = CreateWindowEx(
			WS_EX_WINDOWEDGE,                        // DWORD dwExStyle
			CLASSNAME_MAIN,                          // LPCTSTR lpClassName
			MAIN_TITLE,                              // LPCTSTR lpWindowName
			WS_OVERLAPPED |                          // DWORD dwStyle
	//			WS_VISIBLE |                         //
				WS_CLIPCHILDREN |                    //
				WS_BORDER |                          //
				WS_SYSMENU |                         //
				WS_THICKFRAME |                      //
				WS_MINIMIZEBOX |                     //
				WS_MAXIMIZEBOX,                      //
			rMainDefault.left,                       // int x
			rMainDefault.top,                        // int y
			rMainDefault.right - rMainDefault.left,  // int nWidth
			rMainDefault.bottom - rMainDefault.top,  // int nHeight
			NULL,                                    // HWND hWndParent
			NULL,                                    // HMENU hMenu
			g_hInstance,                             // HINSTANCE hInstance
			NULL                                     // LPVOID lpParam
		);
		// =======================================================================================
	
	//#endif

	// =======================================================================================
	#ifdef NOGUI

		// If this is not called a crash occurs
		PlaylistView::Create();

		// We have now done what we wanted so we skip the rest of the file
		return true;

	#else
	// =======================================================================================


	if( !WindowMain )
	{
		UnregisterClass( CLASSNAME_MAIN, g_hInstance );
		return false;
	}
	
	

	
	// Build context menu
	HMENU main_menu      = CreateMenu();
	HMENU plainamp_menu  = CreatePopupMenu();
	HMENU playback_menu  = CreatePopupMenu();
	HMENU playlist_menu  = CreatePopupMenu();
	HMENU windows_menu   = CreatePopupMenu();


	// Plainamp
	AppendMenu( plainamp_menu, MF_STRING, WINAMP_OPTIONS_PREFS, TEXT( "Preferences   \tCtrl+P" ) );
	AppendMenu( plainamp_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );
	AppendMenu( plainamp_menu, MF_STRING, WINAMP_HELP_ABOUT, TEXT( "&About\tCtrl+F1" ) );
	AppendMenu( plainamp_menu, MF_STRING, WINAMP_FILE_QUIT, TEXT( "&Exit       \tAlt+F4" ) );

	// Playback
	AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON1, TEXT( "Pre&vious    \tZ" ) );
	AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON2, TEXT( "&Play\tX" ) );
	AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON3, TEXT( "P&ause\tC" ) );
	AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON4, TEXT( "&Stop\tV" ) );
	AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON5, TEXT( "&Next\tB" ) );

	// Playlist
	AppendMenu( playlist_menu, MF_STRING, ID_PE_OPEN, TEXT( "&Open\tCtrl+O" ) );
	AppendMenu( playlist_menu, MF_STRING, ID_PE_SAVEAS, TEXT( "&Save as\tCtrl+S" ) );
	AppendMenu( playlist_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );
	AppendMenu( playlist_menu, MF_STRING, WINAMP_FILE_PLAY, TEXT( "Add &files\tL" ) );
	AppendMenu( playlist_menu, MF_STRING, WINAMP_FILE_DIR, TEXT( "Add &directory\tShift+L" ) );
	AppendMenu( playlist_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );
	AppendMenu( playlist_menu, MF_STRING, PLAINAMP_PL_REM_SEL, TEXT( "Remove selected\tDel" ) );
	AppendMenu( playlist_menu, MF_STRING, PLAINAMP_PL_REM_CROP, TEXT( "Remove unselected     \tCtrl+Del" ) );
	AppendMenu( playlist_menu, MF_STRING, ID_PE_CLEAR, TEXT( "Remove all\tCtrl+Shift+Del" ) );
	AppendMenu( playlist_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );
	AppendMenu( playlist_menu, MF_STRING, ID_PE_SELECTALL, TEXT( "Select &all\tCtrl+A" ) );
	AppendMenu( playlist_menu, MF_STRING, ID_PE_NONE, TEXT( "Select &zero\tCtrl+Shift+A" ) );
	AppendMenu( playlist_menu, MF_STRING, ID_PE_INVERT, TEXT( "Select &invert\tCtrl+I" ) );

	// Windows
	AppendMenu( windows_menu, MF_STRING, MENU_MAIN_WINDOWS_CONSOLE, TEXT( "&Console" ) );
	AppendMenu( windows_menu, MF_STRING, MENU_MAIN_WINDOWS_MANAGER, TEXT( "Plugin &Manager" ) );

	// Main
	AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )plainamp_menu, TEXT( "&Plainamp" ) );	
	AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )playback_menu, TEXT( "Play&back" ) );	
	AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )playlist_menu, TEXT( "Play&list" ) );	
	AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )windows_menu, TEXT( "&Windows" ) );	

	SetMenu( WindowMain, main_menu );
	
	////////////////////////////////////////////////////////////////////////////////

	main_context_menu = CreatePopupMenu();
	AppendMenu( main_context_menu, MF_STRING, WINAMP_HELP_ABOUT, TEXT( "Plainamp" ) );

	AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );

		play_context_menu = CreatePopupMenu();
		AppendMenu( play_context_menu, MF_STRING, WINAMP_FILE_PLAY, TEXT( "Files   \tL" ) );
		AppendMenu( play_context_menu, MF_STRING, WINAMP_FILE_DIR, TEXT( "Folder   \tShift+L" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_POPUP, ( UINT_PTR )play_context_menu, TEXT( "Play" ) );
	
	AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );
	
	AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED | MF_CHECKED, WINAMP_MAIN_WINDOW, TEXT( "Main Window\tAlt+W" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED | MF_CHECKED, WINAMP_OPTIONS_PLEDIT, TEXT( "Playlist Editor\tAlt+E" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, WINAMP_OPTIONS_EQ, TEXT( "Equalizer\tAlt+G" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, WINAMP_OPTIONS_VIDEO, TEXT( "Video\tAlt+V" ) );
	AppendMenu( main_context_menu, MF_STRING, PLAINAMP_TOGGLE_CONSOLE, TEXT( "Console" ) );
	AppendMenu( main_context_menu, MF_STRING, PLAINAMP_TOGGLE_MANAGER, TEXT( "Plugin Manager" ) );
	
	
	/*	
	AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, MENU_MAIN_CONTEXT_MANAGER, TEXT( "Plugin Manager" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, MENU_MAIN_CONTEXT_CONSOLE, TEXT( "Console" ) );
	*/

	AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );
	
		opts_context_menu = CreatePopupMenu();
		AppendMenu( opts_context_menu, MF_STRING, WINAMP_OPTIONS_PREFS, TEXT( "Preferences   \tCtrl+P" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_POPUP, ( UINT_PTR )opts_context_menu, TEXT( "Options" ) );

		playback_context_menu = CreatePopupMenu();
		AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON1, TEXT( "Previous    \tZ" ) );
		AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON2, TEXT( "Play\tX" ) );
		AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON3, TEXT( "Pause\tC" ) );
		AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON4, TEXT( "Stop\tV" ) );
		AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON5, TEXT( "Next\tB" ) );
	AppendMenu( main_context_menu, MF_STRING | MF_POPUP, ( UINT_PTR )playback_context_menu, TEXT( "Playback" ) );
	
	AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL );

	AppendMenu( main_context_menu, MF_STRING, WINAMP_FILE_QUIT, TEXT( "Exit" ) );


	Toolbar::Create(); // This removes all buttons and status bars
	//BuildMainStatus();


	// =======================================================================================
	// If this is not created a crash occurs
	PlaylistView::Create();
	
	Playlist::Create();


	SetWindowPlacement( WindowMain, &WinPlaceMain );

	
	return true;
#endif
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
void About( HWND hParent )
{
	// For info goto
	// http://predef.sourceforge.net/precomp.html

	TCHAR szBuildDetails[ 1000 ] = "";

#ifdef __GNUC__
	_stprintf( szBuildDetails,
		TEXT( "\n\n\nGNU GCC " __VERSION__ "\n" __DATE__ )
	);
#else
# ifdef _MSC_VER
	_stprintf(
		szBuildDetails,
		TEXT( "\n\n\nMicrosoft Visual C++ %i.%i\n" __DATE__ ),
		_MSC_VER / 100 - 6,
		( _MSC_VER % 100 ) / 10
	);
# endif
#endif

	TCHAR szBuffer[ 1000 ];
	_stprintf(
		szBuffer,
		PLAINAMP_LONG_TITLE TEXT( "\n"
			"\n"
			"Copyright İ 2005 Sebastian Pipping  \n"
			"<webmaster@hartwork.org>\n"
			"\n"
			"-->  http://www.hartwork.org"
			"%s"
		),
		szBuildDetails
	);

	MessageBox(
		hParent,
		szBuffer,
		TEXT( "About" ),
		MB_ICONINFORMATION
	);
}

#define TRAY_MAIN_ID  13
#define TRAY_MSG      ( WM_USER + 1 )



NOTIFYICONDATA nid;

bool AddTrayIcon( HWND hwnd )
{
	ZeroMemory( &nid, sizeof( NOTIFYICONDATA ) );
	nid.cbSize            = sizeof( NOTIFYICONDATA );
	nid.hWnd              = hwnd;
	nid.uID               = TRAY_MAIN_ID;
	nid.uFlags            = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	nid.uCallbackMessage  = TRAY_MSG;
	nid.hIcon             = LoadIcon( g_hInstance, TEXT( "IDI_ICON1" ) );
	_tcscpy( nid.szTip, TEXT( "Plainamp" ) );

	return ( Shell_NotifyIcon( NIM_ADD, &nid ) != 0 );
}

void RemoveTrayIcon()
{
	Shell_NotifyIcon( NIM_DELETE, &nid );
}



////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndprocMain( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
{
	// Tool windows are hidden on minimize/re-shown on restore
	static bool bConsoleTodo = false;
	static bool bManagerTodo = false;
	static bool bRemoveIcon = false;

	#ifdef NOGUI
		//NOTICE_LOG(AUDIO,"DLL > Main.cpp:WndprocMain() was called. But nothing will be done. \n");
	#else
		Console::Append( TEXT( "Main.cpp:WndprocMain was called" ) );
	#endif	


	switch( message )
	{

	#ifdef NOGUI
		//printf(" > WndprocMain message: %i\n", message);
	#else

	case WM_SETFOCUS:
		// To re-"blue"
		SetFocus( WindowPlaylist );
		break;

	case WM_CREATE:
		// Note: [WindowMain] is not valid yet but [hwnd] is!
		Console::Create(); // make the console window
		PluginManager::Build(); // make the plugin window
		break;

	case WM_NOTIFY:
		{
			NMHDR * hdr = ( NMHDR * )lp;
			
			switch( hdr->code )
			{
			case LVN_GETDISPINFO:
				{
					LV_DISPINFO * lpdi = ( LV_DISPINFO * )lp;
					playlist->Fill( lpdi->item );
				}
				return 0;
/*				
			case LVN_ODCACHEHINT:
				{
					LPNMLVCACHEHINT   lpCacheHint = (LPNMLVCACHEHINT)lParam;
					/
					This sample doesn't use this notification, but this is sent when the 
					ListView is about to ask for a range of items. On this notification, 
					you should load the specified items into your local cache. It is still 
					possible to get an LVN_GETDISPINFO for an item that has not been cached, 
					therefore, your application must take into account the chance of this 
					occurring.
					/
				}
				return 0;

			case LVN_ODFINDITEM:
				{
					LPNMLVFINDITEM lpFindItem = (LPNMLVFINDITEM)lParam;
					/
					This sample doesn't use this notification, but this is sent when the 
					ListView needs a particular item. Return -1 if the item is not found.
					/
				}
				return 0;
*/				
			case NM_CUSTOMDRAW:
				{
					NMLVCUSTOMDRAW * custom = ( NMLVCUSTOMDRAW * )lp;

					switch( custom->nmcd.dwDrawStage )
					{
					case CDDS_PREPAINT:
						return CDRF_NOTIFYITEMDRAW;
						
					case CDDS_ITEMPREPAINT:
						return CDRF_NOTIFYSUBITEMDRAW;
						
					case ( CDDS_SUBITEM | CDDS_ITEMPREPAINT ):
						{
							// This is the prepaint stage for an item. Here's where we set the
							// item's text color. Our return value will tell Windows to draw the
							// item itself, but it will use the new color we set here.
							// We'll cycle the colors through red, green, and light blue.

							if( custom->nmcd.dwItemSpec == playlist->GetCurIndex() )
							{
								custom->clrTextBk = RGB( 225, 225, 225 );
							}
							else
							{
								if( custom->nmcd.dwItemSpec & 1 )
									custom->clrTextBk = RGB( 245, 248, 250 );
								else
									custom->clrTextBk = RGB( 255, 255, 255 );
							}

							if( custom->iSubItem == 0 )
								custom->clrText = RGB( 255, 0, 0 );
							else
								custom->clrText = RGB( 0, 0, 0 );
							
							
/*
							if ( (custom->nmcd.dwItemSpec % 3) == 0 )
								crText = RGB(255,0,0);
							else if ( (custom->nmcd.dwItemSpec % 3) == 1 )
								crText = RGB(0,255,0);
							else
								crText = RGB(128,128,255);

							// Store the color back in the NMLVCUSTOMDRAW struct.
							custom->clrText = crText;
*/
							// Tell Windows to paint the control itself.
						}
					/*
						custom->clrText = RGB( 190, 190, 190 );
						custom->clrTextBk = RGB( 255, 0, 0 );*/
						return CDRF_DODEFAULT;
						
						
					}
					break;

				}
				
				/*
			case RBN_CHILDSIZE:
				{
				NMREBARCHILDSIZE * chs = ( NMREBARCHILDSIZE * )lp;
				const int width_client = chs->rcChild.right - chs->rcChild.left;
				int diff = width_client - 120;
				if( diff > 0 )
				{
					const int width_band = chs->rcBand.right - chs->rcBand.left;
					// chs->rcChild.right = chs->rcChild.left + 120;
	
					DEBUGF( 1000, "CHILDSIZE [%i] [%i]", chs->uBand, width_band );
	
					const int client_band_diff = width_band - width_client;
					chs->rcBand.right = chs->rcBand.left + 120 + client_band_diff;
					// chs->uBand
	
					
					REBARBANDINFO rbbi;
					rbbi.cbSize = sizeof( REBARBANDINFO );
					rbbi.fMask = RBBIM_SIZE;
					rbbi.cx = 154; //width_band + diff;
					LRESULT lResult = SendMessage(
						rebar,
						RB_SETBANDINFO, 
						chs->uBand,
						( LPARAM )( REBARBANDINFO * )&rbbi
					);
					
				}
				break;
			}
			*/
			case RBN_HEIGHTCHANGE:
				{
					const int iRebarHeightBefore = iRebarHeight;
					RECT r;
					GetWindowRect( WindowRebar, &r );
					iRebarHeight = r.bottom - r.top;

					InvalidateRect( WindowRebar, NULL, TRUE );
					InvalidateRect( WindowPlaylist, NULL, TRUE );

					RECT client;
					GetClientRect( WindowMain, &client );
					PostMessage(
						hwnd,
						WM_SIZE,
						SIZE_RESTORED,
						( client.right - client.left ) << 16 |
							( client.bottom - client.top )
					);
					
					break;
				}
			}
			break;
		}
	
	case WM_SYSKEYDOWN:
		switch( wp ) // [Alt]+[...]
		{
		case VK_UP:
		case VK_DOWN:
			SetFocus( WindowPlaylist );
			SendMessage( WindowPlaylist, message, wp, lp );
			break;
		}
		break;

	case WM_KEYDOWN:
	case WM_KEYUP:
		SetFocus( WindowPlaylist );
		SendMessage( WindowPlaylist, message, wp, lp );
		break;

	case WM_COMMAND:
		{
			const int code = HIWORD( wp );
			switch( code )
			{
			case 1: // also == CBN_SELCHANGE
				{
					if( ( HWND )lp == WindowOrder )
					{
						LRESULT res = SendMessage( WindowOrder, CB_GETCURSEL, 0, 0 );
						if( res == CB_ERR ) break;
						Playback::Order::SetMode( ( int )res );
					}
					else if( ( HWND )lp == WindowEq )
					{
						LRESULT res = SendMessage( WindowEq, CB_GETCURSEL, 0, 0 );
						if( res == CB_ERR ) break;
						Playback::Eq::SetIndex( ( int )( res - 1 ) );
					}
					
					return WndprocWinamp( hwnd, message, wp, lp );
				}
				
			case 0:
				{
					// Menu
					const int id = LOWORD( wp );
					switch( id )
					{
					case MENU_MAIN_WINDOWS_CONSOLE:
						Console::Popup();
						break;
	
					case MENU_MAIN_WINDOWS_MANAGER:
						PluginManager::Popup();
						break;

					}
/*
					TCHAR szBuffer[ 5000 ];
					_stprintf( szBuffer, TEXT( "default 1 id = <%i>" ), id );
					MessageBox( 0, szBuffer, "", 0 );
*/						
					return WndprocWinamp( hwnd, message, wp, lp );
				}

			default:
				return WndprocWinamp( hwnd, message, wp, lp );
			}
			break;
		}

	case WM_GETMINMAXINFO:
		{
			MINMAXINFO * mmi = ( MINMAXINFO * )lp;
			mmi->ptMinTrackSize.x = 400;
			mmi->ptMinTrackSize.y = 300;
			return 0;
		}
		
	case WM_SIZE:
		{
			// Resize children
			RECT client;
			GetClientRect( WindowMain, &client );
		
			const int iClientWidth = client.right - client.left;
			const int iClientHeight = client.bottom - client.top;
			const int iPlaylistHeight = iClientHeight - iRebarHeight - iStatusHeight;
	
			if( WindowRebar )
				MoveWindow( WindowRebar, 0, 0, iClientWidth, iRebarHeight, TRUE );
			if( WindowPlaylist )
			{
				MoveWindow( WindowPlaylist, 0, iRebarHeight, iClientWidth, iPlaylistHeight, TRUE );
				playlist->Resize( WindowMain );
			}
			if( WindowStatus )
				MoveWindow( WindowStatus, 0, iRebarHeight + iPlaylistHeight, iClientWidth, iStatusHeight, TRUE );
			break;
		}

	case WM_TIMER:
		Playback::UpdateSeek();
		break;
		
	case WM_CONTEXTMENU:
		PostMessage( hwnd, WM_COMMAND, WINAMP_MAINMENU, 0 );
		break;
		
	case WM_CLOSE:
		{
			// Clean shutdown
			
			// Stop
			Playback::Stop();
	
			// Dsp
			DspLock.Enter();
				for( int d = active_dsp_count - 1; d >= 0; d-- )
				{
					DspLock.Leave();
						active_dsp_mods[ d ]->Stop();
					DspLock.Enter();
				}
			DspLock.Leave();
	
			
			// Vis
			VisLock.Enter();
				for( int v = active_vis_count - 1; v >= 0; v-- )
				{
					VisLock.Leave();
						active_vis_mods[ v ]->Stop();
					VisLock.Enter();
				}
			VisLock.Leave();
		}
		break;

	case WM_DESTROY:
		{
			// =======================================================================================
			// Save playlist
			/*
			TCHAR * szPlaylistMind = new TCHAR[ iHomeDirLen + 12 + 1 ];
			memcpy( szPlaylistMind, szHomeDir, iHomeDirLen * sizeof( TCHAR ) );
			memcpy( szPlaylistMind + iHomeDirLen, TEXT( "Plainamp.m3u" ), 12 * sizeof( TCHAR ) );
			szPlaylistMind[ iHomeDirLen + 12 ] = TEXT( '\0' );

			Playlist::ExportPlaylistFile( szPlaylistMind );

			delete [] szPlaylistMind;
			*/
			// =======================================================================================

			cwpcWinPlaceMain.TriggerCallback();
			cwpcWinPlaceMain.RemoveCallback();
			
			Console::Destroy();
			PluginManager::Destroy();
			
			if( bRemoveIcon )
			{
				RemoveTrayIcon();
				bRemoveIcon = false;
			}
			
			PostQuitMessage( 0 );
			return 0;
		}
		
	case WM_ACTIVATEAPP:
		{
			if( wp != TRUE ) break;

			// Also bring console/manager to front
			const bool bConsoleVisible  = ( IsWindowVisible( WindowConsole ) == TRUE );
			const bool bManagerVisible  = ( IsWindowVisible( WindowManager ) == TRUE );
			const bool bMainTodo        = ( bConsoleVisible || bManagerVisible );

			if( bConsoleVisible ) BringWindowToTop( WindowConsole );
			if( bManagerVisible ) BringWindowToTop( WindowManager );
			if( bMainTodo       ) BringWindowToTop( WindowMain    );
			
			break;
		}

	case WM_SYSCOMMAND:
		switch( ( wp & 0xFFF0 ) )
		{
		case SC_CLOSE:
			if( !SendMessage( WindowMain, WM_WA_IPC, 0, IPC_HOOK_OKTOQUIT ) )
			{
				return 0;
			}
			break;

		case SC_MINIMIZE:
			{
				// Hide console/manager on minimize
				bConsoleTodo = ( IsWindowVisible( WindowConsole ) == TRUE );
				if( bConsoleTodo ) ShowWindow( WindowConsole, SW_HIDE );

				bManagerTodo = ( IsWindowVisible( WindowManager ) == TRUE );
				if( bManagerTodo ) ShowWindow( WindowManager, SW_HIDE );
				
				if( bMinimizeToTray )
				{
					if( !bRemoveIcon )
					{
						bRemoveIcon = AddTrayIcon( hwnd );
					}

					ShowWindow( hwnd, FALSE );
					return 0;
				}

				break;
			}

		case SC_RESTORE:
			{
				const LRESULT res = DefWindowProc( hwnd, message, wp, lp );

				// Unhide console/manager
				const bool bMainTodo = ( bConsoleTodo || bManagerTodo );
				if( bConsoleTodo ) ShowWindow( WindowConsole, SW_SHOW );
				if( bManagerTodo ) ShowWindow( WindowManager, SW_SHOW );
				if( bMainTodo    ) BringWindowToTop( WindowMain );
				
				return res;
			}
		}
		break;
	
	case TRAY_MSG:
		switch( lp )
		{
		case WM_RBUTTONDOWN: // TODO: context menu instead
		case WM_LBUTTONDOWN:
			if( IsWindowVisible( hwnd ) == FALSE )
			{
				ShowWindow( hwnd, TRUE );
			}
			break;
			
		case WM_RBUTTONUP: // TODO: context menu instead
		case WM_LBUTTONUP:
			if( bRemoveIcon )
			{
				RemoveTrayIcon();
				bRemoveIcon = false;
			}
			break;

		}
		return 0;
	#endif
	default:
		return WndprocWinamp( hwnd, message, wp, lp );
	}
	return DefWindowProc( hwnd, message, wp, lp );

}
