/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno             */
/* All rights reserved.                                             */
/********************************************************************/
/**
 * @file fugenDoc.h
 * @brief クラス fugenDoc の宣言
 */
#if !defined(AFX_fugenDoc_H__8A2ED6BE_2FF4_4B96_B975_98B3658F2584__INCLUDED_)
#define AFX_fugenDoc_H__8A2ED6BE_2FF4_4B96_B975_98B3658F2584__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <stack>
#include "mg/Box.h"
#include "mg/Position.h"
#include "mg/Group.h"
#include "mg/GelPositions.h"
#include "mg/PickObjects.h"
#include "mgGL/Context.h"
#include "fugen.h"
#include "Undo/IActionTarget.h"
#include "Common/CursorRestriction.h"

struct IActionManager;
class MGObject;
class MGLocateInfo;
class MGCommandBase;
class MGSelectState;
class MGIdleCommandTool;
class MGNamedViewTool;
class MGNamedCPlaneTool;
class fugenView;

class fugenDoc : public CDocument,
               public IActionTarget
{
	DECLARE_DYNCREATE(fugenDoc)
	friend class mgGelAction; // remove_current のみ。
	friend class MGCommandBase; // root_groupアクセス用
	friend class MGNamedViewTool; // NamedView対応。要検討
	friend class MGNamedCPlaneTool; // WorkPlane対応。要検討

protected: // シリアライズ機能のみから作成します。
	fugenDoc();

public:
	/// getMainView()で取得できるViewに対応するMGGroupを返却
	/// 返却されるMGGroupは、Viewに含まれるすべてのオブジェクトとなる。
	virtual MGGroup& get_main_group(){return m_group;};
	virtual MGGroup& get_main_group(fugenView* View){return m_group;};

	//Add an object newobj to the document root group.
	//newobj must be a newed object and the ownership is transfered
	//to the document.
	void add_object_to_root(MGObject* newobj);

	///append the current objects(MGGelPositions).
	void append_current_object(const MGGelPositions& gelps, bool redraw=true);

	///Return the box of this document.
	const MGBox& box()const;

	///Set the box data of this document.
	void setBox(const MGBox& box);

	///Set the defalut box(origin+ length 10 for each axis).
	void setDefaultBox();

	///Clear the last executed command.
	void clearLastCommand();

	///Clear the undo/redo actions.
	void clearUndoRedo();

	///Get the main view's context. When this doc does not hold the context, the context
	///is generated from the box data.
	MGContext* context();
	const MGContext* context() const;

	///Return the current command tool.
	MGCommandBase* current_command_tool(){return m_command_tool_stack.top();}
	const MGCommandBase* current_command_tool() const{
		return m_command_tool_stack.top();
	}
	
	///Get the document's current group, which is the target group of fugenDoc that contains
	//objects to add or update.
	MGGroup* current_group(){return m_current_group;};
	const MGGroup* current_group()const{return m_current_group;};

	///Set the current group for the document update.
	void set_current_group(MGGroup* grp){
		m_current_group=grp;
	};

	const MGPickObjects& current_objects()const {return m_current_objects;};
	MGPickObjects& current_objects(){ return m_current_objects; };

	///Return the cursor position data of the doc coordinate.
	const MGPosition& cursor() const{ return m_cursor;}
	void set_cursor(const MGPosition& pos){ m_cursor = pos;}

	// 表示モードを表す値を返す
	//WIRE=0,		///< wire frame mode
	//SHADING=1,	///< surface mode
	//WIRE_AND_SHADING=2 ///< wire and surface mode
	MGCL::VIEWMODE GetViewMode()const{return m_viewMode;}

	// 表示モードを変更する
	void ChangeViewMode(MGCL::VIEWMODE mode, bool bNotify = true);

	/// Return the current snapping operation status.
	MGSnapAttrib& getSnapAttr(){return context()->snap_attrib();}
	const MGSnapAttrib& getSnapAttr() const {return context()->snap_attrib();}
	
	///Test if this doc's main view includes a system display list of
	///the function code function_code.
	bool includes_sysgl(size_t function_code)const;

	///Invalidate all the standard views.
	void InvalidateAllStdViews();

	///Test if the command tool is current and breaking into the other command.
	bool is_breaking_command(const MGCommandBase* tool)const;

	/// Return the number of objects of class MGGel (not MGObject)
	/// which this document has.
	int num_of_objects() const{return m_group.num_of_objects();}

	/// Set the gel positions that are added to this document.
	/// set_last_added is called only by CGelAddAction::Do().
	void set_last_added(const MGGelPositions& gelps){ m_last_added = gelps;}

	///Set the main view of this fugenDoc.
	virtual void set_main_view(fugenView* mainView);

	///Get the document's main view.
	fugenView* get_main_view(){return m_main_view;};
	const fugenView* get_main_view()const;

	/// 選択オブジェクトを新規ファイルに保存する。
	/// @param[in] strPath エクスポート先フルパス
	/// @param[in] target 保存したいオブジェクト
	///
	/// @retval -1 保存形式が不明でエラー
	/// @retval  0 ファイルが書き込みできなかった等
	/// @retval  N (>=0) 保存できたオブジェクトの個数
	///
	/// @throws 特別な例外は投げない。
	///
	/// この関数はおそらくいずれ fugenDoc の外に追い出すことになるだろう。
	int ExportSelection(const CString& strPath, const MGPickObjects& target);

	/// ツール＞オプション ダイアログでの処理後に呼び出す。
	/// @throws 特別な例外は投げない。
	//void PostEditProperty();

	/// コマンドスタックサイズ取得(アイドルはカウントしない)
	int getCommandStackSize() {return (int)m_command_tool_stack.size() - 1;};
	/// 割り込みコマンド終了
	void cancel_breaking_command_tool();
	
// オペレーション
public:

	///cancel the current command tool.
	///cancel_command_tool will invoke ctool's OnCommandEnd and
	///terminate_tool(true) and delete ctool.
	void cancel_command_tool(
		MGCommandBase* ctool,	///<command tool to cancel.
		///<This ctool will be removed from the command stack.
		UINT nIDS=1
		);

	///全てのコマンドを終了する
	void cancel_command_tool_all();


	///terminate the command ctool as normal end.
	///normalEnd_command_tool will invoke ctool's OnCommandEnd and
	///terminate_tool(false). Then delete ctool.
	void normalEnd_command_tool(
		MGCommandBase* ctool,	///<command tool to cancel.
		///<This ctool will be removed from the command stack.
		UINT nIDS=1,	///<=0: erase the current message, and display no messages.
		///<=1: display "xxxx" normally end.
		///<=2: display "xxxx" failed.
		///otherwise: nIDS is a string id, and load the message from string table to display.
		bool erase_temporary_display=true
		///true when erase_temporary() will be invoked.
		);

	///Delete all the temporary display lists of all the views of this doc.
	///Temporary means pictures not included in the document.
	void DeleteDisplayList();

	///Delete the temporary display list of all the views of this doc, which was displayed
	///by the command id(Function id of system display list of fugenView::push_back_to_sysgl).
	void DeleteDisplayList(size_t command_id, bool redraw=false);

	///Delete the temporary display list of all the views of this doc, which was displayed
	///by the command id(Function id of system display list of fugenView::push_back_to_sysgl) and
	///the object id gel.
	void DeleteDisplayList(size_t command_id, const MGGel* gel, bool redraw=false);

	/// Enable grid-snap.
	void enableGridSnap(bool enable = true);

	int load(LPCTSTR file);

	///Lock or unlock all the windows update of this doc.
	void lockAllWindowsUpdate();
	void unlockAllWindowsUpdate();

	/// Note that this function returns null position if there is no input
	/// position when it is called.
	const MGPosition& recent_input_position()const;

	///Replace the input group data.
	///When this is invoked,
	///(1) current objects are cleared.
	///(2) All of the data of the group docGroup are replaced with the data of newGroup.
	///(3) The docGroup's current displays are erased and new data of newGroup will be showed.
	void replace(
		MGGroup& docGroup,	///A group of this document
		MGGroup&& newGroup
	);

	/// The most recent input position is usually set by this class.
	/// However, the position may be modified in OnLocate() of derived class.
	/// This function is called in such case.
	void set_recent_input_position(const MGPosition& pos);

	///Save all the standard views' viewing context in context.
	void save_view_context();

	///Apply drawing attributes and colors of this context to all views.
	void apply_view_context_colors();

	///Set current objects of this doc's views.
	///These functions do update views also.
	void set_current_object(bool redraw=true);
	void set_current_object(const MGGelPositions& gelps,bool redraw=true);

	///Set the current command tool.
	///If ntool's can_break_into() return true, the current command tool will be pushed
	///onto the doc's command stack, then ntool will be set current.
	///If command pusing was done, the pushed command will resume the command currency
	///on termination of ntool,
	///If can_break_into() return false, command manager of the doc will terminate
	///the current command(invoke terminate_tool()) and not push to command stack),
	///then ntool will be set current.
	///The ntool's initiate_tool() will be invoked.
	void set_current_command_tool(MGCommandBase* ntool);

	virtual void SetModifiedFlag(BOOL bModified = TRUE);

	///The function's return value is true, if the tool termination is accepted.
	///False, if the termination is refused.
	bool terminate_current_command_tool();

	/// Pseudo-UpdateAllViews.
	void UpdateAllStdViews(const MGBox* box=0);

//オーバーライド
	//{{AFX_VIRTUAL(fugenDoc)
public:
	virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo);
	virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
	virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
	virtual void OnCloseDocument();
	virtual void DeleteContents();
	//}}AFX_VIRTUAL

