/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/

// fugen.cpp : アプリケーション用クラスの機能定義を行います。

#include "stdafx.h"
#include "mg/GelPositions.h"
#include "mg/PickObject.h"
#include "mgGL/MGStringWriter.h"

#include "fugen.h"
#include "AboutDlg.h"

#include "MainFrm.h"
#include "ChildFrm.h"
#include "SplitChildFrame.h"
#include "fugenView.h"
#include "GLParamView.h"
#include "fugenView2Dxy.h"
#include "fugenView2Dyz.h"
#include "fugenView2Dxz.h"
#include "fugenViewPlane.h"

#include "GLTessWView.h"
#include "GLTessPView.h"
#include "fugenDocManager.h"

#include "IO/GLFileManager.h"
#include "Misc/vcdbgbuf.h"
#include "Misc/UserPreference.h"
#include "Common/GLCommandFactory.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace Gdiplus;

/////////////////////////////////////////////////////////////////////////////
// fugen

BEGIN_MESSAGE_MAP(fugen, CWinApp)
	//{{AFX_MSG_MAP(fugen)
	
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_HELP_WEB, OnHelpWeb)
	ON_COMMAND(ID_FILE_SAVE_ALL, OnFileSaveAll)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_ALL, OnUpdateFileSaveAll)
	//}}AFX_MSG_MAP
	// 標準のファイル基本ドキュメント コマンド
	ON_COMMAND(ID_FILE_NEW, OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
	// 標準の印刷セットアップ コマンド
	ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)

	ON_UPDATE_COMMAND_UI(ID_FILE_MRU_FILE1, OnUpdateRecentFileMenu)
	ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE16, OnOpenRecentFile)

	//ON_COMMAND(ID_HELP, OnHelp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// fugen クラスの構築

fugen::fugen()

	: m_hRes(0),
	   m_pParamTemplate(0),
	   m_p2DxyViewTemplate(0),
	   m_p2DyzViewTemplate(0),
	   m_p2DxzViewTemplate(0),
	   m_pTexturePlaneTemplate(0),
	   m_spCmdFactory(new CGLCommandFactory){
	// コンストラクターの中から呼び出す。
	EnableHtmlHelp();
}

/////////////////////////////////////////////////////////////////////////////
// 唯一の fugen オブジェクト

fugen theApp;

/////////////////////////////////////////////////////////////////////////////
// fugen クラスの初期化

void fugen::InitLanguage(){
	ASSERT(!m_hRes);
	ASSERT(m_pUserPref.get());

	HINSTANCE hRes = 0;
	LPCTSTR lpszSuffix = _T(".chm");
	int nLang = m_pUserPref->GetIntValue(upv_Screen_Lang);
	switch(nLang){
	case 0: // 0: en (US)
		hRes = ::LoadLibrary(_T("res_en.dll"));
		lpszSuffix = _T("_en.chm");
		break;
	case 1: // 1: jpn; exe 内のリソースをそのまま利用する。
		break;
	default:
		ASSERT(!"nLang is out of range.");
		break;
	}

	if(hRes){
		::AfxSetResourceHandle(hRes);
		m_hRes = hRes;
	}

#if 0 //defined(_AFXDLL)
	// LoadString が改善されるが、コモンダイアログリソースとかは駄目。
	AFX_MODULE_STATE* pState = ::AfxGetModuleState();
	if(pState->m_appLangDLL){
		LPCTSTR lpszDll = 0;
		switch(nLang){
		case 0:
			lpszDll = _T("mfc71enu.dll");
			break;
		case 1:
			lpszDll = _T("mfc71jpn.dll");
			break;
		default:
			ASSERT(!"nLang is out of range.");
			break;
		}

		if(lpszDll){
			HMODULE hInst = ::LoadLibrary(lpszDll);
			if(hInst){
				::FreeLibrary(pState->m_appLangDLL);
				pState->m_appLangDLL = hInst;
			}
		}
	}
#endif

	// HTML Help のファイル名修正
	InitHelpPath(lpszSuffix);

	// 文字描画のための準備
	MGStringWriter::Init();
}

// appinit.cpp より拝借
void fugen::InitHelpPath(LPCTSTR lpszSuffix)
{
	ASSERT(m_pszHelpFilePath);
	ASSERT(m_eHelpType == afxHTMLHelp);

	// get path of executable
	TCHAR szBuff[_MAX_PATH];
	DWORD dwRet = ::GetModuleFileName(m_hInstance, szBuff, _MAX_PATH);
	ASSERT(dwRet != 0 && dwRet != _MAX_PATH);
	if(dwRet == 0 || dwRet == _MAX_PATH){
		AfxThrowUserException();
	}

	LPTSTR lpszExt = ::PathFindExtension(szBuff);
	ASSERT(lpszExt);
	if(!lpszExt){
		AfxThrowUserException();
	}

	ASSERT(*lpszExt == '.');
	*lpszExt = 0; // no suffix

	lstrcpy(lpszExt, lpszSuffix);
	BOOL bEnable = AfxEnableMemoryTracking(FALSE);
	free((void*)m_pszHelpFilePath); // CWinApp::~CWinApp style
	m_pszHelpFilePath = _tcsdup(szBuff);
	AfxEnableMemoryTracking(bEnable);
	*lpszExt = '\0';
}

