#include"../../Maid/FrameWork/Windows/CApplicationWindows.h"
#include"../../Maid/Auxiliary/Thread/CProcessMutex.h"
#include"../../Maid/Auxiliary/Windows/CShell.h"
#include"../../Maid/Auxiliary/CString.h"

#include"resource.h"
#include"CSetupConfig.h"
#include"Install.h"

#include <shlobj.h>
#include <shellapi.h>

using namespace Maid;

/*!
	\file
	\brief	いうまでも無い Windows おなじみの WinMain
 */
static BOOL CALLBACK InstallConfigDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
static BOOL CALLBACK InstallStateProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );


static mstring InstallConfigPath;	//	Installer_config.txt があるパス
									//	このＥＸＥはＨＤＤにコピー後起動されるので、絶対パスで指定する必要がある

static HINSTANCE	s_hInstance;
static CSetupConfig	s_SetupConfig;

static Install				s_InstallThread;
static Install::INSTALLDATA	s_InstallData;


//	チェックボックスのあたり判定
//	この中に入ると HELP_TEXT のテキストが IDC_STATIC_HELP に表示される
static const DWORD HELP_CONTROLID[] =
{
	COMBBOX_INSTALLTYPE,
	BUTTON_BEGININSTALL,
	EDIT_INSTALLDIR,
	BUTTON_SELECTINSTALLDIR,
	CHECK_MD5CHECK,
	CHECK_CREATEDESKTOPSHOTCUT,
};

static const char* HELP_TEXT[] =
{
	"",	//	<- 設定ファイルで更新するので変化しない
	"インストールを開始します",
	"インストールするフォルダです\nフォルダが存在しない場合、自動的に作成されます",
	"インストールするフォルダを変更します",
	"インストール中にファイルが破損していないかチェックします",
	"スタートメニューの他にデスクトップにショートカットを作成します",
};

typedef MySTL::map<HWND,const char*> HELPLIST;
static HELPLIST	s_Control;		//	HELP_CONTROLID,HELP_TEXT の関係を持っているやつ
static HWND		s_NowControl;	//	現在ヘルプを表示しているコントロール



static HWND s_ProgressHWND;		//	インストール進行具合ダイアログのHWND
static const int PROGRESSMAX = 1000;


void SetProgressText( const mstring& text )
{
	const MySTL::string t = CString::ConvertMAIDtoSJIS(text);

	char buf[1024];
	GetDlgItemText( s_ProgressHWND, STATIC_NOWSTATE, buf, NUMELEMENTS(buf) );
	if( t!=buf ) { ::SetDlgItemText( s_ProgressHWND, STATIC_NOWSTATE, t.c_str() ); }
}

void SetNowProgress( float per )
{
	const int v = int(per*float(PROGRESSMAX)+0.5f);

	::SendMessage( GetDlgItem( s_ProgressHWND, PROGRESS_NOWSTATE  ), PBM_SETPOS,   v,   0 );
}

void SetTotalProgress( float per )
{
	const int v = int(per*float(PROGRESSMAX)+0.5f);

	::SendMessage( GetDlgItem( s_ProgressHWND, PROGRESS_TOTALSTATE  ), PBM_SETPOS,   v,   0 );
}









class CApp : public IApplicationWindows
{
protected:
	virtual void OnSetup(){}
	virtual void OnCleanup(){}

	virtual void OnLoop()
	{
		s_hInstance = GetHINSTANCE();

		InstallConfigPath = GetCmdLine(1);

		if( InstallConfigPath.empty() )
		{
			::MessageBox( NULL, "設定ファイルが見つかりません", "起動エラー", MB_OK );
			ApplicationExit();
			return;
		}

		s_SetupConfig.Initialize( InstallConfigPath );

		// 二重起動防止用
		CProcessMutex	hMutex;

		try
		{
			hMutex.Open( s_SetupConfig.GetMutexName() );
		}catch(...)
		{
			ApplicationExit();
			return ;
		}

		::DialogBox( s_hInstance, MAKEINTRESOURCE(DIALOG_INSTALLCONFIG), NULL, InstallConfigDlgProc );

		ApplicationExit();
	}
};











