/*
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 "mqsdx/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 サポートクラス
	//! @{
	/*! @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 class Lock {
	public:
		Lock( System::Object^ pObject );
		~Lock();
	private:
		System::Object^ m_pObject;
	};

	/*! @brief メタセコイアのメインウィンドウハンドル

		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 String {
		/// @addtogroup サポートクラス
		/// @{

		/*!	@brief 文字列を繰り返す文字列を生成する
			@param src 繰り返す文字列
			@param ct 繰り返す回数
			@return srcがnullptrまたはctがマイナスの時nullptr。ctがゼロの時System::String::Empty。それ以外であれば繰り返した文字列。
		*/
		System::String^ Repeat(System::String^ src, int ct);

		/*!	@brief インデントするStringBuilder
		*/
		public ref class IndentBuilder {
		public:
			IndentBuilder( System::String^ IndentUnit ) {
				m_Builder = gcnew System::Text::StringBuilder();
				this->IndentUnit = IndentUnit;
			}
			property System::String^ IndentUnit {
				System::String^ get() { return m_IndentUnit; }
				void set( System::String^ value ) { m_IndentUnit = value!=nullptr?value:System::String::Empty; }
			}
			void Append( System::String^ str, int indentDepth ) {
				if ( str == nullptr ) return;
				System::String^ indent = Repeat(IndentUnit, System::Math::Max(indentDepth,0));
				m_Builder->Append( indent + str->Replace(System::Environment::NewLine,System::Environment::NewLine + indent) );
			}
			void Append( System::String^ str ) { Append(str, 0); }
			void AppendLine( System::String^ str, int indentDepth ) { Append( str, indentDepth ); m_Builder->AppendLine(); }
			void AppendLine( System::String^ str ) { AppendLine(str ,0); }
			virtual System::String^ ToString() override { return m_Builder->ToString(); }
		private:
			System::String^ m_IndentUnit;
			System::Text::StringBuilder^ m_Builder;
		};

		///	@brief 例外データを文字列に変換します。InnerExceptionの入れ子構造をインデント整形で表現しています。
		System::String^ FromException(System::Exception^ ex, System::String^ indentUnit, int indentDepth );
		///	@brief 例外データを文字列に変換します。InnerExceptionの入れ子構造をインデント整形で表現しています。
		inline System::String^ FromException(System::Exception^ ex, System::String^ indentUnit ) {
			return FromException(ex,indentUnit,0);
		}
		///	@brief 例外データを文字列に変換します。InnerExceptionの入れ子構造をインデント整形で表現しています。
		inline System::String^ FromException(System::Exception^ ex) { return FromException( ex, "\t" ); }

		/// @}
	}

	///@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 StationPlugins
		@brief StationPluginの基底実装クラス

		インターフェースおよび標準実装を公開するクラス。
	*/
	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 CommandPlugins
		@brief CommandPluginの基底実装クラス

		インターフェースおよび標準実装を公開するクラス。
	*/
	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の基底実装テンプレートです。

StationPluginTemplateはT_MANAGEDBASEに指定したマネージド型を継承するマネージドクラスです。

@par

StationPluginはStationPluginsInterfacedBaseを継承し、そのまま機能をStationPlugin型として公開します。
CommandPluginはCommandPluginsInterfacedBaseを継承し、さらにStationPluginTemplateだけでは足りないいくつかの機能を追加してCommandPlugin型として公開します。

@par

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ハンドルの取得

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);
		}
	};

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

}

#endif