// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU General Public License(GPL).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

#pragma once

namespace UVTexIntegra {
	namespace CustomControls {
	
		using namespace OpenTK::Graphics;
		using namespace OpenTK::Graphics::OpenGL;

		// pre define
		struct UVTransformedBuffer;
		ref class MainEditForm;

		//! @addtogroup UVTexIntegra-CustomControls名前空間
		//! @{

		public ref class Plugin : MQCLI::CommandPlugin {
		public:
			Plugin();
			virtual BOOL Activate(MQDocument doc, BOOL flag) override;
			virtual void Exit() override;
			virtual BOOL IsActivated(MQDocument doc) override;
			virtual void OnEndDocument(MQDocument doc) override;
			virtual void OnMaterialModified(MQDocument doc) override;
			virtual void OnNewDocument(MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param) override;
			virtual void OnUpdateMaterialList(MQDocument doc) override;
			virtual void OnUpdateObjectList(MQDocument doc) override;
			virtual void OnSaveDocument(MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param) override;
			virtual bool OnException(System::Exception^ ex);
		private:
			MainEditForm^ m_dlg;
		};

		//! @brief MainEditForm_Baseを継承し、メイン画面の実装を提供するクラスです。
		public ref class MainEditForm : MainEditForm_Base {
		private:
			//! @brief MainEditFormを元に、IConfigインターフェース（プラグイン設定情報および文書ごとのデータフォーマットIDocumentFormatをセットで公開するインターフェース）に変換するアダプタークラスです。
			ref class ConfigAdapter : IConfig {
			public:
				//! @brief テクスチャ焼き込み時の下地色を取得します。
				virtual property System::Int32 BakeTextureBaseColor
				{
					System::Int32 get() { return m_dlg->BakeTextureBaseColor; }
					void set(System::Int32 value) { m_dlg->BakeTextureBaseColor = value; }
				}
		
				//! @brief テクスチャ焼き込み時の輪郭の太さを取得します。
				virtual property float BakeTextureBorder
				{
					float get() { return m_dlg->BakeTextureBorder; }
					void set(float value) { m_dlg->BakeTextureBorder = value; }
				}
		
				//! @brief テクスチャ焼き込み時のサイズをピクセル単位で取得します。
				virtual property System::Drawing::Size BakeTextureImageSize
				{
					System::Drawing::Size get() { return System::Drawing::Size(m_dlg->BakeTextureW, m_dlg->BakeTextureH); }
					void set(System::Drawing::Size value)
					{
						m_dlg->BakeTextureW = value.Width;
						m_dlg->BakeTextureH = value.Height;
					}
				}

				//! @brief テクスチャ焼き込み時のファイルパスを取得,設定します。
				virtual property System::String^ BakeTextureLastFilePath {
					System::String^ get() { return m_dlg->BakeTextureLastFilePath; }
					void set(System::String^ value) { m_dlg->BakeTextureLastFilePath = value; }
				}
		
				//! @brief カンバスモードを取得します。
				virtual property UVTexIntegra::CanvasMode CanvasMode
				{
					UVTexIntegra::CanvasMode get()
					{
						if ( m_dlg->rdbDisplayVColorPreview->Checked ) return UVTexIntegra::CanvasMode::VertexColorPreview;
						return UVTexIntegra::CanvasMode::Normal;
					}
				}
		
				//! @brief 読み取り用のドキュメント情報を取得します。
				//! このデータはConfigAdapter生成時にクローニングされるディープコピーデータで、このドキュメント情報を書き換えてもオリジナルには反映されません。
				virtual property IDocumentFormat^ Document { IDocumentFormat^ get() { return m_doc; } }

				//! @brief メインウィンドウサイズを取得します
				virtual property System::Drawing::Size FormSize { System::Drawing::Size get() { return m_dlg->Size; } }

				//! @brief グリッド解像度を取得します。
				virtual property int GridResolution { int get() { return m_dlg->GridResolution; } }

				//! @brief テクスチャ設定ダイアログのサイズを設定/取得します。
				virtual property System::Drawing::Size TextureSetFormSize
				{
					System::Drawing::Size get() { return m_dlg->TexSetFormSize; }
					void set(System::Drawing::Size value) { m_dlg->TexSetFormSize = value; }
				}

				ConfigAdapter( MainEditForm^ dlg );

			private:
				MainEditForm^ m_dlg;
				IDocumentFormat^ m_doc;
			};

		public:
			
			//! @brief 文書ごとの情報を読み書き可能に保持するクラスです。また、読み取り用であるIDocumentFormatｍも実装します。
			ref class Document : IDocumentFormat {
			public:
				virtual property bool IsCloneTrans {
					bool get() { return m_payload->IsCloneTrans; }
					private: void set(bool value) sealed { m_payload->IsCloneTrans = value; }
				}
				virtual property bool IsRecursiveTrans {
					bool get() { return m_payload->IsRecursiveTrans; }
					private: void set(bool value) sealed { m_payload->IsRecursiveTrans = value; }
				}
				virtual property bool IsIntegrateTexture {
					bool get() { return m_payload->IsIntegrateTexture; }
					private: void set(bool value) sealed { m_payload->IsIntegrateTexture = value; }
				}
				virtual property bool IsClrTextureOnIntegrate {
					bool get() { return m_payload->IsClrTextureOnIntegrate; }
					private: void set(bool value) sealed { m_payload->IsClrTextureOnIntegrate = value; }
				}
				virtual property bool IsIntegrateAlpha {
					bool get() { return m_payload->IsIntegrateAlpha; }
					private: void set(bool value) sealed { m_payload->IsIntegrateAlpha = value; }
				}
				virtual property bool IsClrAlphaOnIntegrate {
					bool get() { return m_payload->IsClrAlphaOnIntegrate; }
					private: void set(bool value) sealed { m_payload->IsClrAlphaOnIntegrate = value; }
				}
				virtual property bool IsIntegrateBump {
					bool get() { return m_payload->IsIntegrateBump; }
					private: void set(bool value) sealed { m_payload->IsIntegrateBump = value; }
				}
				virtual property bool IsClrBumpOnIntegrate {
					bool get() { return m_payload->IsClrBumpOnIntegrate; }
					private: void set(bool value) sealed { m_payload->IsClrBumpOnIntegrate = value; }
				}
				virtual property System::Int32 TextureClearColor {
					System::Int32 get() { return m_payload->TextureClearColor; }
					private: void set(System::Int32 value) sealed { m_payload->TextureClearColor = value; }
				}
				virtual property System::Int32 AlphaClearColor {
					System::Int32 get() { return m_payload->AlphaClearColor; }
					private: void set(System::Int32 value) sealed { m_payload->AlphaClearColor = value; }
				}
				virtual property System::Int32 BumpClearColor {
					System::Int32 get() { return m_payload->BumpClearColor; }
					private: void set(System::Int32 value) sealed { m_payload->BumpClearColor = value; }
				}
				//! @brief 現在コンボボックスからの選択を受け付けているのか、メタセコ側の選択情報を反映すべきかを取得,設定します。
				bool IsComboboxIndexSelecting;
				property bool IsEmpty { bool get(){ return m_payload->EditDataItems->Count < 1; } }
				virtual property ::MQDocument MQDocument {
					::MQDocument get() { return m_payload->MQDocument; }
					void set( ::MQDocument value ) { m_payload->MQDocument = value; }
				}
				virtual property System::Collections::ObjectModel::ReadOnlyCollection<EditData^>^ EditDataItems {
					System::Collections::ObjectModel::ReadOnlyCollection<EditData^>^ get() { return m_payload->ReadonlyEditDataItems; }
				}

				/// @brief デフォルトコンストラクタ
				Document() :
				m_payload(gcnew DocumentFormat()),
				m_refreshingLock(DotNetEx::CounterKey::UsingKey()),
				IsComboboxIndexSelecting(false)
				{
				}

				void BakeTexturePaint(MainEditForm^ dlg, System::Drawing::Graphics^ g, System::Drawing::Size size);

				//! @brief マテリアル設定エラーを検地します。
				//! @return エラーがある時trueを返します。エラーとは以下の条件のいずれかを満たす設定が見つかる時です。
				//! - ToMaterialIDが不正（現在のMQDocumentから見つけることが出来ない）
				//! - MaterialID == ToMaterialIDが成立する時
				bool IsIncludeNotChangeMaterialSetting();
				
				//! @brief ユニークIDを指定してEditDataアイテムを検索します。見つからない場合はnullが返ります。
				virtual EditData^ ItemFromID( const UINT UniqueID ) { return m_payload->ItemFromID(UniqueID); }
				//! @brief マテリアル名を指定してEditDataアイテムを検索します。見つからない場合はnullが返ります。
				virtual EditData^ ItemFromName( System::String^ matName ) { return m_payload->ItemFromName(matName); }
				//! @brief インデックスを指定してEditDataアイテムを検索します。見つからない場合はnullが返ります。
				virtual EditData^ ItemFromIndex( const int index ) { return m_payload->ItemFromIndex(index); }
				//! @brief ユニークIDを指定して、EditDataアイテムのインデックスを検索します。見つからない場合は無効な負のインデックスが返ります。
				virtual int ItemIndexFromID( const UINT UniqueID ) { return m_payload->ItemIndexFromID(UniqueID); }
				//! @brief 保有するEditData配列の中で、なおかつMQDocumentで現在マテリアル選択されているEditDataのコレクションを返します。
				//! @details MQDocumentがNULLの時は空の配列が返ります。先頭のカレントマテリアル以外のマテリアルの順序は不定です。
				virtual EditData::SelectedCollection^ ItemsOnSelected() { return ItemsOnSelected(-1); }
				//! @brief 保有するEditData配列の中で、なおかつMQDocumentで現在マテリアル選択されているEditDataのコレクションを返します。
				//! @details MQDocumentがNULLの時は空の配列が返ります。先頭のカレントマテリアル以外のマテリアルの順序は不定です。
				//! @param [in] index -1などItemFromIndex()に渡してEditDataが取得不能なインデックスを指定すると何も起こりませんが、ItemFromIndex()に渡してEditDataが取得可能なインデックスを指定すると、カレントマテリアルよりも先にそのEditDataが挿入されます。
				//! このとき、そのEditDataマテリアルが現在選択されているかどうかは判定されません。以後は通常通りカレントマテリアルが並んでからその他の複数選択中のマテリアルが続きます。
				virtual EditData::SelectedCollection^ ItemsOnSelected(const int index) { return m_payload->ItemsOnSelected(index); }
				
				//! @brief カスタムベイク処理を開始します。
				//! @details カスタムベイクはモーダレスに別ダイアログを呼び出す処理になっており、
				//! このメソッドの呼び出し中はメタセコイアおよびメインダイアログが、モーダレス処理の親ウィンドウとして操作できなくなります。
				void OnCustomBake(MainEditForm^ dlg);
				
				//!	@brief ダイアログに更新があった時に呼び出して、内部データを更新します。
				void OnInputChanged(MainEditForm^ dlg);
				//!	@brief mqoが読み込まれたときに、内部データを一新する処理。またRefreshDialog()も実行されます。
				void OnMQOLoaded(MainEditForm^ dlg, MQXmlElement elem);
				//!	@brief mqoが変更されたときなど、内部データとMQOの整合を取ります（可能な限り内部データを残しつつ現在のMQOデータで再構築する）。
				void OnMQOModified(MainEditForm^ dlg, bool isCreateOnEmptyList);
				//!	@brief mqo保存時の処理	を行います
				void OnMQOSave(MainEditForm^ dlg, MQXmlElement elem);
				void Paint(MainEditForm^ dlg, System::Drawing::Graphics^ g, System::Drawing::Size size, bool isDraging, bool isBorderHighLight);
				//! @brief 実際のデータのディープコピークローンを取得します
				DocumentFormat^ PayloadClone() { return m_payload->Clone(); }
				// OnMQOLoaded		内部データを強制的に一新 ＞	内部データとMQOの整合 ＞	内部データを画面に反映
				// OnMQOModified							内部データとMQOの整合 ＞	内部データを画面に反映
				// RefreshDialog													内部データを画面に反映
				//!	@brief 現在の内部データを画面に反映する処理です。
				//! @param materialIndex マテリアルを強制的に選択する場合、0以上のインデックスを指定してください。マイナスや無効なインデックスが指定されると、このパラメータは無視されます。
				//! @details マテリアル選択がなかった時、呼び出し時点のdlgのマテリアル選択が維持されます。
				//! マテリアルが未選択状態で、マテリアルが一つ以上あれば最初のマテリアルが選択されます。
				void RefreshDialog(MainEditForm^ dlg, int materialIndex);
				
				//! @brief 選択中のマテリアル情報を初期状態にクリアします
				void SelectedMaterialsReset(MainEditForm^ dlg);

				//! @brief 選択中のマテリアル情報の変更先マテリアルを-1(未設定状態)にクリアします
				void SelectedMaterialsToMatClear(MainEditForm^ dlg);

				//! @brief 変換を試行します
				void TryTrans(MainEditForm^ dlg);

			private:
				DotNetEx::CounterKey^ m_refreshingLock;
				DocumentFormat^ m_payload;
				void PaintDefault(MainEditForm^ dlg, System::Drawing::Graphics^ g, bool isDraging, bool isBorderHighLight);
				int RecursiveTrans(const MQObject obj, const int depth, std::vector<char>* const buf);
			};

			static property System::Collections::ObjectModel::ReadOnlyCollection<unsigned int>^ GridResolutions {
				System::Collections::ObjectModel::ReadOnlyCollection<unsigned int>^ get();
			}
			static property System::Drawing::Point EmptyPosition { System::Drawing::Point get(){ return System::Drawing::Point(-1,-1); } }
			static property System::Drawing::RectangleF EmptyRectangleF { System::Drawing::RectangleF get(){ return System::Drawing::RectangleF(float::NaN,float::NaN,float::NaN,float::NaN); } }
			static bool IsEmptyRectangleF( System::Drawing::RectangleF rc ) { return float::IsNaN(rc.Left) || float::IsNaN(rc.Top) || float::IsNaN(rc.Width) || float::IsNaN(rc.Height); }

			MainEditForm(Plugin^ plugin);
			~MainEditForm() { this->!MainEditForm(); }
			!MainEditForm();

			BOOL Activate(MQDocument doc, BOOL value);
			BOOL Activate(BOOL value) { return Activate( m_docData->MQDocument, value ); }
			void Exit();
			BOOL IsActivated() { MQCLI::Lock lock( m_lock ); if ( m_isFirstActivate ) return FALSE; return Form::Visible; }

			void OnEndDocument() {
				m_docData->MQDocument = NULL;
				m_docData->OnMQOLoaded(this,NULL);
			}

			void OnMaterialModified(MQDocument doc) {
				m_docData->MQDocument = doc;
				m_docData->OnMQOModified(this, false);
			}

			void OnNewDocument( MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param );

			void OnSaveDocument( MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param ) {
				m_docData->MQDocument = doc;
				if ( param.elem != NULL ) m_docData->OnMQOSave( this, param.elem );
			}

		protected:
			virtual System::Void OnFormClosing(System::Windows::Forms::FormClosingEventArgs^ e) override;

		private:
			static System::Collections::ObjectModel::ReadOnlyCollection<unsigned int>^ m_gridResolutions;
			Plugin^ m_ownerPlugin;
			bool m_isGLRepaintRequested;
			bool m_isFirstActivate;
			bool m_isMouseRangeSelecting;
			bool m_isMouseCanceled;
		
			System::Object^ m_drawLock;
			System::Object^ m_lock;

			Document^ m_docData;
			System::Drawing::Point m_mousePosition;
			System::Drawing::Point m_mouseDragStartPosition;

			System::Int32 BakeTextureBaseColor;
			float BakeTextureBorder;
			System::String^ BakeTextureLastFilePath;
			int BakeTextureW;
			int BakeTextureH;

			/// @brief 現在選択中のグリッド解像度
			/// @return グリッド解像度。コンボボックスからの数値取得に失敗したら0。
			property int GridResolution { int get(); }
			/// @brief 現在のマウス座標（グリッド単位）
			/// @return マウス座標。コンボボックスからの解像度取得失敗またはカンバス範囲外にカーソルがある場合はEmptyPosition。
			property System::Drawing::Point mousePosition { System::Drawing::Point get(); }
			/// @brief テクスチャ設定画面のサイズを格納するプロパティ
			System::Drawing::Size TexSetFormSize; // privatable
			/// @brief 指定されたカンバス座標の矩形領域を取得
			/// @return 座標の矩形。カンバス座標がカンバス上にない場合、コンボボックスから解像度取得に失敗した場合EmptyRectangle。
			System::Drawing::RectangleF GetGridCursorRectangle( System::Drawing::Point pt );
			/// @brief 指定されたカンバス座標を囲う矩形領域を取得
			/// @return 座標の矩形。どちらかのカンバス座標がカンバス上にない場合、コンボボックスから解像度取得に失敗した場合EmptyRectangle。
			System::Drawing::RectangleF GetGridCursorRectangle( System::Drawing::Point ptA, System::Drawing::Point ptB );

			System::Int32 m_glShaderProgram;

			UVTransformedBuffer* m_pUVBuffer;

			System::String^ m_customBakeScriptDirectory;
			int m_customBakeScriptFileFilterIndex;
			int m_buildResultWinX, m_buildResultWinY;

			System::Void pnlPaint_CallInvalidate(System::Object^, System::EventArgs^) { pnlGL->Invalidate(); }
			System::Void btnPilotSwitch_Click			( System::Object^, System::EventArgs^ );
			System::Void btnMatClr_Click				( System::Object^, System::EventArgs^ );
			System::Void btnToMatClr_Click				( System::Object^, System::EventArgs^ );
			System::Void btnTrans_Click					( System::Object^, System::EventArgs^ );
			System::Void btnBakeVColor_Click			( System::Object^, System::EventArgs^ );
			System::Void btnCustomBake_Click			( System::Object^, System::EventArgs^ );
			System::Void cmbSelMat_SelectedIndexChanged	( System::Object^, System::EventArgs^ );
		
			System::Void pnlGL_GLPrePaintOnce(System::Object^, System::ComponentModel::CancelEventArgs^);
			System::Void pnlGL_GLPaint(System::Object^, System::Windows::Forms::PaintEventArgs^);

			System::Void txt_KeyUp( System::Object^, System::Windows::Forms::KeyEventArgs^ );
			System::Void txt_FloatValidating( System::Object^, System::ComponentModel::CancelEventArgs^ );
			System::Void txt_AbsIntValidating( System::Object^, System::ComponentModel::CancelEventArgs^ );
			System::Void txt_ColorValidating( System::Object^, System::ComponentModel::CancelEventArgs^ );
			System::Void chkSelMatTrans_CheckedChanged( System::Object^, System::EventArgs^ );
			System::Void pluginData_InputChanged( System::Object^, System::EventArgs^ );

			System::Void cmbResolution_SelectedIndexChanged( Object^ sender, System::EventArgs^ e );
			System::Void pnlGL_GLMouseMove(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e);
		};

		//! @}
	}
}