namespace {
	const bool NEED_TO_GDISTARTUP = true;
}

BOOL fugen::InitInstance(){
	// 1. レジストリー関連が先頭。
	// 設定が保存される下のレジストリ キーを変更します。
	SetRegistryKey(_T("System fugen G.K."));
	//Set up the singleton of UserPreference.
	m_pUserPref = std::unique_ptr<UserPreference>(new UserPreference());
	m_pUserPref->Init();
	m_pUserPref->LoadRegistry(*this);
	UserPreference::setInstance(m_pUserPref.get());

	// 2. リソースハンドル固定化がその次。
	// 基底 InitInstance より先にリソースロードが来るようだ。
	InitLanguage();

	// 3. 基底クラスの InitInstance 呼び出し。
	if(!CWinApp::InitInstance()){
		return FALSE;
	}

	MGCL::start_up(NEED_TO_GDISTARTUP);

	InitDebugTools();

	InitFileHandlers();
	InitCommands();

	AfxEnableControlContainer();
	AfxInitRichEdit2();

	LoadStdProfileSettings(16);  // 標準の INI ファイルのオプションをローﾄﾞします (MRU を含む)

	const UINT nRes = IDR_MGCLTYPE;

	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(
		nRes,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CSplitChildFrame),
		RUNTIME_CLASS(fugenView));
	AddfugenDocTemplate(pDocTemplate);

	m_pParamTemplate = new CMultiDocTemplate(
		IDR_MGCLTYPE_LIGHT,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(CGLParamView));

	m_p2DxyViewTemplate = new CMultiDocTemplate(
		nRes,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(fugenView2Dxy));

	m_p2DyzViewTemplate = new CMultiDocTemplate(
		nRes,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(fugenView2Dyz));

	m_p2DxzViewTemplate = new CMultiDocTemplate(
		nRes,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(fugenView2Dxz));

	m_pTessWorldTemplate = new CMultiDocTemplate(
		IDR_MGCLTYPE_LIGHT,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(CGLTessWView));

	m_pTessParamTemplate = new CMultiDocTemplate(
		IDR_MGCLTYPE_LIGHT,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(CGLTessPView));

	m_pTexturePlaneTemplate = new CMultiDocTemplate(
		IDR_SHADING,
		RUNTIME_CLASS(fugenDoc),
		RUNTIME_CLASS(CChildFrame),
		RUNTIME_CLASS(fugenViewPlane));

	// メイン MDI フレーム ウィンドウを作成
	fugenMainFrame* pMainFrame = new fugenMainFrame;
	if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
		return FALSE;
	m_pMainWnd = pMainFrame;

	// ドラッグ/ドロップ のオープンを許可します
	m_pMainWnd->DragAcceptFiles();

	// DDE Execute open を使用可能にします。
	EnableShellOpen();
	RegisterShellFileTypes(TRUE);

	// DDE、file open など標準のシェル コマンドのコマンドラインを解析します。
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);
	if(cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew){
		// 起動時に新しい MDI 子ウィンドウを表示しない!!!
		cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
	}

	// コマンドラインでディスパッチ コマンドを指定します。
	if (!ProcessShellCommand(cmdInfo)) return FALSE;

	ShowAppState();

	// メイン ウィンドウが初期化されたので、表示と更新を行います。
	pMainFrame->LoadWindowPos();
	pMainFrame->ShowWindow(m_nCmdShow);
	pMainFrame->UpdateWindow();


	return TRUE;
}

void fugen::AddfugenDocTemplate(CDocTemplate* pDocTemplate){
	if(m_pDocManager == 0){
		m_pDocManager = new fugenDocManager;
	}
	m_pDocManager->AddDocTemplate(pDocTemplate);
	ASSERT_VALID(m_pDocManager);
}

//Get curent CView.
CView* fugen::current_view()const{
	ASSERT_VALID(m_pMainWnd);
	ASSERT(m_pMainWnd == AfxGetMainWnd());

	CMDIFrameWnd *pFrame = STATIC_DOWNCAST(CMDIFrameWnd, m_pMainWnd);
	ASSERT(pFrame);

	// アクティブな MDI 子ウィンドウを取得する。 
	CMDIChildWnd *pChild = STATIC_DOWNCAST(CMDIChildWnd, (pFrame->GetActiveFrame()));
	if(!pChild) return 0;

	// アクティブな MDI 子ウィンドウに結び付けられているアクティブなビューを取得する。
	return pChild->GetActiveView();
}

//Get curent fugenView.
fugenView* fugen::currentfugenView(const fugenDoc* pDoc)const{
	ASSERT_VALID(pDoc);
	fugenView* pView = DYNAMIC_DOWNCAST(fugenView, current_view());
	if(!pView) return 0;

	if(pView->GetDocument() != pDoc) return 0;
	return pView;
}