int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
	CApp capp;
	return capp.Run( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! パックファイル管理クラス
/*!	
 *	\param	hWnd	[i ]	呼び出し元のウィンドウハンドル
 *	\param	msg		[i ]	送られてきたメッセージ
 *	\param	wParam	[i ]	メッセージの詳細
 *	\param	lParam	
 */
static BOOL CALLBACK InstallConfigDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
	case WM_INITDIALOG:
		{
			{	//	ダイアログボックスを画面中央に持ってくる
				RECT rc;
				::GetWindowRect( hWnd, &rc );

				const int w = rc.right  - rc.left;
				const int h = rc.bottom - rc.top;
				const int x = (::GetSystemMetrics(SM_CXSCREEN) - w) /2;
				const int y = (::GetSystemMetrics(SM_CYSCREEN) - h) /2;

				::MoveWindow( hWnd, x, y, w, h, TRUE );
			}


			{	//	リストボックスにインストール項目を追加する
				const HWND hCombBox = ::GetDlgItem(hWnd, COMBBOX_INSTALLTYPE);

				for( int i=0; i<s_SetupConfig.GetInstallTypeCount(); ++i )
				{
					const CSetupConfig::INSTALLTYPE type = s_SetupConfig.GetInstallType(i);
					const MySTL::string str = CString::ConvertMAIDtoSJIS(type.InstallTypeName);
					::SendMessage( hCombBox, CB_INSERTSTRING, (WPARAM)i, (LPARAM)str.c_str() );
				}

				::SendMessage( hCombBox, CB_SETCURSEL, 0, 0L);
			}

			{	//	デフォルトのインストール先の設定
				const MySTL::string str = CString::ConvertMAIDtoSJIS(s_SetupConfig.GetDefaultTarget());

				::SetDlgItemText( hWnd, EDIT_INSTALLDIR, str.c_str() );
			}

			{	//	コントロールハンドルからヘルプテキストを参照できるようにしておく
				//	ただしそのコントロールが使えるものだけに限定する
				for( int i=0; i<NUMELEMENTS(HELP_CONTROLID); ++i )
				{
					const HWND hwnd  = GetDlgItem(hWnd,HELP_CONTROLID[i]);
					const LONG Style = ::GetWindowLong(hwnd,GWL_STYLE);
					if( !(Style&WS_DISABLED) )
					{
						s_Control[hwnd] = HELP_TEXT[i];
					}
				}
			}

			//	ＭＤ５チェックはデフォルトでＯＮ
			::CheckDlgButton( hWnd, CHECK_MD5CHECK, BST_CHECKED );
		}
		break;

	case WM_COMMAND:
		{

			switch( LOWORD(wParam) )
			{
			case BUTTON_BEGININSTALL:
				{
					mstring InstallFolder;

					{
						char buf[MAX_PATH];
						::GetDlgItemText( hWnd, EDIT_INSTALLDIR, buf, MAX_PATH );
						InstallFolder = CString::ConvertSJIStoMAID(buf);
						if( InstallFolder.empty() )
						{
							::MessageBox( hWnd, "インストールするディレクトリ名が不明です", NULL, MB_OK );
							return 0;
						}
					}

					const int32 ComboNo = (int)(DWORD)SendMessage(GetDlgItem(hWnd, COMBBOX_INSTALLTYPE), CB_GETCURSEL, 0L, 0L);

					s_SetupConfig.SetInstallFolder(InstallFolder);

					s_InstallData.pConfig = &s_SetupConfig;
					s_InstallData.InstallTypeNo = ComboNo;
					s_InstallData.IsMD5Check = ::IsDlgButtonChecked(hWnd,CHECK_MD5CHECK)==BST_CHECKED;

					::DialogBox( s_hInstance, MAKEINTRESOURCE(DIALOG_INSTALLSTATE), hWnd, InstallStateProc );

					::EndDialog( hWnd, 0 );

				}break;
			case BUTTON_SELECTINSTALLDIR:
				{
					char buf[MAX_PATH];
					::GetDlgItemText( hWnd, EDIT_INSTALLDIR, buf, MAX_PATH );

					const mstring Folder = CShell::BrowseForFolder( hWnd, MAIDTEXT("インストールフォルダの選択"), CString::ConvertSJIStoMAID(buf) );

					if( Folder.empty() ) { break; }

					{
						const MySTL::string FolderName = CString::ConvertMAIDtoSJIS(Folder);
						::SetDlgItemText( hWnd, EDIT_INSTALLDIR, FolderName.c_str() );
					}
				}break;
			}
		}
		break;	

	case WM_CLOSE:
		{
			::EndDialog( hWnd, 0 );
		}
		break;

	case WM_SETCURSOR:
		{
			const HWND hwnd		= (HWND)wParam;
			const UINT MouseMSG = HIWORD(lParam);
			const int  HitCode  = LOWORD(lParam);

			switch( MouseMSG )
			{
			case WM_MOUSEMOVE:
				{
					//	各コントロールの上に乗っていたら
					//	ヘルプを表示する
					HELPLIST::iterator ite = s_Control.find(hwnd);

					if( ite!=s_Control.end()  )
					{
						if( s_NowControl!=hwnd )
						{
							if( hwnd == GetDlgItem(hWnd,COMBBOX_INSTALLTYPE) )
							{	//	リストボックスの時は設定ファイルのテキストを拾ってくる
								const int32 ComboNo = (int)(DWORD)SendMessage(GetDlgItem(hWnd, COMBBOX_INSTALLTYPE), CB_GETCURSEL, 0L, 0L);

								const CSetupConfig::INSTALLTYPE type = s_SetupConfig.GetInstallType(ComboNo);
								const MySTL::string str = CString::ConvertMAIDtoSJIS(type.InstallTypeComment);


								SetWindowText( GetDlgItem(hWnd,STATIC_HELPTEXT), str.c_str() );
							}else
							{
								SetWindowText( GetDlgItem(hWnd,STATIC_HELPTEXT), ite->second );
							}
						}
						s_NowControl = hwnd;
					}
					else
					{
						SetWindowText( GetDlgItem(hWnd,STATIC_HELPTEXT), NULL );
						s_NowControl = NULL;
					}
				}break;
			}

		}break;
	}

	return 0;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! パック状況を知らせるダイアログボックス
