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

// GLPropPanel.cpp : インプリメンテーション ファイル

#include "stdafx.h"
#include "fugenView.h"
#include "fugenViewPropPane.h"
#include "GLPropPanel.h"
#include "GLColorPropPane.h"
#include "GLGeneralPropPane.h"
#include "GLCPlanePropPane.h"
#include "GLMeshPropPane.h"
#include "GLUnitPropPane.h"
#include "GLDisplayPropPane.h"


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

/////////////////////////////////////////////////////////////////////////////
// DDV

void DDV_AngleInt(CDataExchange* pDX, int nAngle){
	if(nAngle < 0 || nAngle >= 360){
		if(!pDX->m_bSaveAndValidate){
			TRACE0("Warning: initial dialog data is out of range.\n");
			return;
		}
		CString str;
		VERIFY(str.LoadString(IDS_WARNING_ANGLE_RANGE));
		AfxMessageBox(str);
		pDX->Fail();
	}
}

void DDV_PositiveDouble(CDataExchange* pDX, double dValue){
	if(dValue <= 0.){
		if(!pDX->m_bSaveAndValidate){
			TRACE0("Warning: initial dialog data is out of range.\n");
			return;
		}
		CString str;
		VERIFY(str.LoadString(IDS_WARNING_POSITIVE));
		AfxMessageBox(str);
		pDX->Fail();
	}
}

/////////////////////////////////////////////////////////////////////////////
// CGLPropPane ダイアログ

CGLPropPane::CGLPropPane(fugenDoc* pTargetDoc)
:m_pTargetDoc(pTargetDoc){;}

void CGLPropPane::OnOK(){
	if(!UpdateData()){
		TRACE("Warning CGLPropPane::UpdateData() \n");
		return;
	}
	CDialog::OnOK();
}

void CGLPropPane::OnCancel(){
	CDialog::OnCancel();
}

void CGLPropPane::OnApply(){
	if(!ApplyData()){
	}
}

void CGLPropPane::OnReset(){
	ResetToDefault();
}

BOOL CGLPropPane::OnInitDialog(){
	CDialog::OnInitDialog();
	LoadData();
	return false;
}

void CGLPropPane::PostNcDestroy(){
	delete this;
}

void CGLPropPane::LoadData(){
	if(m_pTargetDoc==NULL){
		LoadUserProf();
	} else {
		LoadDocContext();
	}
}

BOOL CGLPropPane::ApplyData(){
	BOOL bRet = false;
	if(m_pTargetDoc==NULL){
		bRet = UpdateUserProf();
	} else {
		bRet = UpdateDocContext();
		m_pTargetDoc->SetDirtyFlag();
	}
	return bRet;
}

void CGLPropPane::ResetToDefault(){
	if(m_pTargetDoc==NULL){
		ResetToDefaultUserProf();
	} else {
		ResetToDefaultDocContext();
	}
}

void CGLPropPane::LoadUserProf(){
}

BOOL CGLPropPane::UpdateUserProf(){
	return TRUE;
}

void CGLPropPane::ResetToDefaultUserProf(){
}

void CGLPropPane::LoadDocContext(){
}

BOOL CGLPropPane::UpdateDocContext(){
	return TRUE;
}

void CGLPropPane::ResetToDefaultDocContext(){
}


// MSDN 参照
void CGLPropPane::AdjustDroppedWidth(CComboBox& combo, bool bUseScrollBar){
	LONG nMaxWidth = 0;
	CDC* pDC = combo.GetDC();
	CFont* pFont = combo.GetFont();
	int nSaved = pDC->SaveDC();
	pDC->SelectObject(pFont);

	// Get the text metrics for avg char width
	TEXTMETRIC txtmt;
	pDC->GetTextMetrics(&txtmt);

	// Iterate through all strings in Combobox and get MaxWidth
	CString str;
	CSize szText;
	const int nItem = combo.GetCount();
	for(int i = 0; i < nItem; i++){
		combo.GetLBText(i, str);
		szText = pDC->GetTextExtent(str);

		// Add the avg width to prevent clipping
		szText.cx += txtmt.tmAveCharWidth;

		nMaxWidth = (std::max)(nMaxWidth, szText.cx);
	}

	// Select the old font back into the DC
	pDC->RestoreDC(nSaved);
	combo.ReleaseDC(pDC);

	nMaxWidth += 2 * ::GetSystemMetrics(SM_CXEDGE);

	// Adjust the width for the vertical scroll bar and
	// the left and right border.
	if(bUseScrollBar){
		nMaxWidth += ::GetSystemMetrics(SM_CXVSCROLL);
	}

	// Set the dropdown width of combobox.
	combo.SetDroppedWidth(nMaxWidth);
}

/////////////////////////////////////////////////////////////////////////////
// CGLPropPanel ダイアログ


