/** 
 * @file
 * @brief System インターフェースの実装クラス
 *
 * @author S.F.
 * @version $Id:
 * 外部？からはSystemインターフェースを通してアクセスさせることで、OS臭い
 * 処理をすべて実装クラスの中に封じ込めます。
 * 2001/3/13 ぷよーんさんのライブラリを参考にWindowsメッセージ処理方法を変更
 * (参照・利用の許可はぷよーんさん(http://puyon.pns.to/)よりいただいてます。)
 * Copyright (C) 2000-2002 Satoshi Fujiwara. All Rights Reserved. */

#pragma warning( disable : 4786 )	//STLの警告外し
#include "sfdebug.h"


// SYSTEM INCLUDES
//
//#define INITGUID

#define WIN32_LEAN_AND_MEAN

#include "math.h"
#include <list>
#include <queue>
#include <memory>
#include <map>
#include <iostream>
#include <strstream>

#include "windows.h"
#include "windowsx.h"
#include "d3d9.h"
#include "d3dx9.h"
//#include "dmusici.h"
#include "mmsystem.h"
#include "dinput.h"
#include "dxerr8sf.h"

// PROJECT INCLUDES
//

#include "exception.h"
#include "resource.h"
#include "System.h"
#include "Console.h"
#include "Obj.h"
#include "singleton.h"
#include "AbstractSprite.h"
#include "ConsoleImpl.h"	
#include "SoundImpl.h"
#include "InputImpl.h"
#include "Main.h"
#include "SystemWin32.h"
#include "SystemImpl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// メモリーリーク検出用

// LOCAL INCLUDES
//

// FORWARD REFERENCES
//

// namespace
namespace sf {
	namespace system {
// constant

System * const instance(void)
{ 
	return SystemImpl::instance();
}// getInstance()

const wchar_t SystemImpl::smAppName[] = L"Shooting Game!";	// constant

// シングルトンパターンを端折るためのマクロ
IMPLEMENT_SINGLETON(SystemImpl,SystemImpl);

// コンストラクタ -------------------------------------------------------------
SystemImpl::SystemImpl()
{
	// もろもろの初期化処理
	mIsWindowActive = false;
	mpConsole = NULL;
	mpSound = NULL;
	mpInput = NULL;
	mpMain = NULL;
	mTimerInterval = TIMER_INTERVAL;
	mbWindowed = false;
	mTimerElapsed = 0;
	mTimerValue = timeGetTime();
	mTimerBefore = mTimerValue;

	mFrameStep = (float)(TIMER_INTERVAL) / 32.0f;
	mFramePerSec = 1000.0f / (float)TIMER_INTERVAL;

	// COM の初期化
	CoInitialize(NULL);

}// SystemImpl()


// デストラクタ ---------------------------------------------------------------
SystemImpl::~SystemImpl()
{
	OutputDebugString("~SystemImpl()");
	uninitialize();
	CoUninitialize();
}// ~SystemImpl()

/*! 終了処理を行う
　開放しなければならないポインタ、COMオブジェクトなどの処理はすべてここで行います */
void SystemImpl::uninitialize(void)
{
	// Consoleインターフェースの開放
	if(mpConsole){
		delete mpConsole;
		mpConsole = NULL;
	}

	// Mainインターフェースの開放
	// auto_ptrなのでdeleteはなし
	if(mpMain){
	
		mpMain = NULL;
	}

	// Soundインターフェースの開放
	if(mpSound)
	{
		delete mpSound;
		mpSound = NULL;
	}

	// Inputインターフェースの開放
	if(mpInput)
	{
		delete mpInput;
		mpInput = NULL;
	}
	
	// WindowMessageMapの開放
	WindowMessageMap::iterator it;
	for(it = mWindowMessageMap.begin();it != mWindowMessageMap.end();++it)
	{
		if((*it).second){
			delete ((*it).second);
		}
	}
	mWindowMessageMap.erase(mWindowMessageMap.begin(),mWindowMessageMap.end());

}

// Windowsメイン --------------------------------------------------------------
// Windowsくっさい部分
int SystemImpl::winMain (const HINSTANCE hThisInstance, const HINSTANCE hPrevInstance,
					const LPSTR lpszCmdParam, const int nCmdShow)
{
	MSG msg;

	//	HWND mHWNDMain;
	// デバッグ用


	setupMessageMap();

	//ウィンドウクラスの登録//
	
	//白ボテ？のウインドウを作成する。
	WNDCLASSW    wndclass;
    
	wndclass.style         = CS_DBLCLKS;   
	wndclass.lpfnWndProc   = &wndFunc;	 // window function
	wndclass.cbClsExtra    = 0;			 // no extra count of bytes
	wndclass.cbWndExtra    = 0;			 // no extra count of bytes
	wndclass.hInstance     = hThisInstance; // this instance
	wndclass.hIcon         = LoadIcon (hThisInstance,  MAKEINTRESOURCE(IDI_ICON1));
	wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH);
	wndclass.lpszMenuName  = NULL;		//メニューなしのウインドウ
	//MAKEINTRESOURCE (IDR_MENU1);;
    wndclass.lpszClassName = smAppName;	// アプリの名前
   