// インプリメンテーション
public:
	virtual ~fugenDoc();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif
	
	///@name IActionTarget
	//@{
	virtual IActionManager* GetActionManager();
	virtual const IActionManager* GetActionManager() const;
	virtual void SetActionManager(IActionManager* pMgr);

	virtual bool IsDirty() const;
	virtual void SetDirtyFlag(bool bDirty = true);
	//@}
	
public:
	// Create a new frame window from this object and a document template.
	// pTemplate must be one of the document template members of class fugen.
	// See the header file fugen.h.
	void InitialUpdateFrame(
		CDocTemplate* pTemplate,
		bool inherit_view_env=true
			//True if necessary to inherit the viewing environment from the current view.
		);

	//コマンド終了処理、ドキュメントを閉じるときに呼ぶ
	void clearCommand();

	void updateSnapMode();

// 生成されたメッセージ マップ関数
protected:
	//{{AFX_MSG(fugenDoc)

	// ファイル
	//@{
	afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
	afx_msg void OnUpdateFileSaveAs(CCmdUI* pCmdUI);
	//@}

	// 編集
	//@{
	afx_msg void OnEditUndo();
	afx_msg void OnEditRedo();
	afx_msg void OnEditCopy();
	afx_msg void OnEditCut();
	afx_msg void OnEditPaste();
	afx_msg void OnEditClear();
	afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEditRedo(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEditCut(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEditCopy(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEditPaste(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEditClear(CCmdUI* pCmdUI);

	// 編集＞オブジェクトを選択
	//@{
	afx_msg void OnSelectAll();
	afx_msg void OnSelectClear();
	afx_msg void OnSelectInvert();
	afx_msg void OnSelectLast();
	afx_msg void OnSelectPoint();
	afx_msg void OnSelectCurve();
	afx_msg void OnSelectSurface();
	afx_msg void OnSelectShell();
	afx_msg void OnSelectMesh();
	afx_msg void OnUpdateSelectAll(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectClear(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectInvert(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectLast(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectPoint(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectCurve(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectSurface(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectShell(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSelectMesh(CCmdUI* pCmdUI);
	//@}

	// 編集＞表示
	//@{
	afx_msg void OnDisplayHide();
	afx_msg void OnDisplayShow();
	afx_msg void OnDisplayHideSwap();

	afx_msg void OnUpdateDisplayHide(CCmdUI* pCmdUI);
	afx_msg void OnUpdateDisplayShow(CCmdUI* pCmdUI);
	//@}
	//@}

	// ビュー
	//@{
	afx_msg void OnZoomExtentAllView();
	afx_msg void OnZoomSelectedAllView();
	afx_msg void OnViewWireframe();
	afx_msg void OnViewShading();
	afx_msg void OnViewWireAndShading();
	afx_msg void OnViewProjectXY();
	afx_msg void OnViewProjectYZ();
	afx_msg void OnViewProjectZX();

	afx_msg void OnUpdateZoomExtentAllView(CCmdUI* pCmdUI);
	afx_msg void OnUpdateViewZoomSelectedAll(CCmdUI* pCmdUI);
	afx_msg void OnUpdateZoomSelectedAllView(CCmdUI* pCmdUI);
	afx_msg void OnUpdateViewWireframe(CCmdUI *pCmdUI);
	afx_msg void OnUpdateViewShading(CCmdUI *pCmdUI);
	afx_msg void OnUpdateViewWireAndShading(CCmdUI *pCmdUI);
	//@}

	// ツール
	//@{
	afx_msg void OnSnapCenter();
	afx_msg void OnSnapDialog();
	afx_msg void OnSnapEnd();
	afx_msg void OnSnapGrid();
	afx_msg void OnSnapKnot();
	afx_msg void OnSnapNear();
	afx_msg void OnSnapVertex();

	afx_msg void OnUpdateSnapCenter(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSnapDialog(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSnapEnd(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSnapKnot(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSnapNear(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSnapVertex(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSnapGrid(CCmdUI* pCmdUI);
	//@}

	// 評価
	//@{
	afx_msg void OnSysdKnotsNo();
	afx_msg void OnSysdCurvaOff();
	//afx_msg void OnSysdCpolyOff();

	afx_msg void OnUpdateSysdCurvaOff(CCmdUI* pCmdUI);
	afx_msg void OnUpdateSysdKnotsNo(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEvalSnapDisplayOn(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEvalSnapDisplayOff(CCmdUI* pCmdUI);
	afx_msg void OnUpdateEvalSnapGen(CCmdUI* pCmdUI);
	//afx_msg void OnUpdateSysdCpolyOff(CCmdUI* pCmdUI);
	afx_msg void OnUpdateTessWorld(CCmdUI* pCmdUI);
	//@}

	// レンダー
	//@{
	//afx_msg void OnRender();

	//afx_msg void OnUpdateRender(CCmdUI* pCmdUI);
	//@}

	afx_msg void OnCoordInput();
	afx_msg void OnUpdateCoordInput(CCmdUI* pCmdUI);
	afx_msg void OnConstraintDistance();
	afx_msg void OnConstraintAngle();

	afx_msg void OnF8Key();
	afx_msg void OnUpdateF8(CCmdUI *pCmdUI);
	afx_msg void OnEscapeKey();
	afx_msg void OnUpdateEscape(CCmdUI *pCmdUI);
	DECLARE_MESSAGE_MAP()

public:
	// Write message in the leftmost pane of status bar.
	void SetStatusMessage(const CString& str);
	void SetStatusMessage(UINT nIDS, ...);

// Member data
private:
	//List of all the MGObjects in this document.
	MGGroup m_group;

	std::stack<MGPickObjects> m_cobjects_stack;//Stack of current objects.
	std::stack<MGCommandBase*> m_command_tool_stack;
		//Command tool pointer stack. OpenGLWindow will invoke this stack's
		//top tool whenever an event which is defined in the class
		//MGCommandBase takes place in an OpenGLWindow.
		//Initially MGDefaultCommandTool will be set.

private:

	//The document's current group, which is the target group of fugenDoc that contains
	//objects to add or update.
	MGGroup* m_current_group; //

	mutable MGBox m_box;
	mutable fugenView* m_main_view;
	std::unique_ptr<IActionManager> m_undoManager;

	std::unique_ptr<MGIdleCommandTool> m_default_tool;//default command tool.
	MGPickObjects m_current_objects;

	MGPosition m_cursor;//Cursor position in world coordinate of the doc.
	MGPosition m_posRecent;//Last input position of this doc.

	MGGelPositions m_last_added;
		// The last gel positions that are added by CGelAddAction.

	MGCL::VIEWMODE m_viewMode;

	//Remove gelps from the current objects.
	void remove_current(const MGGelPositions& gelps);

	//Push and pop a command and the current_objects.
	void push_command(MGCommandBase* ntool);
	void pop_command();

	//Update the all selection hilighting of all the standard views.
	void UpdateSelectionViews();

	//The function's return value is true, if the tool termination is accepted.
	//False, if the termination is refused.
	void terminate_current_command();
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。

#endif // !defined(AFX_fugenDoc_H__8A2ED6BE_2FF4_4B96_B975_98B3658F2584__INCLUDED_)