CGLPropDialog::CGLPropDialog(fugenDoc* pDoc, CWnd* pParent, LPARAM lInitPageItemData)
	: CDialog(CGLPropDialog::IDD, pParent),
	  m_pPrevPane(0),
	  m_pDoc(pDoc),
	  m_nInitPageItemData(lInitPageItemData)
{
	//{{AFX_DATA_INIT(CGLPropPanel)
	//}}AFX_DATA_INIT
}

void CGLPropDialog::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CGLPropPanel)
	DDX_Control(pDX, IDC_TREE_PROP, m_tree);
	//}}AFX_DATA_MAP
}

CGLPropPane* CGLPropDialog::CreatePane(UINT nIDResource){
	switch(nIDResource){
	case IDD_PROP_VIEW:
		return new fugenViewPropPane(this->m_pDoc);
	case IDD_PROP_COLOR:
		return new CGLColorPropPane(this->m_pDoc);
	case IDD_PROP_GENERAL:
		return new CGLGeneralPropPane(this->m_pDoc);
	case IDD_PROP_CPLANE:
		return new CGLCPlanePropPane(this->m_pDoc);
	case IDD_PROP_MESH:
		return new CGLMeshPropPane(this->m_pDoc);
	case IDD_PROP_UNIT:
		return new CGLUnitPropPane(this->m_pDoc);
	default:
		return new CGLPropPane(this->m_pDoc);
	}
}

// MSDN の例を一部改変
fugenView* CGLPropDialog::GetActiveView() const
{
	CMDIFrameWnd* pFrame = static_cast<CMDIFrameWnd*>(AfxGetMainWnd());
	// Get the active MDI child window.
	CMDIChildWnd *pChild = pFrame->MDIGetActive();

	// Get the active view attached to the active MDI child
	// window.
	fugenView* pView =  static_cast<fugenView*>(pChild->GetActiveView());
	if(pView->GetDocument() == GetDocument()){
		return pView;
	}
	return 0;
}

void CGLPropDialog::MakeTree(){
	// ツリーアイテムは子ダイアログのリソースIDとする
	CString str;
	// 現在のドキュメントの設定シリーズ
	if(GetDocument()){
		VERIFY(str.LoadString(IDS_PROP_DOCUMENT));
		HTREEITEM hTreeItem = m_tree.InsertItem(
			TVIF_TEXT | TVIF_PARAM, str, -1,-1,-1,-1, 0, TVI_ROOT, TVI_LAST);

		// 色
		VERIFY(str.LoadString(IDS_PROP_COLOR));
		InsertPage(str, hTreeItem, IDD_PROP_COLOR);

		// メッシュ
		VERIFY(str.LoadString(IDS_PROP_MESH));
		InsertPage(str, hTreeItem, IDD_PROP_MESH);

		// 単位
		VERIFY(str.LoadString(IDS_PROP_UNIT));
		InsertPage(str, hTreeItem, IDD_PROP_UNIT);

		// グリッド
		VERIFY(str.LoadString(IDS_PROP_CPLANE));
		InsertPage(str, hTreeItem, IDD_PROP_CPLANE);

	} else {
		// DocumentがNULLの場合はアプリケーション設定。

		//ツリーのルート
		VERIFY(str.LoadString(IDS_PROP_FUGEN_OPTION));
		HTREEITEM hTreeItem = m_tree.InsertItem(
			TVIF_TEXT | TVIF_PARAM, str, -1,-1,-1,-1, 0, TVI_ROOT, TVI_LAST);

		//ツリーの子アイテム追加。子アイテムは挿入順に並ぶ

		// ビュー
		VERIFY(str.LoadString(IDS_PROP_VIEW));
		InsertPage(str, hTreeItem, IDD_PROP_VIEW);

		// 全般
		VERIFY(str.LoadString(IDS_PROP_GENERAL));
		InsertPage(str, hTreeItem, IDD_PROP_GENERAL);

		// 色
		VERIFY(str.LoadString(IDS_PROP_COLOR));
		InsertPage(str, hTreeItem, IDD_PROP_COLOR);

		// メッシュ
		VERIFY(str.LoadString(IDS_PROP_MESH));
		InsertPage(str, hTreeItem, IDD_PROP_MESH);

		// 単位
		VERIFY(str.LoadString(IDS_PROP_UNIT));
		InsertPage(str, hTreeItem, IDD_PROP_UNIT);
	}

}

void CGLPropDialog::InsertPage(LPCTSTR lpszItem, HTREEITEM hItem, LPARAM lParam){
	m_tree.InsertItem(
		TVIF_TEXT | TVIF_PARAM, lpszItem, -1,-1,-1,-1, lParam, hItem, TVI_LAST);
}

void CGLPropDialog::OnCancel(){
	CDialog::OnCancel();
}

void CGLPropDialog::OnApply(){
	ApplyData();
}

void CGLPropDialog::OnOK(){
	ApplyData();
	EndDialog(IDOK);
}

