/*
First author tiritomato 2013.

mqsdx is distributed under the GNU Lesser General Public License 3.0(LGPLv3).

support blog (Japanese only)
http://d.hatena.ne.jp/tiri_tomato/
*/

#pragma once

#ifdef __cplusplus_cli
#include "MQ0x.hpp"

//! @namespace MQCLI
namespace MQCLI {

#pragma region Interface

	/*! @addtogroup インターフェース
		@{
	*/
	public interface class IActivate {
	public: virtual BOOL Activate(MQDocument doc, BOOL flag);
	};
	public interface class IEnumFileExt {
	public: virtual const char *EnumFileExt(int index);
	};
	public interface class IEnumFileType {
	public: virtual const char *EnumFileType(int index);
	};
	public interface class IExecute {
	public: virtual BOOL Execute(int index, MQDocument doc);
	};
	public interface class IExit {
	public: virtual void Exit();
	};
	public interface class IExportFile {
	public: virtual BOOL ExportFile(int index, const char *filename, MQDocument doc);
	};
	public interface class IImportFile {
	public: virtual BOOL ImportFile(int index, const char *filename, MQDocument doc);
	};
	public interface class IInitialize {
	public: virtual BOOL Initialize();
	};
	public interface class IIsActivated {
	public: virtual BOOL IsActivated(MQDocument doc);
	};
	public interface class IOnMinimize {
	public: virtual void OnMinimize(MQDocument doc, BOOL flag);
	};
	public interface class IOnReceiveUserMessage {
	public: virtual int OnReceiveUserMessage(MQDocument doc, DWORD src_product, DWORD src_id, const char *description, void *message);
	};
	public interface class IOnDraw {
	public: virtual void OnDraw(MQDocument doc, MQScene scene, int Width, int Height);
	};
	public interface class IOnNewDocument {
	public: virtual void OnNewDocument(MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param);
	};
	public interface class IOnEndDocument {
	public: virtual void OnEndDocument(MQDocument doc);
	};
	public interface class IOnSaveDocument {
	public: virtual void OnSaveDocument(MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param);
	};
	public interface class IOnUndo {
	public: virtual BOOL OnUndo(MQDocument doc, int undo_state);
	};
	public interface class IOnRedo {
	public: virtual BOOL OnRedo(MQDocument doc, int redo_state);
	};
	public interface class IOnUpdateUndo {
	public: virtual void OnUpdateUndo(MQDocument doc, int undo_state, int undo_size);
	};
	public interface class IOnObjectModified {
	public: virtual void OnObjectModified(MQDocument doc);
	};
	public interface class IOnObjectSelected {
	public: virtual void OnObjectSelected(MQDocument doc);
	};
	public interface class IOnUpdateObjectList {
	public: virtual void OnUpdateObjectList(MQDocument doc);
	};
	public interface class IOnMaterialModified {
	public: virtual void OnMaterialModified(MQDocument doc);
	};
	public interface class IOnUpdateMaterialList {
	public: virtual void OnUpdateMaterialList(MQDocument doc);
	};
	public interface class IOnUpdateScene {
	public: virtual void OnUpdateScene(MQDocument doc, MQScene scene);
	};
	public interface class IExecuteCallback {
	public: virtual bool ExecuteCallback(MQDocument doc, void *option);
	};
	public interface class IOnLeftButtonDown {
	public: virtual BOOL OnLeftButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnLeftButtonMove {
	public: virtual BOOL OnLeftButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnLeftButtonUp {
	public: virtual BOOL OnLeftButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnMiddleButtonDown {
	public: virtual BOOL OnMiddleButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnMiddleButtonMove {
	public: virtual BOOL OnMiddleButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnMiddleButtonUp {
	public: virtual BOOL OnMiddleButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnRightButtonDown {
	public: virtual BOOL OnRightButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnRightButtonMove {
	public: virtual BOOL OnRightButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnRightButtonUp {
	public: virtual BOOL OnRightButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnMouseMove {
	public: virtual BOOL OnMouseMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnMouseWheel {
	public: virtual BOOL OnMouseWheel(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnKeyDown {
	public: virtual BOOL OnKeyDown(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IOnKeyUp {
	public: virtual BOOL OnKeyUp(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
	};
	public interface class IBasePlugin {
	public: property MQBasePlugin* basePlugin { virtual MQBasePlugin* get(); }
	};
	//! @}
#pragma endregion

	//! @addtogroup MQCLIの一覧
	//! @{

	/*! @brief C\#のlockライクな機能を提供するクラス
	@code
	void func() {
		static System::Object^ locker_instance = gcnew System::Object();
		Lock lock(locker_instance); // << lock!
		// some process...
		// 明示的にlockを破壊する必要は無いはず。
	}
	@endcode*/
	public ref struct Lock {
	public:
		Lock( System::Object^ pObject );
		~Lock() { this->!Lock(); }
		!Lock();
	private:
		System::Object^ m_pObject;
	};

	//! @brief メタセコイアのメインウィンドウハンドル
	//! @details HWNDをCLI用にラップしたForm::Showの引数など親ウィンドウとして設定可能なクラスオブジェクト。
	public ref class MQWndHandle : System::Windows::Forms::IWin32Window
	{
	public:
		MQWndHandle() { m_Handle = (System::IntPtr)MQ_GetWindowHandle(); }
		virtual property System::IntPtr Handle { System::IntPtr get() { return m_Handle; } }
		static operator System::IntPtr ( MQWndHandle^ src ) {
			if ( src != nullptr ) return src->Handle;
			return System::IntPtr();
		}
	private:
		System::IntPtr m_Handle;
	};
	/// @}

	namespace Drawing {
		/// @addtogroup MQCLI-Drawing名前空間
		/// @{
		
		/*! @brief 三角形を描画します（枠線を描画します）。角は丸く描かれます。
		@details ソフトウェア処理のため、リアルタイムレンダリングには不向きです。
		@param points ３つの頂点座標
		@param colors ３つの頂点色
		@param border_radius 線の太さの半径。
		@param isExcludeInside trueに設定すると、枠線のうち三角形の内側と重複する部分が描画されません。*/
		void DrawTriangle( System::Drawing::Graphics^ g, array<System::Drawing::Point>^ points, array<System::Drawing::Color>^ colors, float border_radius, bool isExcludeInside = true );
		
		/*! @brief 三角形を描画します（枠線を描画します）。角は丸く描かれます。
		@details ソフトウェア処理のため、リアルタイムレンダリングには不向きです。
		@param points ３つの頂点座標
		@param colors ３つの頂点色
		@param border_radius 線の太さの半径。
		@param isExcludeInside trueに設定すると、枠線のうち三角形の内側と重複する部分が描画されません。*/
		void DrawTriangle( System::Drawing::Graphics^ g, array<System::Drawing::PointF>^ points, array<System::Drawing::Color>^ colors, float border_radius, bool isExcludeInside = true );
		
		/*! @brief 三角形を塗りつぶします。
		@details ソフトウェア処理のため、リアルタイムレンダリングには不向きです。
		@param points ３つの頂点座標
		@param colors ３つの頂点色	*/
		void FillTriangle( System::Drawing::Graphics^ g, array<System::Drawing::Point>^ points, array<System::Drawing::Color>^ colors );
		
		/*! @brief 三角形を塗りつぶします。
		@details ソフトウェア処理のため、リアルタイムレンダリングには不向きです。
		@param points ３つの頂点座標
		@param colors ３つの頂点色 */
		void FillTriangle( System::Drawing::Graphics^ g, array<System::Drawing::PointF>^ points, array<System::Drawing::Color>^ colors );
		/// @}
	}

	namespace Plugins {

		///@cond
		/*!	@brief 標準戻値取得のダミー実装とprotected解除のために公開するクラス */
		template <typename T> class StationPluginDefaultTemplate : public T {
		public:
			StationPluginDefaultTemplate(){}
			virtual void GetPlugInID(DWORD*,DWORD*) {  }
			virtual const char *GetPlugInName(void) { return NULL; }
			virtual const char *EnumString(void) { return NULL; }
			virtual BOOL Initialize() { return TRUE; }
			virtual BOOL Activate(MQDocument doc, BOOL flag) { return TRUE; }
			virtual void Exit() {}
			typedef typename T::DRAW_OBJECT_VIISIBILITY DRAW_OBJECT_VIISIBILITY;
			typedef typename T::SCENE_OPTION SCENE_OPTION;
			MQObject CreateDrawingObject(MQDocument doc, typename T::DRAW_OBJECT_VIISIBILITY visibility, BOOL instant) {
				return T::CreateDrawingObject(doc,visibility,instant);
			}
			MQMaterial CreateDrawingMaterial(MQDocument doc, int& index, BOOL instant) { return T::CreateDrawingMaterial(doc,index,instant); }
			void DeleteDrawingObject(MQDocument doc, MQObject obj) { T::DeleteDrawingObject(doc,obj); }
			void DeleteDrawingMaterial(MQDocument doc, MQMaterial mat) { T::DeleteDrawingMaterial(doc,mat); }
			int GetCurrentUndoState(MQDocument doc) { return T::GetCurrentUndoState(doc); }
			void GetSceneOption(MQScene scene, typename T::SCENE_OPTION& option) { T::GetSceneOption(scene,option); }
		};
		template <typename T> class CommandPluginDefaultTemplate : public StationPluginDefaultTemplate<T> {
		public:
			CommandPluginDefaultTemplate(){}
			void UpdateUndo() { T::UpdateUndo(); }
			void RedrawScene(MQScene scene) { T::RedrawScene(scene); }
			void RedrawAllScene() { T::RedrawAllScene(); }
			void GetEditOption(typename T::EDIT_OPTION& option) { T::GetEditOption(option); }
			MQPoint GetSnappedPos(MQScene scene, const MQPoint& p, typename T::SNAP_GRID_TYPE type) { return T::GetSnappedPos(scene,p,type); }
			BOOL HitTest(MQScene scene, POINT p, typename T::HIT_TEST_PARAM& param) { return T::HitTest(scene,p,param); }
			HCURSOR GetResourceCursor(typename T::MQCURSOR_TYPE cursor_type) { return T::GetResourceCursor(cursor_type); }
			void SetMouseCursor(HCURSOR cursor) { T::SetMouseCursor(cursor); }
			void SetStatusString(const char *str) { T::SetStatusString(str); }
		};
		//! @brief テンプレート特殊化を使ってPluginDefault<>の名前でStationPluginDefaultTemplate/CommandPluginDefaultTemplateの実装をスイッチする
		template <typename T> class PluginDefault;
		template <> class PluginDefault<MQStationPlugin> : public StationPluginDefaultTemplate<MQStationPlugin> { public: PluginDefault(){} };
		template <> class PluginDefault<MQCommandPlugin> : public CommandPluginDefaultTemplate<MQCommandPlugin> { public: PluginDefault(){} };
		//! @brief StationPluginDefaultTemplate/CommandPluginDefaultTemplateどちらでも良い場合にはとりあえず用のPluginDefault<MQCommandPlugin>をtypedef
		typedef PluginDefault<MQCommandPlugin> CommandPluginDefault;
		/// @endcond

		/// @cond
		public ref class PluginMainHolder {
		public:
			static bool Regist( IBasePlugin^ pluginBody );
			static property MQBasePlugin* pluginMain { MQBasePlugin* get(); }
			static BOOL Activate(MQDocument doc, BOOL flag);
			static bool ExecuteCallback(MQDocument doc, void *option);
			static void Exit();
			static BOOL Initialize();
			static BOOL IsActivated(MQDocument doc);
			static void OnMinimize(MQDocument doc, BOOL flag);
			static int OnReceiveUserMessage(MQDocument doc, DWORD src_product, DWORD src_id, const char *description, void *message);
			static void OnDraw(MQDocument doc, MQScene scene, int Width, int Height);
			static void OnNewDocument(MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param);
			static void OnEndDocument(MQDocument doc);
			static void OnSaveDocument(MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param);
			static BOOL OnUndo(MQDocument doc, int undo_state);
			static BOOL OnRedo(MQDocument doc, int redo_state);
			static void OnUpdateUndo(MQDocument doc, int undo_state, int undo_size);
			static void OnObjectModified(MQDocument doc);
			static void OnObjectSelected(MQDocument doc);
			static void OnUpdateObjectList(MQDocument doc);
			static void OnMaterialModified(MQDocument doc);
			static void OnUpdateMaterialList(MQDocument doc);
			static void OnUpdateScene(MQDocument doc, MQScene scene);
			static BOOL OnLeftButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnLeftButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnLeftButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnMiddleButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnMiddleButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnMiddleButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnRightButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnRightButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnRightButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnMouseMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnMouseWheel(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnKeyDown(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			static BOOL OnKeyUp(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
		private:
			static System::Object^ m_lock = gcnew System::Object();
			static IBasePlugin^ m_pluginMain = nullptr;
			static bool m_firstRequestFailedFlag = false;
		};
		///@endcond

		///@cond
		template <typename T> class Adapter : public T {
		public:
			typedef T PluginType;
			MQObject CreateDrawingObject( MQDocument doc, MQStationPlugin::DRAW_OBJECT_VIISIBILITY visibility, BOOL instant ) {
				return T::CreateDrawingObject(doc,visibility,instant);
			}
			MQMaterial CreateDrawingMaterial( MQDocument doc, int& index, BOOL instant ) {
				return T::CreateDrawingMaterial( doc, index, instant );
			}
			void DeleteDrawingObject(MQDocument doc, MQObject obj) { T::DeleteDrawingObject(doc,obj); }
			void DeleteDrawingMaterial(MQDocument doc, MQMaterial mat) { T::DeleteDrawingMaterial(doc,mat); }
			int GetCurrentUndoState(MQDocument doc) { return T::GetCurrentUndoState(doc); }
			void GetSceneOption(MQScene scene, MQStationPlugin::SCENE_OPTION& option) { T::GetSceneOption(scene,option); }
			void UpdateUndo() { T::UpdateUndo(); }
			void RedrawScene(MQScene scene){ T::RedrawScene(scene); }
			void RedrawAllScene() { T::RedrawAllScene(); }
			void GetEditOption(MQCommandPlugin::EDIT_OPTION& option) { T::GetEditOption(option); }
			MQPoint GetSnappedPos(MQScene scene, const MQPoint& p, MQCommandPlugin::SNAP_GRID_TYPE type) {
				return T::GetSnappedPos(scene,p,type);
			}
			BOOL HitTest(MQScene scene, POINT p, MQCommandPlugin::HIT_TEST_PARAM& param) { return T::HitTest(scene, p, param); }
			HCURSOR GetResourceCursor(MQCommandPlugin::MQCURSOR_TYPE cursor_type) { return T::GetResourceCursor(cursor_type); }
			void SetMouseCursor(HCURSOR cursor) { T::SetMouseCursor(cursor); }
			void SetStatusString(const char *str) { T::SetStatusString(str); }
		private:
			virtual BOOL Initialize() {
				return PluginMainHolder::Initialize();
			}
			virtual bool ExecuteCallback(MQDocument doc, void *option) {
				return PluginMainHolder::ExecuteCallback(doc, option);
			}
			virtual void Exit() {
				PluginMainHolder::Exit();
			}
			virtual BOOL Activate(MQDocument doc, BOOL flag) {
				return PluginMainHolder::Activate(doc, flag);
			}
			virtual BOOL IsActivated(MQDocument doc) {
				return PluginMainHolder::IsActivated(doc);
			}
			virtual void OnMinimize(MQDocument doc, BOOL flag) {
				PluginMainHolder::OnMinimize(doc, flag);
			}
			virtual int OnReceiveUserMessage(MQDocument doc, DWORD src_product, DWORD src_id, const char *description, void *message) {
				return PluginMainHolder::OnReceiveUserMessage(doc, src_product, src_id, description, message);
			}
			virtual void OnDraw(MQDocument doc, MQScene scene, int Width, int Height) {
				PluginMainHolder::OnDraw(doc, scene, Width, Height);
			}
			virtual void OnNewDocument(MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param) {
				PluginMainHolder::OnNewDocument(doc, filename, param);
			}
			virtual void OnEndDocument(MQDocument doc) {
				PluginMainHolder::OnEndDocument(doc);
			}
			virtual void OnSaveDocument(MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param) {
				PluginMainHolder::OnSaveDocument(doc, filename, param);
			}
			virtual BOOL OnUndo(MQDocument doc, int undo_state) {
				return PluginMainHolder::OnUndo(doc, undo_state);
			}
			virtual BOOL OnRedo(MQDocument doc, int redo_state) {
				return PluginMainHolder::OnRedo(doc, redo_state);
			}
			virtual void OnUpdateUndo(MQDocument doc, int undo_state, int undo_size) {
				PluginMainHolder::OnUpdateUndo(doc, undo_state, undo_size);
			}
			virtual void OnObjectModified(MQDocument doc) {
				PluginMainHolder::OnObjectModified(doc);
			}
			virtual void OnObjectSelected(MQDocument doc) {
				PluginMainHolder::OnObjectSelected(doc);
			}
			virtual void OnUpdateObjectList(MQDocument doc) {
				PluginMainHolder::OnUpdateObjectList(doc);
			}
			virtual void OnMaterialModified(MQDocument doc) {
				PluginMainHolder::OnMaterialModified(doc);
			}
			virtual void OnUpdateMaterialList(MQDocument doc) {
				PluginMainHolder::OnUpdateMaterialList(doc);
			}
			virtual void OnUpdateScene(MQDocument doc, MQScene scene) {
				PluginMainHolder::OnUpdateScene(doc, scene);
			}
		};
		///@endcond

		///@cond
		class StationPluginsUnmanaged : public MQ0x::PluginBase<Adapter<MQStationPlugin>> {
		public:
			StationPluginsUnmanaged( const char* productName, const char* pluginFullName, const char* displayName, const DWORD idProduct, const DWORD idPlugin )
				: PluginBase( productName, pluginFullName, displayName, idProduct, idPlugin )
			{
			}
		};

		class CommandPluginsUnmanaged : public MQ0x::PluginBase<Adapter<MQCommandPlugin>> {
		public:
			CommandPluginsUnmanaged( const char* productName, const char* pluginFullName, const char* displayName, const DWORD idProduct, const DWORD idPlugin )
				: PluginBase( productName, pluginFullName, displayName, idProduct, idPlugin )
			{
			}
			virtual BOOL IsActivated(MQDocument doc) {
				return PluginMainHolder::IsActivated(doc);
			}
			virtual BOOL OnLeftButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnLeftButtonDown(doc, scene, state);
			}
			virtual BOOL OnLeftButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnLeftButtonMove(doc, scene, state);
			}
			virtual BOOL OnLeftButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnLeftButtonUp(doc, scene, state);
			}
			virtual BOOL OnMiddleButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnMiddleButtonDown(doc, scene, state);
			}
			virtual BOOL OnMiddleButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnMiddleButtonMove(doc, scene, state);
			}
			virtual BOOL OnMiddleButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnMiddleButtonUp(doc, scene, state);
			}
			virtual BOOL OnRightButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnRightButtonDown(doc, scene, state);
			}
			virtual BOOL OnRightButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnRightButtonMove(doc, scene, state);
			}
			virtual BOOL OnRightButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnRightButtonUp(doc, scene, state);
			}
			virtual BOOL OnMouseMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnMouseMove(doc, scene, state);
			}
			virtual BOOL OnMouseWheel(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnMouseWheel(doc, scene, state);
			}
			virtual BOOL OnKeyDown(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnKeyDown(doc, scene, key, state);
			}
			virtual BOOL OnKeyUp(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state) {
				return PluginMainHolder::OnKeyUp(doc, scene, key, state);
			}
		};
		///@endcond

		/*!	@ingroup MQCLI-Plugins名前空間
		@brief StationPluginの基底実装クラス
		@details インターフェースおよび標準実装を公開するクラス。*/
		public ref class StationPluginsInterfacedBase abstract :
			IActivate,
			IExit,
			IInitialize,
			IExecuteCallback,
			IIsActivated,
			IOnMinimize,
			IOnReceiveUserMessage,
			IOnDraw,
			IOnNewDocument,
			IOnEndDocument,
			IOnSaveDocument,
			IOnUndo,
			IOnRedo,
			IOnUpdateUndo,
			IOnObjectModified,
			IOnObjectSelected,
			IOnUpdateObjectList,
			IOnMaterialModified,
			IOnUpdateMaterialList,
			IOnUpdateScene,
			IBasePlugin
		{
		public:
			virtual BOOL Activate(MQDocument doc, BOOL flag) { return TRUE; }
			virtual void Exit() {}
			virtual BOOL Initialize() { return TRUE; }
			virtual bool ExecuteCallback(MQDocument doc, void *option);
			virtual BOOL IsActivated(MQDocument doc);
			virtual void OnMinimize(MQDocument doc, BOOL flag);
			virtual int OnReceiveUserMessage(MQDocument doc, DWORD src_product, DWORD src_id, const char *description, void *message);
			virtual void OnDraw(MQDocument doc, MQScene scene, int Width, int Height);
			virtual void OnNewDocument(MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param);
			virtual void OnEndDocument(MQDocument doc);
			virtual void OnSaveDocument(MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param);
			virtual BOOL OnUndo(MQDocument doc, int undo_state);
			virtual BOOL OnRedo(MQDocument doc, int redo_state);
			virtual void OnUpdateUndo(MQDocument doc, int undo_state, int undo_size);
			virtual void OnObjectModified(MQDocument doc);
			virtual void OnObjectSelected(MQDocument doc);
			virtual void OnUpdateObjectList(MQDocument doc);
			virtual void OnMaterialModified(MQDocument doc);
			virtual void OnUpdateMaterialList(MQDocument doc);
			virtual void OnUpdateScene(MQDocument doc, MQScene scene);
			property MQBasePlugin* basePlugin { virtual MQBasePlugin* get() abstract; }
			int SendUserMessage(MQDocument doc, DWORD target_product, DWORD target_id, const char *description, void *param);
			void WindowClose();
			void BeginCallback(void *option);
			void GetPlugInID(DWORD *Product, DWORD *ID);
			property DWORD ProductID { DWORD get(); }
			property DWORD PluginID { DWORD get(); }
			const char* GetPlugInName();
			int GetPlugInType();
			const char* EnumString();
		protected:
			property MQStationPlugin* stationPlugin { virtual MQStationPlugin* get() abstract; }
		};

		/*!	@ingroup MQCLI-Plugins名前空間
		@brief CommandPluginの基底実装クラス
		@details インターフェースおよび標準実装を公開するクラス。*/
		public ref class CommandPluginsInterfacedBase abstract : StationPluginsInterfacedBase,
											IOnLeftButtonDown,
											IOnLeftButtonMove,
											IOnLeftButtonUp,
											IOnMiddleButtonDown,
											IOnMiddleButtonMove,
											IOnMiddleButtonUp,
											IOnRightButtonDown,
											IOnRightButtonMove,
											IOnRightButtonUp,
											IOnMouseMove,
											IOnMouseWheel,
											IOnKeyDown,
											IOnKeyUp
		{
		public:
			virtual BOOL OnLeftButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnLeftButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnLeftButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnMiddleButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnMiddleButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnMiddleButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnRightButtonDown(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnRightButtonMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnRightButtonUp(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnMouseMove(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnMouseWheel(MQDocument doc, MQScene scene, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnKeyDown(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
			virtual BOOL OnKeyUp(MQDocument doc, MQScene scene, int key, MQCommandPlugin::MOUSE_BUTTON_STATE& state);
		};

		/*! @ingroup MQCLIの一覧
		@brief StationPluginおよびCommandPluginの基底実装テンプレート
		@details StationPluginTemplateはT_MANAGEDBASEに指定したマネージド型を継承するマネージドクラスです。
		@par @details StationPluginはStationPluginsInterfacedBaseを継承し、そのまま機能をStationPlugin型として公開します。
		CommandPluginはCommandPluginsInterfacedBaseを継承し、さらにStationPluginTemplateだけでは足りないいくつかの機能を追加してCommandPlugin型として公開します。
		@par @details T_UNMANAGEDについては、通常プログラマは意識する必要はありません。
		StationPluginやCommandPluginの定義時に、それぞれ対応するMQStationPluginやMQCommandPluginを専用にカスタムしたアンマネージド型を自動的に割り当てます
		（またこのアンマネージド型が暗黙にInitialize()処理と連携してマネージドクラスへ処理をパイプしたり、もともとのprotect属性メンバをマネージド側へ公開する仕組みになっています）。*/
		template <class T_MANAGEDBASE, class T_UNMANAGED>
		public ref class StationPluginTemplate : T_MANAGEDBASE {
		public:
			StationPluginTemplate( const char* productName, const char* pluginFullName, const char* displayName ) :
				m_pluginBody( new T_UNMANAGED( productName, pluginFullName, displayName,
					MQ0x::CollectionHashCode<DWORD>(productName), MQ0x::CollectionHashCode<DWORD>(pluginFullName) ) ) {}
			StationPluginTemplate( const char* productName, const char* pluginFullName, const char* displayName, const DWORD idProduct, const DWORD idPlugin ) :
				m_pluginBody( new T_UNMANAGED( productName, pluginFullName, displayName, idProduct, idPlugin ) ) {}
		
			property MQBasePlugin* basePlugin { virtual MQBasePlugin* get() override { return static_cast<MQBasePlugin*>(m_pluginBody); } }
			/*!	@brief Settingハンドルの取得
			@details Settingハンドルを取得してMQ0x::Settingクラスを生成します。
			MQ0x::SettingはMQSettingクラスの代行です。
			コンストラクト時にOpen、デストラクト時にCloseを自動的に実行します。
			@code
			StationPlugin^ plugin = gcnew StationPlugin("tiritomato", "SamplePlugin Copyright(C) 2013, tiritomato.", "SamplePlugin");
			{
				SettingProxy setting( plugin.Setting );
				setting.Load(...);
				setting.Save(...);
				setting.Close(); // このClose()はSettingProxyのデストラクタに任せる事も出来ます。
			}
			@endcode */
			property MQ0x::SettingProxy::Handle Setting {
				MQ0x::SettingProxy::Handle get() {
					if ( m_pluginBody == NULL ) return NULL;
					return m_pluginBody->BasePlugin();
				}
			}
		protected:
			typedef PluginDefault<typename T_UNMANAGED::PluginType> PluginDefault;
			typedef typename PluginDefault::DRAW_OBJECT_VIISIBILITY DRAW_OBJECT_VIISIBILITY;
			typedef typename PluginDefault::SCENE_OPTION SCENE_OPTION;
			property MQStationPlugin* stationPlugin { virtual MQStationPlugin* get() override { return m_pluginBody; } }
			property T_UNMANAGED* pluginBody { virtual T_UNMANAGED* get() { return m_pluginBody; } }
			MQObject CreateDrawingObject( MQDocument doc, DRAW_OBJECT_VIISIBILITY visibility, BOOL instant ) {
				if ( m_pluginBody != NULL ) return m_pluginBody->CreateDrawingObject(doc,visibility,instant);
				return PluginDefault().CreateDrawingObject(doc,visibility,instant);
			}
			MQObject CreateDrawingObject( MQDocument doc, DRAW_OBJECT_VIISIBILITY visibility ) {
				return CreateDrawingObject(doc,visibility,TRUE);
			}
			MQMaterial CreateDrawingMaterial( MQDocument doc, int& index, BOOL instant ) {
				if ( m_pluginBody != NULL ) return m_pluginBody->CreateDrawingMaterial(doc,index,instant);
				return PluginDefault().CreateDrawingMaterial(doc,index,instant);
			}
			MQMaterial CreateDrawingMaterial( MQDocument doc, int& index ) { return CreateDrawingMaterial(doc,index,TRUE); }
			void DeleteDrawingObject(MQDocument doc, MQObject obj) {
				if ( m_pluginBody != NULL ) m_pluginBody->DeleteDrawingObject(doc,obj);
				PluginDefault().DeleteDrawingMaterial(doc,mat);
			}
			void DeleteDrawingMaterial(MQDocument doc, MQMaterial mat) {
				if ( m_pluginBody != NULL ) m_pluginBody->DeleteDrawingMaterial(doc,mat);
				PluginDefault().DeleteDrawingMaterial(doc,mat);
			}
			int GetCurrentUndoState(MQDocument doc) {
				if ( m_pluginBody != NULL ) return m_pluginBody->GetCurrentUndoState(doc);
				return PluginDefault().GetCurrentUndoState(doc);
			}
			void GetSceneOption(MQScene scene, SCENE_OPTION& option) { if ( m_pluginBody != NULL ) m_pluginBody->GetSceneOption(scene,option); }
		private:
			T_UNMANAGED *m_pluginBody;
		};

		public ref class StationPlugin : StationPluginTemplate<StationPluginsInterfacedBase,StationPluginsUnmanaged> {
		public:
			StationPlugin( const char* productName, const char* pluginFullName, const char* displayName ) :
				StationPluginTemplate( productName, pluginFullName, displayName ) {}
			StationPlugin( const char* productName, const char* pluginFullName, const char* displayName, const DWORD idProduct, const DWORD idPlugin ) :
				StationPluginTemplate( productName, pluginFullName, displayName, idProduct, idPlugin ) {}
		};

		public ref class CommandPlugin : StationPluginTemplate<CommandPluginsInterfacedBase,CommandPluginsUnmanaged> {
		public:
			CommandPlugin( const char* productName, const char* pluginFullName, const char* displayName ) :
				StationPluginTemplate( productName, pluginFullName, displayName ) {}
			CommandPlugin( const char* productName, const char* pluginFullName, const char* displayName, const DWORD idProduct, const DWORD idPlugin ) :
				StationPluginTemplate( productName, pluginFullName, displayName, idProduct, idPlugin ) {}
		protected:
			void UpdateUndo() {
				if ( pluginBody != NULL ) pluginBody->UpdateUndo();
				PluginDefault().UpdateUndo();
			}
			void RedrawScene(MQScene scene){
				if ( pluginBody != NULL ) pluginBody->RedrawScene(scene);
				PluginDefault().RedrawScene(scene);
			}
			void RedrawAllScene() {
				if ( pluginBody != NULL ) pluginBody->RedrawAllScene();
				PluginDefault().RedrawAllScene();
			}
			void GetEditOption(MQCommandPlugin::EDIT_OPTION& option) {
				if ( pluginBody != NULL ) pluginBody->GetEditOption(option);
				PluginDefault().GetEditOption(option);
			}
			MQPoint GetSnappedPos(MQScene scene, const MQPoint& p, MQCommandPlugin::SNAP_GRID_TYPE type) {
				if ( pluginBody != NULL ) return pluginBody->GetSnappedPos(scene,p,type);
				return PluginDefault().GetSnappedPos(scene,p,type);
			}
			BOOL HitTest(MQScene scene, POINT p, MQCommandPlugin::HIT_TEST_PARAM& param) {
				if ( pluginBody != NULL ) return pluginBody->HitTest(scene, p, param);
				return PluginDefault().HitTest(scene,p,param);
			}
			HCURSOR GetResourceCursor(MQCommandPlugin::MQCURSOR_TYPE cursor_type) {
				if ( pluginBody != NULL ) return pluginBody->GetResourceCursor(cursor_type);
				return PluginDefault().GetResourceCursor(cursor_type);
			}
			void SetMouseCursor(HCURSOR cursor) {
				if ( pluginBody != NULL ) pluginBody->SetMouseCursor(cursor);
				PluginDefault().SetMouseCursor(cursor);
			}
			void SetStatusString(const char *str) {
				if ( pluginBody != NULL ) pluginBody->SetStatusString(str);
				PluginDefault().SetStatusString(str);
			}
		};

	}
	using namespace Plugins;

	/*!	@addtogroup MQCLIの一覧
		@{
	*/
	/// @brief MQCLIライブラリの初期化
	bool Initialize( StationPlugin^ pluginBody );
	bool Initialize( CommandPlugin^ pluginBody );
	/// @}

}

#endif