/*!	
 *	\param	hWnd	[i ]	呼び出し元のウィンドウハンドル
 *	\param	msg		[i ]	送られてきたメッセージ
 *	\param	wParam	[i ]	メッセージの詳細
 *	\param	lParam	
 */
static BOOL CALLBACK InstallStateProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	const int TIMERID = 100;

	switch(msg)
	{
	case WM_TIMER:
		{
			switch( wParam )
			{
			case TIMERID:
				{
					Install::STATUS s = s_InstallThread.GetStatus();

					SetProgressText( s.ProgressText );
					SetNowProgress( s.NowProgress );
					SetTotalProgress( s.TotalProgress );

					switch( s.Status )
					{
					case Install::STATUS::THREADSTATUS_EXECUTING:
						{
						}break;
					case Install::STATUS::THREADSTATUS_ERROR:
						{ 
							::KillTimer( hWnd, TIMERID );

							::MessageBox( hWnd, "エラーが発生しました", "エラー", MB_ICONSTOP|MB_OK );
							::EndDialog( hWnd, 0 );
						}break;

					case Install::STATUS::THREADSTATUS_SUCCESS:
						{ 
							::KillTimer( hWnd, TIMERID );

							::MessageBox( hWnd, "インストールが終了しました", "インストールの成功", MB_OK );

							const CSetupConfig::EXECUTEINFO info = s_SetupConfig.GetExecuteInfo();

							if( !info.ExecutePath.empty() )
							{
								const MySTL::string mess  = CString::ConvertMAIDtoSJIS(info.CheckComment);
								const MySTL::string exec  = CString::ConvertMAIDtoSJIS(info.ExecutePath);
								const MySTL::string param = CString::ConvertMAIDtoSJIS(info.Parameters);
								const MySTL::string dir   = CString::ConvertMAIDtoSJIS(CString::GetDirectory(info.ExecutePath));

								if( ::MessageBox( hWnd, mess.c_str(), "確認", MB_YESNO )==IDYES )
								{
									ShellExecute( hWnd, "open", exec.c_str(), param.c_str(), dir.c_str(), SW_SHOWNORMAL );
								}
							}

							::EndDialog( hWnd, 0 );
						}break;

					case Install::STATUS::THREADSTATUS_CANCEL: 
						{ 
							::KillTimer( hWnd, TIMERID );

							::MessageBox( hWnd, "キャンセルしました", "キャンセル", MB_ICONINFORMATION|MB_OK );
							::EndDialog( hWnd, 0 );
						}break;
					}
				}
				break;
			}

		}
		break;

	case WM_COMMAND:
		{
			switch( LOWORD(wParam) )
			{
			case BUTTON_CANCEL:
				{
					const int ret = ::MessageBox( hWnd, "インストールを中断しますか？", "確認", MB_YESNO );
					if( ret==IDYES ) { s_InstallThread.OnCancel(); }
				}
				break;
		   }
		}
		break;

	case WM_INITDIALOG:
		{
			s_ProgressHWND = hWnd;
			s_InstallData.hWnd = hWnd;


			SendMessage( GetDlgItem( hWnd, PROGRESS_NOWSTATE), PBM_SETRANGE, 0, MAKELPARAM( 0, PROGRESSMAX ) );
			SendMessage( GetDlgItem( hWnd, PROGRESS_NOWSTATE), PBM_SETSTEP,  1,   0 );
			SendMessage( GetDlgItem( hWnd, PROGRESS_NOWSTATE), PBM_SETPOS,   0,   0 );

			SendMessage( GetDlgItem( hWnd, PROGRESS_TOTALSTATE), PBM_SETRANGE, 0, MAKELPARAM( 0, PROGRESSMAX ) );
			SendMessage( GetDlgItem( hWnd, PROGRESS_TOTALSTATE), PBM_SETSTEP,  1,   0 );
			SendMessage( GetDlgItem( hWnd, PROGRESS_TOTALSTATE), PBM_SETPOS,   0,   0 );

			try
			{
				s_InstallThread.BeginInstall( s_InstallData );
			}catch(...)
			{
				::MessageBox( hWnd, "インストールに失敗しました", NULL, MB_OK );
				::EndDialog( hWnd, 0 );
				return 0;
			}

			SetTimer( hWnd, TIMERID, 100, NULL );
		}
		break;

	case WM_CLOSE:
		{
			::SendMessage( hWnd, WM_COMMAND, IDCANCEL, 0 );
		}
		break;
    }

	return 0;
}