BOOL CGLPropDialog::ApplyData(){
	std::map<LPARAM, CGLPropPane*>::iterator cur = m_pane.begin(), last = m_pane.end();
	for(; cur != last; ++cur){
		CGLPropPane* pDlg = cur->second;
		if(!pDlg->UpdateData()){
			continue;
		}
		// データを回収する
		pDlg->ApplyData();
	}

	return TRUE;
}

BEGIN_MESSAGE_MAP(CGLPropDialog, CDialog)
	//{{AFX_MSG_MAP(CGLPropPanel)
	ON_BN_CLICKED(IDC_APPLY, OnApply)
	ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_PROP, OnSelchangedTreeProp)
	ON_NOTIFY(TVN_SELCHANGING, IDC_TREE_PROP, OnSelchangingTreeProp)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

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

namespace
{
	HTREEITEM FindItem(LPARAM lParam, const CTreeCtrl& tree, HTREEITEM hRoot)
	{
		if(!hRoot){
			hRoot = tree.GetNextItem(0, TVGN_ROOT);
			while(hRoot){
				if(HTREEITEM hItem = FindItem(lParam, tree, hRoot)){
					return hItem;
				}
				hRoot = tree.GetNextItem(hRoot, TVGN_NEXT);
			}
			return 0;
		}

		// check whether the current item is the searched one
		const DWORD_PTR val = tree.GetItemData(hRoot);
		if(val == lParam){
			return hRoot;
		}

		// get a handle to the first child item
		HTREEITEM hItem = tree.GetNextItem(hRoot, TVGN_CHILD);
		// iterate as long a new item is found
		while (hItem) {
			// check the children of the current item
			HTREEITEM hFound = FindItem(lParam, tree, hItem);
			if(hFound){
				return hFound;
			}
			// get the next sibling of the current item
			hItem = tree.GetNextItem(hItem, TVGN_NEXT);
		}

		// return NULL if nothing was found
		return 0;
	}
}

BOOL CGLPropDialog::OnInitDialog()
{
	if(!CDialog::OnInitDialog()){
		return FALSE;
	}

	SetIcon(::AfxGetApp()->LoadIcon(IDR_MGCLTYPE), FALSE);

	// サイズを記録
	m_tree.GetClientRect(m_rcTree);
	CRect rc;
	GetClientRect(rc);
	m_ptOrg.x = rc.left + m_rcTree.Width() + 20; // offsetベタ書き
	m_ptOrg.y = rc.top + 18;

	// 完全なツリーを生成する
	MakeTree();

	// 最初に選択されているのは何か
	m_tree.Expand(m_tree.GetRootItem(), TVE_EXPAND);

	HTREEITEM hItemActive = 0;
	if(m_nInitPageItemData){
		hItemActive = FindItem(m_nInitPageItemData, m_tree, 0);
	}
	m_tree.SelectItem(hItemActive ? hItemActive : m_tree.GetRootItem());

	return TRUE;
}

void CGLPropDialog::OnSelchangingTreeProp(NMHDR* pNMHDR, LRESULT* pResult)
{
	NM_TREEVIEW* pNMTreeView = reinterpret_cast<NM_TREEVIEW*>(pNMHDR);
	// Hide old pane.
	if(m_pPrevPane->GetSafeHwnd()){
		// !!! Validate input data before swicthing panes.
		if(!m_pPrevPane->UpdateData()){
			pNMTreeView->itemNew = pNMTreeView->itemOld;
			*pResult = -1;
		}else{
			m_pPrevPane->ShowWindow(SW_HIDE);
			*pResult = 0;
		}
	}
}

void CGLPropDialog::OnSelchangedTreeProp(NMHDR* pNMHDR, LRESULT* pResult){
	NM_TREEVIEW* pNMTreeView = reinterpret_cast<NM_TREEVIEW*>(pNMHDR);
	TV_ITEM tv = pNMTreeView->itemNew;

	tv.mask = TVIF_PARAM;
	m_tree.GetItem(&tv);
	if(tv.lParam){
		// Search new pane.
		CGLPropPane* pNewPage = 0;
		std::map<LPARAM, CGLPropPane*>::iterator i = m_pane.find(tv.lParam);
		if(i == m_pane.end()){
			// ペインの初期化
			UINT nData = static_cast<UINT>(tv.lParam);
			pNewPage = CreatePane(nData);
			pNewPage->Create(nData, this); // OnInitDialog を間接的に呼ぶ
			m_pane.insert(std::make_pair(nData, pNewPage));
		}else{
			pNewPage = i->second;
		}

		// Show new pane.
		if(pNewPage->GetSafeHwnd()){
			pNewPage->SetWindowPos(&wndTop, m_ptOrg.x, m_ptOrg.y, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE);
			m_pPrevPane = pNewPage;
		}
	}
	*pResult = 0;
}