	RegisterClassW (&wndclass);
//WS_EX_TOPMOST
	//　この辺はほんとはConsoleクラスの実装クラスに入れたいのだが...	
    mHWNDMain = CreateWindowExW (
		WS_EX_APPWINDOW,				// window style
		smAppName,					// window class name
		smAppName,	// window caption
		WS_VISIBLE | WS_POPUP | WS_SYSMENU, // window style
		CW_USEDEFAULT,			// initial x position
		CW_USEDEFAULT,         // initial y position
		console::screen::WIDTH,                   // initial x size
		console::screen::HEIGHT,                   // initial y size
		NULL,                  // parent window handle
		NULL,                  // window menu handle
		hThisInstance,         // program instance handle
		NULL);                 // creation parameters

    // 作れない場合 終了
	if (!mHWNDMain)
		return FALSE;

	ShowWindow (mHWNDMain, nCmdShow);
	UpdateWindow (mHWNDMain);
	SetFocus(mHWNDMain);

	

#ifdef _DEBUG
	mbWindowed = true;
#endif
	try {
		// 画面オブジェクトを生成 
		mpConsole = new console::ConsoleImpl(mHWNDMain,mbWindowed);
		mpConsole->initialize();
		
		// サウンドオブジェクトを生成
		mpSound = new sound::SoundImpl();
		mpSound->initialize();
	
		// インプットデバイスオブジェクトの生成
		mpInput = new input::InputImpl(mHWNDMain);
		mpInput->initialize();

		//画面が落ち着くまで...	
		Sleep(3000);

		// メインオブジェクトの生成
//		mpMain = ::sf::application::Maininstance();
		mpMain->initialize();

	} catch (FatalErrorException& e) {
		errorTerminate(e.getDefaultMessage());
		return FALSE;
	} catch (RecoverbleErrorException& e) {
		using namespace sf::system;
		switch(e.errorType()){
		case Exception::SCENE_IS_EMPTY:
			return TRUE;
			break;
		default:
			throw;
		}
	} catch (...) {
		throw;
	}
	
	// タイマーの分解能をあげる処理
	initTimer();
	
//	HANDLE hProcess;
//	hProcess = GetCurrentProcess();
//	SetPriorityClass(hProcess,CRITICAL_PRIORITY_CLASS);