bool fugen::IsModifiedDocumentExist() const{
	POSITION posDocTemp = GetFirstDocTemplatePosition();
	while(posDocTemp){
		CDocTemplate* pDocTemp = GetNextDocTemplate(posDocTemp);
		if(pDocTemp){
			POSITION posDoc = pDocTemp->GetFirstDocPosition();
			while(posDoc){
				CDocument* pDoc = pDocTemp->GetNextDoc(posDoc);
				if(pDoc && pDoc->IsModified()){
					return true;
				}
			}
		}
	}
	return false;
}

BOOL fugen::PromptFileName(CString& newFile, BOOL bOpen){
	return m_pDocManager->DoPromptFileName(newFile, AFX_IDS_OPENFILE,
		OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, bOpen, NULL);
}

BOOL fugen::PromptFileNameMulti(std::vector<CString>& newFile)
{
	fugenDocManager* pDocManager = static_cast<fugenDocManager*>(m_pDocManager);
	ASSERT_VALID(pDocManager);
	return pDocManager->PromptFileNameMulti(newFile);
}

/////////////////////////////////////////////////////////////////////////////
// fugen メッセージ ハンドラ

// Don't apply CWinApp::SaveAllModified().
void fugen::OnFileSaveAll(){
	int nDoc = 0;
	POSITION posDocTemp = GetFirstDocTemplatePosition();
	while(posDocTemp){
		CDocTemplate* pDocTemp = GetNextDocTemplate(posDocTemp);
		if(pDocTemp){
			POSITION posDoc = pDocTemp->GetFirstDocPosition();
			while(posDoc){
				CDocument* pDoc = pDocTemp->GetNextDoc(posDoc);
				if(pDoc && pDoc->IsModified()){
					// CDocument::OnFileSave() is protected...
					if(pDoc->DoFileSave()){
						nDoc++; // count
						ASSERT(!pDoc->IsModified());
					}else{
						// Unnamed document is denied saving as a file.
						pDoc->SetModifiedFlag(FALSE); // warning message.
//						pDoc->OnCloseDocument();
					}
				}
			}
		}
	}
	CString strFormat;
	VERIFY(strFormat.LoadString(IDS_SAVE_ALL_FORMAT));

	CString strDisplay;
	strDisplay.Format(strFormat,nDoc);

	// ステータスバーに表示する
	::AfxGetMainWnd()->SendMessage(
		WM_GL_STATUSBAR, 0, (LPARAM)(LPCTSTR)strDisplay);
}

void fugen::OnUpdateFileSaveAll(CCmdUI* pCmdUI){
	pCmdUI->Enable(IsModifiedDocumentExist());
}

// ダイアログを実行するためのアプリケーション コマンド
void fugen::OnAppAbout(){
	CAboutDlg aboutDlg;
	aboutDlg.DoModal();
}

void fugen::OnHelpWeb(){
	CString strURL;
	VERIFY(strURL.LoadString(IDS_URL_FUGEN));
	::ShellExecute(GetMainWnd()->GetSafeHwnd(), _T("open"), strURL, 0, 0, SW_SHOW);
}

void fugen::OnUpdateRecentFileMenu(CCmdUI* pCmdUI){
	if(pCmdUI->m_pSubMenu) // updating a submenu?
	{
		// update your submenu here, if you need to
		
		return;
	}
	CWinApp::OnUpdateRecentFileMenu(pCmdUI);
}

int fugen::ExitInstance(){
	SaveAppState();
	delete m_pParamTemplate;
	delete m_p2DxyViewTemplate;
	delete m_p2DyzViewTemplate;
	delete m_p2DxzViewTemplate;
	delete m_pTessWorldTemplate;
	delete m_pTessParamTemplate;
	//delete m_pShadeTemplate;
	//delete m_pRenderTemplate;
	delete m_pTexturePlaneTemplate;
	MGCL::shut_down();

	if(m_hRes){
		::FreeLibrary(m_hRes);
		m_hRes = 0;
	}

	return CWinApp::ExitInstance();
}

CGLCommandFactory& fugen::GetCommandFactory(){
	ASSERT(m_spCmdFactory.get());
	return *m_spCmdFactory.get();
}

fugenStatusBar& theStatusBar(){
	return theApp.MainFrame()->status_bar();
}

MGCursorRestriction& theCursorRestriction(){
	fugenStatusBar& sbar = theStatusBar();
	return sbar.restrictionMode();
}

///Show(show=true) or hide(show=false) the output window.
void fugen::showOutputWindow(bool show){
	fugenMainFrame& mainFrame = *MainFrame();
	CControlBar* pBar = mainFrame.GetControlBar(ID_VIEW_PROMPT_BAR);
	mainFrame.ShowControlBar(pBar, show, FALSE);
}

void putInOutputWindow(const CString& string){
	theApp.showOutputWindow(true);
	COUT << (TCAST)string << std::endl;
}
void putInOutputWindow(const std::string& string){
	theApp.showOutputWindow(true);
	std::cout<<string <<std::endl;
}
void putInOutputWindow(const char string[]){
	theApp.showOutputWindow(true);
	std::cout<<string <<std::endl;
}