	// メッセージループ
	// ちょっとこの辺が、普通のWindowsアプリと違う部分。
	//  Windowメッセージがない場合は、メインを呼び出す。
	while(1)
	{
		if (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if (!GetMessage( &msg, NULL, 0, 0))
			{
				return msg.wParam;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else        
		{
			if(isWindowActive())
			{
				try{
					wait();
					// 画面更新 
					mpConsole->update();
					// 入力デバイスのスナップショットを取得
					mpInput->update();
					mpSound->update();
					
					mpMain->run();
//#ifdef _DEBUG
					mpConsole->print(0,console::viewport::X + 8,0.0f,0.0f,1.0f,0xffffffff,"%d ms ",mTimerElapsed);
//#endif
					// 処理時間
					mTimerElapsed = timeGetTime() - mTimerValue;
					} catch (FatalErrorException e) {
						delete mpConsole;
						mpConsole = NULL;
						errorTerminate(e.getDefaultMessage());
						windowActivate(false);
					} catch (RecoverbleErrorException e) {
						PostMessage( mHWNDMain, WM_CLOSE, 0, 0L );
						windowActivate(false);
					} catch (...) {
						throw;
					}
			}
		} 
	}
}

// Window Function ------------------------------------------------------------
//	ほんとに自分のわかる範囲の最低限のイベント処理しかしていないので簡単です。
LRESULT CALLBACK SystemImpl::wndFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// iSystemインターフェースのままでは、具象クラスのプライベートメソッドが
	// 呼び出せないので、ダイナミックキャスト
	
	SystemImpl *pSysSystemImpl = dynamic_cast<SystemImpl *>(instance());
	
	if(pSysSystemImpl == NULL)
		return DefWindowProc (hwnd, message, wParam, lParam);

	WindowMessage *pmessage = pSysSystemImpl->mWindowMessageMap[message]; 

	if(pmessage)
		return pmessage->call(hwnd,wParam,lParam);
	
	// message分岐 
	switch (message)
	{
        case WM_SYSCOMMAND:
			return pSysSystemImpl->onSysCommand(hwnd,wParam,lParam);
		// 終了するときの処理
		case WM_CLOSE:
			pSysSystemImpl->uninitialize();
	        DestroyWindow( hwnd );
			return 0;
	   case WM_DESTROY:
		  PostQuitMessage(0);      //terminate the application
		  return 0;
		default:
			break;
    }

	// その他はWindowsにお任せ 
    return DefWindowProc (hwnd, message, wParam, lParam);
}// WndFunc

// Windowsからの描画要求 --------------------------------------------------
LRESULT SystemImpl::onPaint(const HWND hwnd, const WPARAM wParam, const LPARAM lParam)
{
//   return DefWindowProc (hwnd, WM_PAINT, wParam, lParam);
	if(!mpConsole || !mpConsole->isEnabled())
		{
			PAINTSTRUCT ps;
			HDC hDC = BeginPaint (hwnd, &ps);
			std::string strMessage("初期化中");
			TextOut(hDC,0,0,strMessage.c_str(),strMessage.length());
			EndPaint(hwnd, &ps );	
		}
	ValidateRect(hwnd,NULL);		
	return 1;
}// onPaint()

// Windowがアクティブ、非アクティブ時の処理 -----------------------------------
LRESULT SystemImpl::onActivate(const HWND hwnd, const WPARAM wParam, const LPARAM lParam)
{
	if((BOOL)wParam){
		windowActivate(true);
	} else {
		windowActivate(false);
	}
	return 0;
}


// メニュー選択の時 -----------------------------------------------------------
LRESULT SystemImpl::onCommand(const HWND hwnd, const WPARAM wParam, const LPARAM lParam)
{
	// 現在は無視状態
    // Let Default Window Proc handle it for us.
    return DefWindowProc (hwnd, WM_COMMAND, wParam, lParam);
} // onCommand



// サイズ変更イベント ---------------------------------------------------------
LRESULT SystemImpl::onSize(const HWND hwnd,const WPARAM wParam,const LPARAM lParam)
{
	return 0;
}// onSize()

// 
LRESULT		SystemImpl::onSysCommand(const HWND hwnd, const WPARAM wParam, const LPARAM lParam)
{
       // Prevent moving/sizing and power loss in fullscreen mode
	switch( wParam )
	{
	case SC_MOVE:
	case SC_SIZE:
	case SC_MAXIMIZE:
	case SC_KEYMENU:
	case SC_MONITORPOWER:
		if(mbWindowed == false)
			return 1;
		break;
	}
	return DefWindowProc (hwnd, WM_SYSCOMMAND,wParam, lParam);
}


// タイマーの初期化処理 -------------------------------------------------------
void SystemImpl::initTimer(void)
{
	const UINT TARGET_RESOLUTION  = 1 ;// 1-millisecond target resolution

	::TIMECAPS tc;
	UINT timer_res;

	if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR) 
	{
		timer_res = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
		timeBeginPeriod(timer_res); 
	}
}// initTimer()

// 指定したtick(ms)までウェイト ------------------------------------------------
void SystemImpl::wait(void)
{

		DWORD now = timeGetTime();
		
		DWORD rem = now % mTimerInterval;
		DWORD wait_time = (now / mTimerInterval + ((rem > 0)?1:0)) * mTimerInterval ;
//		DWORD wait_time = (mTimerValue / mTimerInterval + 1) * mTimerInterval ;

		DWORD wait_time_tick = wait_time - now;

		if(wait_time_tick > 4) 
			Sleep(wait_time_tick - 3);

		while(wait_time > timeGetTime());
		mTimerBefore = mTimerValue;
		mTimerValue = timeGetTime();

}// wait()

// エラーを表示してアプリケーションを終了する --------------------------------- 
void SystemImpl::errorTerminate(const wchar_t *errString)
{
	outputError(errString);
	SendMessage( mHWNDMain, WM_CLOSE, 0, 0 );
}// errorTerminate()

// エラー表示 -----------------------------------------------------------------
void SystemImpl::outputError(const wchar_t *errString)
{	std::wstring err_string = L"Error Occured!\r\n";
	//strncat(err_string,errString,sizeof(err_string) - strlen(err_string));
	err_string += errString;
	MessageBoxW( NULL, err_string.c_str(),
		smAppName,
		MB_TOPMOST | MB_SETFOREGROUND | MB_TASKMODAL | MB_ICONERROR | MB_OK );
}// outputError()

// メッセージマップのセットアップ −−−−−−−−−−−−−−−−−−−−−
void SystemImpl::setupMessageMap()
{
/*	mWindowMessageMap[WM_PAINT] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onPaint);
	mWindowMessageMap[WM_ACTIVATEAPP] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onActivate);
	mWindowMessageMap[WM_COMMAND] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onCommand);
//	mWindowMessageMap[WM_KEYDOWN] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onKeyDown);
	mWindowMessageMap[WM_SIZE] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onSize);
*/
	addWindowMessage(WM_PAINT,new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onPaint));
	mWindowMessageMap[WM_ACTIVATEAPP] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onActivate);
	mWindowMessageMap[WM_COMMAND] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onCommand);
//	mWindowMessageMap[WM_KEYDOWN] = new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onKeyDown);
	addWindowMessage(WM_SIZE,new WindowMessageImpl<SystemImpl>(this,&SystemImpl::onSize));


}//setupMessageMap()


}
}// namespace sf