// == 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 comment(lib, "user32.lib")
#include "CustomControls.hpp"

using namespace System;
using namespace System::Windows::Forms;
using namespace OpenTK::Graphics;
using namespace OpenTK::Graphics::OpenGL;

namespace UVTexIntegra {
	namespace CustomControls {

		Plugin::Plugin()
			: MQCLI::CommandPlugin(MAKER_NAME, PLUGIN_NAME, PLUGIN_STRING), m_dlg(nullptr)
		{
			m_dlg = gcnew MainEditForm(this);
			m_dlg->Text = gcnew System::String(GetPlugInName());
		}

		BOOL Plugin::Activate(MQDocument doc, BOOL flag) {
			if ( m_dlg == nullptr ) return flag;
			return m_dlg->Activate(doc,flag);
		}

		void Plugin::Exit() {
			if ( m_dlg != nullptr ) m_dlg->Exit();
			m_dlg = nullptr;
		}

		BOOL Plugin::IsActivated(MQDocument doc) {
			if ( m_dlg != nullptr ) return m_dlg->IsActivated();
			return FALSE;
		}

		void Plugin::OnEndDocument(MQDocument doc) {
			if ( m_dlg != nullptr ) m_dlg->OnEndDocument();
		}
			
		void Plugin::OnMaterialModified(MQDocument doc) {
			if ( m_dlg != nullptr ) m_dlg->OnMaterialModified(doc);
		}

		void Plugin::OnNewDocument(MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param) {
			if ( m_dlg != nullptr ) m_dlg->OnNewDocument(doc,filename,param);
		}

		void Plugin::OnUpdateMaterialList(MQDocument doc) {
			if ( m_dlg != nullptr ) m_dlg->OnMaterialModified(doc);
		}

		void Plugin::OnUpdateObjectList(MQDocument doc) {
			if ( m_dlg != nullptr ) m_dlg->OnMaterialModified(doc);
		}

		void Plugin::OnSaveDocument(MQDocument doc, const char *filename, MQStationPlugin::SAVE_DOCUMENT_PARAM& param) {
			if ( m_dlg != nullptr ) m_dlg->OnSaveDocument(doc,filename,param);
		}

		bool Plugin::OnException( System::Exception^ ex ) {
			System::String^ title = gcnew System::String(GetPlugInName());
			System::String^ pluginName = gcnew System::String(EnumString());
			System::String^ filePath = "exception.txt";
			System::String^ LogMessage = System::String::Format("Exception happened. Check error log.{0}{1}", System::Environment::NewLine, filePath );
			System::String^ LogException = nullptr;
			bool isSucceeded = false;
			int LogFileOpenWaitTimeLimit = -1;
			try {
				bool isCreate;
				System::Threading::Mutex^ mutex = gcnew System::Threading::Mutex(true, title + pluginName + filePath, isCreate);
				LogException = DotNetEx::String::Extention::ToNestedMultilineString(ex,"\t",0);
				if ( mutex != nullptr ) {
					if ( isCreate == false ) mutex->WaitOne(System::Math::Max(-1,LogFileOpenWaitTimeLimit));
					{
						System::Reflection::Assembly^ Assembly = System::Reflection::Assembly::GetEntryAssembly();
						System::IO::StreamWriter^ LogWriter = gcnew System::IO::StreamWriter(filePath, true);
						LogWriter->WriteLine( Assembly->ToString() );
						LogWriter->WriteLine( Assembly->Location );
						LogWriter->WriteLine("happened at " + System::DateTime::Now.ToString("yyyyMMdd_HHmmss"));
						LogWriter->WriteLine(DotNetEx::String::Extention::ToNestedMultilineString(ex,"\t",1));
						LogWriter->Close();
					}
					isSucceeded = true;
				}
			}
			catch ( System::Exception^ ) {
			}
		
			if ( isSucceeded == false ) {
				LogMessage = "Log File AccessError.";
				if ( LogException != nullptr ) LogMessage += LogException;
			}
			System::Windows::Forms::MessageBox::Show( LogMessage, pluginName );

	#if _DEBUG
			return true;
	#else
			return false;
	#endif
		}

		MainEditForm::MainEditForm( Plugin^ plugin ) : CustomControls::MainEditForm_Base(), m_lock( gcnew System::Object() ), m_drawLock( gcnew System::Object() )
		{
#if _DEBUG
			ConsoleEnabled = true;
			TextLogFile::MessageOnLog = true;
			TextLogFile::AppendLine("Boot MainEditForm");
#endif

			if ( plugin == nullptr ) throw gcnew System::NullReferenceException();
			m_ownerPlugin = plugin;
			m_pUVBuffer = new UVTransformedBuffer();
			m_customBakeScriptFileFilterIndex = 1;

			m_isFirstActivate = true;
			m_isMouseRangeSelecting = false;
			m_isMouseCanceled = false;
			m_mousePosition = EmptyPosition;
			m_mouseDragStartPosition = EmptyPosition;
			m_docData = gcnew Document();

			for each ( unsigned int res in GridResolutions ) cmbResolution->Items->Add( res.ToString() );

			pnlGL->GLPrePaintOnce += gcnew System::ComponentModel::CancelEventHandler(this, &MainEditForm::pnlGL_GLPrePaintOnce);
			pnlGL->GLPaint += gcnew System::Windows::Forms::PaintEventHandler(this, &MainEditForm::pnlGL_GLPaint);
			System::Windows::Forms::MouseEventHandler^ ehGLMouseMove = gcnew System::Windows::Forms::MouseEventHandler(this, &MainEditForm::pnlGL_GLMouseMove);
			pnlGL->GLMouseDown += ehGLMouseMove;
			pnlGL->GLMouseMove += ehGLMouseMove;

			btnTrans->Click += gcnew System::EventHandler(this, &MainEditForm::btnTrans_Click);
			btnMatClr->Click += gcnew System::EventHandler(this, &MainEditForm::btnMatClr_Click);
			btnToMatClr->Click += gcnew System::EventHandler(this, &MainEditForm::btnToMatClr_Click);
			btnPilotSwitch->Click += gcnew System::EventHandler(this, &MainEditForm::btnPilotSwitch_Click);
			btnBakeVColor->Click += gcnew System::EventHandler(this, &MainEditForm::btnBakeVColor_Click);
			btnCustomBake->Click += gcnew System::EventHandler(this, &MainEditForm::btnCustomBake_Click);

			System::Windows::Forms::KeyEventHandler^ ehTxt_KeyUp = gcnew System::Windows::Forms::KeyEventHandler(this,&MainEditForm::txt_KeyUp);
			txtSclU->KeyUp += ehTxt_KeyUp;
			txtSclV->KeyUp += ehTxt_KeyUp;
			txtMovU->KeyUp += ehTxt_KeyUp;
			txtMovV->KeyUp += ehTxt_KeyUp;
			txtPadding->KeyUp += ehTxt_KeyUp;
			txtCmbTexClearColor->KeyUp += ehTxt_KeyUp;
			txtCmbAlphaClearColor->KeyUp += ehTxt_KeyUp;
			txtCmbBumpClearColor->KeyUp += ehTxt_KeyUp;
		
			System::ComponentModel::CancelEventHandler^ ehTxt_FloatValidating =
				gcnew System::ComponentModel::CancelEventHandler(this, &MainEditForm::txt_FloatValidating);
			txtSclU->Validating += ehTxt_FloatValidating;
			txtSclV->Validating += ehTxt_FloatValidating;
			txtMovU->Validating += ehTxt_FloatValidating;
			txtMovV->Validating += ehTxt_FloatValidating;
			txtPadding->Validating += ehTxt_FloatValidating;
			txtBakeVColorBorder->Validating += ehTxt_FloatValidating;

			System::ComponentModel::CancelEventHandler^ ehTxt_ColorValidating =
				gcnew System::ComponentModel::CancelEventHandler(this, &MainEditForm::txt_ColorValidating);
			txtCmbTexClearColor->Validating += ehTxt_ColorValidating;
			txtCmbAlphaClearColor->Validating += ehTxt_ColorValidating;
			txtCmbBumpClearColor->Validating += ehTxt_ColorValidating;
			txtBakeVColorClear->Validating += ehTxt_ColorValidating;

			System::ComponentModel::CancelEventHandler^ ehTxt_AbsIntValidating =
				gcnew System::ComponentModel::CancelEventHandler(this, &MainEditForm::txt_AbsIntValidating);
			txtBakeVColorW->Validating += ehTxt_AbsIntValidating;
			txtBakeVColorH->Validating += ehTxt_AbsIntValidating;
		
			System::EventHandler^ ehPluginData_InputChanged = gcnew System::EventHandler(this, &MainEditForm::pluginData_InputChanged);
			txtMovU->Validated += ehPluginData_InputChanged;
			txtMovV->Validated += ehPluginData_InputChanged;
			txtSclU->Validated += ehPluginData_InputChanged;
			txtSclV->Validated += ehPluginData_InputChanged;
			txtPadding->Validated += ehPluginData_InputChanged;
			cmbToMat->SelectedIndexChanged += ehPluginData_InputChanged;
			chkRecursive->CheckedChanged += ehPluginData_InputChanged;
			chkClone->CheckedChanged += ehPluginData_InputChanged;
			chkCmbTex->CheckedChanged += ehPluginData_InputChanged;
			chkCmbTexClear->CheckedChanged += ehPluginData_InputChanged;
			txtCmbTexClearColor->Validated += ehPluginData_InputChanged;
			chkCmbAlpha->CheckedChanged += ehPluginData_InputChanged;
			chkCmbAlphaClear->CheckedChanged += ehPluginData_InputChanged;
			txtCmbAlphaClearColor->Validated += ehPluginData_InputChanged;
			chkCmbBump->CheckedChanged += ehPluginData_InputChanged;
			chkCmbBumpClear->CheckedChanged += ehPluginData_InputChanged;
			txtCmbBumpClearColor->Validated += ehPluginData_InputChanged;
			txtBakeVColorBorder->Validated += ehPluginData_InputChanged;
			txtBakeVColorClear->Validated += ehPluginData_InputChanged;
			txtBakeVColorW->Validated += ehPluginData_InputChanged;
			txtBakeVColorH->Validated += ehPluginData_InputChanged;

			chkSelMatTrans->CheckedChanged += gcnew System::EventHandler(this, &MainEditForm::chkSelMatTrans_CheckedChanged);

			cmbSelMat->SelectedIndexChanged += gcnew System::EventHandler(this, &MainEditForm::cmbSelMat_SelectedIndexChanged);
			cmbResolution->SelectedIndexChanged += gcnew System::EventHandler(this, &MainEditForm::cmbResolution_SelectedIndexChanged);
			System::EventHandler^ ehPnlPaint_CallInvalidate = gcnew System::EventHandler(this, &MainEditForm::pnlPaint_CallInvalidate);
			chkDisplayGrid->CheckedChanged += ehPnlPaint_CallInvalidate;
			chkDisplayFill->CheckedChanged += ehPnlPaint_CallInvalidate;
			chkDisplayBorder->CheckedChanged += ehPnlPaint_CallInvalidate;
			chkDisplayPoly->CheckedChanged += ehPnlPaint_CallInvalidate;
			rdbDisplayNormal->CheckedChanged += ehPnlPaint_CallInvalidate;
			rdbDisplayVColorPreview->CheckedChanged += ehPnlPaint_CallInvalidate;
			cmbResolution->DropDownClosed += ehPnlPaint_CallInvalidate;
		}

		MainEditForm::!MainEditForm()
		{
			if ( m_pUVBuffer != NULL ) { delete m_pUVBuffer; m_pUVBuffer = NULL; }
		}

		System::Collections::ObjectModel::ReadOnlyCollection<unsigned int>^ MainEditForm::GridResolutions::get() {
			if ( m_gridResolutions == nullptr ) m_gridResolutions = System::Array::AsReadOnly( gcnew array<unsigned int>{ 8, 10, 12, 14, 16, 24, 32, 48, 64 } );
			return m_gridResolutions;
		}
	
		int MainEditForm::GridResolution::get() {
			int res = 0;
			if ( 0<=cmbResolution->SelectedIndex ) int::TryParse(cmbResolution->SelectedItem->ToString(), res);
			return res;
		}

		System::Drawing::Point MainEditForm::mousePosition::get() {
			int res = GridResolution;
			if ( 0 < res ) {
				System::Drawing::Point pt = pnlGL->GLPointToClient(System::Windows::Forms::Cursor::Position);
				if ( System::Drawing::Rectangle( 0, 0, pnlGL->GLSize.Width, pnlGL->GLSize.Height ).Contains( pt ) )
				return System::Drawing::Point( res * pt.X / pnlGL->GLSize.Width, res * pt.Y / pnlGL->GLSize.Height );
			}
			return EmptyPosition;
		}

		BOOL MainEditForm::Activate(MQDocument doc, BOOL value) {
			MQCLI::Lock lock( m_lock );
			m_docData->MQDocument = doc;
			if ( value == FALSE ) {
				Form::Visible = false;
				pnlGL->Enabled = false;
				m_ownerPlugin->WindowClose();
			}
			else if ( m_isFirstActivate ) {
				
#if _DEBUG
				TextLogFile::AppendLine("ConfigInitialize MainEditForm");
#endif
				m_isFirstActivate = false;

				// read config
				Config^ config = nullptr;
				if ( m_ownerPlugin != nullptr ) {
					MQ0x::SettingProxy settingProxy( m_ownerPlugin->Setting );
					std::string setting("");
					settingProxy.Load( PLUGIN_SETTING_KEY, setting );
					if ( setting != "" ) {
						System::IO::StringReader^ reader = gcnew System::IO::StringReader(msclr::interop::marshal_as<System::String^>(setting));
						try {
							System::Xml::Serialization::XmlSerializer^ serializer
								= gcnew System::Xml::Serialization::XmlSerializer(Config::typeid);
							config = dynamic_cast<Config^>(serializer->Deserialize(reader));
						}
						catch ( System::Exception^ /*ex*/ ) {
							config = nullptr;
							::MessageBox(0,"exception config load",0,0);
						}
						finally {
							reader->Close();
						}
					}
				}
				if ( config == nullptr ) config = gcnew Config();
				if (0 < System::Math::Min(System::Math::Min(config->Width,config->Height),config->SplitterW))
				{
					Size = System::Drawing::Size(config->Width, config->Height);
					if (0 < config->CanvasRestoreW) CanvasClose(config->CanvasRestoreW);
					else {
						CanvasOpen();
						splitter->SplitterDistance = config->SplitterW;
					}
				}
				int selectIndex = 0;
				for (int index = 0; index < cmbResolution->Items->Count; index++) {
					if ( cmbResolution->Items[index] == config->GridResolution.ToString() ) { selectIndex = index; break;	}
				}
				cmbResolution->SelectedIndex = selectIndex;
				chkDisplayTransparent->Checked = config->IsDisplayTransparent;
				chkDisplayGrid->Checked = config->IsDisplayGrid;
				chkDisplayFill->Checked = config->IsDisplayFill;
				chkDisplayBorder->Checked = config->IsDisplayBorder;
				chkDisplayPoly->Checked = config->IsDisplayPoly;
				MenuGroupVisibles["tblSelectedObject"] = config->IsDisplaySelectedObjectMenu;
				MenuGroupVisibles["tblMaterialEdit"] = config->IsDisplayMaterialEditMenu;
				MenuGroupVisibles["tblObjectTrans"] = config->IsDisplayObjcetTransMenu;
				MenuGroupVisibles["tblCmbTex"] = config->IsDisplayCmbTexMenu;
				TexSetFormSize = System::Drawing::Size( System::Math::Max(0,config->TexSetFormW), System::Math::Max(0,config->TexSetFormH) );
				MenuGroupVisibles["tblBakeVColor"] = config->IsDisplayBakeTexMenu;
				BakeTextureBaseColor = config->BakeTexBaseColor;
				BakeTextureBorder = config->BakeTexBorder;
				BakeTextureW = config->BakeTexImgW;
				BakeTextureH = config->BakeTexImgH;
				m_buildResultWinX = config->BuildResultFormW;
				m_buildResultWinY = config->BuildResultFormH;
				m_customBakeScriptDirectory = config->ScriptLoadDir;
				switch ( config->CanvasMode ) {
				case UVTexIntegra::CanvasMode::VertexColorPreview:
					rdbDisplayVColorPreview->Checked = true;
					break;
				default:
					rdbDisplayNormal->Checked = true;
					break;
				}
				
				Show( gcnew MQCLI::MQWndHandle() );
				m_docData->RefreshDialog(this, -1);

#if _DEBUG
				TextLogFile::AppendLine("ConfigInitialize -> Complete MainEditForm");
#endif
			}
			else {
				pnlGL->Enabled = true;
				Form::Visible = true;
			}

			return value;
		}

		void MainEditForm::Exit()
		{
			MQCLI::Lock lock(m_lock);
			if ((m_ownerPlugin != nullptr) && (m_isFirstActivate == false))
			{
				MQ0x::SettingProxy settingProxy(m_ownerPlugin->Setting);
				Config^ config = gcnew Config();
				config->Width = Width;
				config->Height = Height;
				config->SplitterW = splitter->Panel1->Width;
				config->CanvasRestoreW = CanvasRestoreWidth;
				config->IsDisplayTransparent = chkDisplayTransparent->Checked;
				config->IsDisplayGrid = chkDisplayGrid->Checked;
				config->IsDisplayFill = chkDisplayFill->Checked;
				config->IsDisplayBorder = chkDisplayBorder->Checked;
				config->IsDisplayPoly = chkDisplayPoly->Checked;
				config->IsDisplaySelectedObjectMenu = MenuGroupVisibles["tblSelectedObject"];
				config->IsDisplayMaterialEditMenu = MenuGroupVisibles["tblMaterialEdit"];
				config->IsDisplayObjcetTransMenu = MenuGroupVisibles["tblObjectTrans"];
				config->IsDisplayCmbTexMenu = MenuGroupVisibles["tblCmbTex"];
				config->GridResolution = GridResolution;
				config->TexSetFormW = TexSetFormSize.Width;
				config->TexSetFormH = TexSetFormSize.Height;
				config->IsDisplayBakeTexMenu = MenuGroupVisibles["tblBakeVColor"];
				config->BakeTexBaseColor = BakeTextureBaseColor;
				config->BakeTexBorder = BakeTextureBorder;
				config->BakeTexImgW = BakeTextureW;
				config->BakeTexImgH = BakeTextureH;
				config->BuildResultFormW = m_buildResultWinX;
				config->BuildResultFormH = m_buildResultWinY;
				config->ScriptLoadDir = m_customBakeScriptDirectory;
				if (rdbDisplayVColorPreview->Checked) config->CanvasMode = UVTexIntegra::CanvasMode::VertexColorPreview;
				else config->CanvasMode = UVTexIntegra::CanvasMode::Normal;
				System::IO::StringWriter^ writer = gcnew System::IO::StringWriter();
				try
				{
					System::Xml::Serialization::XmlSerializer^ serializer
						= gcnew System::Xml::Serialization::XmlSerializer(Config::typeid);
					serializer->Serialize(writer, config);
					std::string strConfig = msclr::interop::marshal_as<std::string>(writer->ToString()->Replace(System::Environment::NewLine, ""));
					if ( strConfig != "" ) settingProxy.Save( PLUGIN_SETTING_KEY, strConfig );
				}
				catch ( System::Exception^ /*ex*/ ) {}
				finally { delete writer; }
			}
		}

		System::Drawing::RectangleF MainEditForm::GetGridCursorRectangle( System::Drawing::Point pt ) {
			int res = GridResolution;
			if ( ( 0 < res ) && (System::Drawing::Rectangle( 0, 0, res, res ).Contains( pt )) ) {
				const float fres = (float)res;
				const float left = pnlGL->GLSize.Width * pt.X / fres;
				const float right = pnlGL->GLSize.Width * (pt.X+1) / fres;
				const float top = pnlGL->GLSize.Height * pt.Y / fres;
				const float bottom = pnlGL->GLSize.Height * (pt.Y+1) / fres;
				return System::Drawing::RectangleF( left, top, right-left, bottom-top );
			}
			return EmptyRectangleF;
		}

		System::Drawing::RectangleF MainEditForm::GetGridCursorRectangle( System::Drawing::Point ptA, System::Drawing::Point ptB ) {
			int res = GridResolution;
			if ( 0 < res ) {
				System::Drawing::Rectangle rcResolution( 0, 0, res, res );
				if ( rcResolution.Contains(ptA) && rcResolution.Contains(ptB) ) {
					System::Drawing::Point ptMin( std::min(ptA.X, ptB.X), std::min(ptA.Y, ptB.Y) );
					System::Drawing::Point ptMax( std::max(ptA.X, ptB.X), std::max(ptA.Y, ptB.Y) );
					const float fres = (float)res;
					const float left = pnlGL->GLSize.Width * ptMin.X / fres;
					const float right = pnlGL->GLSize.Width * (ptMax.X+1) / fres;
					const float top = pnlGL->GLSize.Height * ptMin.Y / fres;
					const float bottom = pnlGL->GLSize.Height * (ptMax.Y+1) / fres;
					return System::Drawing::RectangleF( left, top, right-left, bottom-top );
				}
			}
			return EmptyRectangleF;
		}

		void MainEditForm::OnNewDocument( MQDocument doc, const char *filename, MQStationPlugin::NEW_DOCUMENT_PARAM& param ) {
			m_docData->MQDocument = doc;
			if ( m_pUVBuffer == NULL ) m_pUVBuffer = new UVTransformedBuffer();
			else m_pUVBuffer->Clear(m_docData, true);
			m_docData->OnMQOLoaded( this, param.elem );
		}

		// event implements //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		System::Void MainEditForm::OnFormClosing(System::Windows::Forms::FormClosingEventArgs^ e)
		{
			if (e->CloseReason == System::Windows::Forms::CloseReason::UserClosing)
			{
				e->Cancel = true;
				Activate(m_docData->MQDocument, FALSE);
				SetForegroundWindow(MQ_GetWindowHandle());
			}
			else MainEditForm_Base::OnFormClosing(e);
		}
	
		System::Void MainEditForm::btnPilotSwitch_Click(System::Object^, System::EventArgs^ ) {
			if ( m_docData->IsEmpty ) {
				m_docData->OnMQOModified(this,true);
				if ( m_docData->IsEmpty ) MessageBox::Show("有効なマテリアルデータがありません");
			}
			else if ( System::Windows::Forms::DialogResult::OK == MessageBox::Show(this,"データをクリアしますか？",System::String::Empty,MessageBoxButtons::OKCancel) ) {
				m_docData->OnMQOLoaded(this, NULL);
			}
		}

		System::Void MainEditForm::btnCustomBake_Click(System::Object^, System::EventArgs^) {
			if (btnBakeVColor->Enabled) m_docData->OnCustomBake(this);
		}

		System::Void MainEditForm::btnMatClr_Click(System::Object^, System::EventArgs^ ) { m_docData->SelectedMaterialsReset(this); }

		System::Void MainEditForm::btnToMatClr_Click(System::Object^ , System::EventArgs^ ) {
			cmbToMat->SelectedIndex = -1;
			m_docData->SelectedMaterialsToMatClear(this);
		}

		System::Void MainEditForm::btnTrans_Click(System::Object^, System::EventArgs^ ) { m_docData->TryTrans(this); }

		System::Void MainEditForm::btnBakeVColor_Click(System::Object^, System::EventArgs^ ) {
			
			System::Windows::Forms::SaveFileDialog^ sfd = nullptr;
			System::Windows::Forms::DialogResult ret = System::Windows::Forms::DialogResult::None;
			System::Drawing::Bitmap^ bmp = nullptr;
			System::Drawing::Graphics^ g = nullptr;
			const int w = BakeTextureW, h = BakeTextureH;
			if ( m_docData->MQDocument == NULL ) {
				MessageBox::Show( "ドキュメントがありません。" );
				return;
			}
			if ( (w<=0) || (h<=0) ) {
				MessageBox::Show( "画像サイズが小さすぎます。" );
				return;
			}
			try {
				char buf[MAX_PATH];
				System::String^ directoryPath = nullptr;
				try { directoryPath = System::IO::Path::GetDirectoryName(BakeTextureLastFilePath); }
				catch ( System::Exception^ ) { directoryPath = nullptr; }
				if ( System::IO::Directory::Exists(directoryPath) == false ) directoryPath = nullptr;
				if ( System::String::IsNullOrEmpty(directoryPath) && MQ_GetSystemPath( buf, MQFOLDER_ROOT ) ) directoryPath = gcnew System::String(buf);
				sfd = gcnew System::Windows::Forms::SaveFileDialog();
				sfd->SupportMultiDottedExtensions = true;
				sfd->InitialDirectory = directoryPath;
				sfd->Filter = "32bitFullColorPNG (*.png)|*.png";
				if ( (ret = sfd->ShowDialog(this)) == System::Windows::Forms::DialogResult::OK ) {
					bmp = gcnew System::Drawing::Bitmap( w, h, System::Drawing::Imaging::PixelFormat::Format32bppArgb );
					g = System::Drawing::Graphics::FromImage(bmp);
					g->Clear( System::Drawing::Color::FromArgb(BakeTextureBaseColor) );
					m_docData->BakeTexturePaint( this, g, bmp->Size );
					delete g;
					g = nullptr;
					bmp->Save( sfd->FileName, System::Drawing::Imaging::ImageFormat::Png );
				}
			}
			catch (System::Exception^ ex) {
				ret = System::Windows::Forms::DialogResult::None;
				MessageBox::Show( ex->ToString(), "エラーが発生しました。" );
	#if _DEBUG
				throw;
	#endif
			}
			finally {
				if (g != nullptr) delete g;
				if (bmp != nullptr) delete bmp;
				if (ret == System::Windows::Forms::DialogResult::OK) BakeTextureLastFilePath = sfd->FileName;
				if (sfd != nullptr) delete sfd;
			}
		}

		System::Void MainEditForm::cmbSelMat_SelectedIndexChanged(System::Object^, System::EventArgs^)
		{
			// apply data
			m_docData->IsComboboxIndexSelecting = true;
			m_docData->RefreshDialog(this, -1);
		}

		System::Void MainEditForm::pnlGL_GLPrePaintOnce(System::Object^ sender, System::ComponentModel::CancelEventArgs^)
		{
			MQCLI::Lock lock(m_drawLock);

			OpenTK::GLControl^ glCtrl = dynamic_cast<OpenTK::GLControl^>(sender);
			// ready opengl
			Int32 glVertexShader = 0, glFragmentShader = 0;

			try
			{
				glCtrl->MakeCurrent();
				m_glShaderProgram = 0;
				glVertexShader = GL::CreateShader(OpenTK::Graphics::OpenGL::ShaderType::VertexShader);
				glFragmentShader = GL::CreateShader(OpenTK::Graphics::OpenGL::ShaderType::FragmentShader);
				GL::ShaderSource(glVertexShader, "void main(void) { gl_Position=gl_Vertex; gl_FrontColor=gl_Color; }");
				GL::ShaderSource(glFragmentShader, "void main(void) { gl_FragColor = gl_Color; }");
				Int32 isCompiledVertexShader = 0;
				GL::CompileShader(glVertexShader);
				GL::GetShader(glVertexShader, OpenTK::Graphics::OpenGL::ShaderParameter::CompileStatus, isCompiledVertexShader);
				if ( isCompiledVertexShader == 0 ) {
					System::Int32 length;
					System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
					GL::GetShaderInfoLog(glVertexShader, 4096, length, sb);
					MessageBox::Show(sb->ToString(),"VertexShader CompileError");
				}
				Int32 isCompiledFragmentShader;
				GL::CompileShader(glFragmentShader);
				GL::GetShader(glFragmentShader, OpenTK::Graphics::OpenGL::ShaderParameter::CompileStatus, isCompiledFragmentShader);
				if ( isCompiledFragmentShader == 0 ) {
					System::Int32 length;
					System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
					GL::GetShaderInfoLog(glFragmentShader, 4096, length, sb);
					MessageBox::Show(sb->ToString(), "FragmentShader CompileError");
				}
				if ( (isCompiledVertexShader!=0) && (isCompiledFragmentShader!=0) ) {
					m_glShaderProgram = GL::CreateProgram();
					GL::AttachShader(m_glShaderProgram, glVertexShader);
					GL::AttachShader(m_glShaderProgram, glFragmentShader);
					GL::LinkProgram(m_glShaderProgram);
					Int32 isLink = 0;
					GL::GetProgram(m_glShaderProgram, OpenTK::Graphics::OpenGL::ProgramParameter::LinkStatus, isLink);
					if (isLink == 0) {
						System::Int32 length;
						System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder();
						GL::GetShaderInfoLog(m_glShaderProgram, 4096, length, sb);
						GL::DeleteProgram(m_glShaderProgram);
						m_glShaderProgram = 0;
						MessageBox::Show(sb->ToString(),"LinkProgram Error");
					}
				}
			}
			catch (System::Exception^ ex) { MessageBox::Show(ex->ToString()); }
			finally {
				if (glVertexShader != 0) GL::DeleteShader(glVertexShader);
				if (glFragmentShader != 0) GL::DeleteShader(glFragmentShader);
			}
			if (m_glShaderProgram == 0) {
				if ( rdbDisplayVColorPreview->Checked ) rdbDisplayNormal->Checked = true;
				btnBakeVColor->Enabled = false;
				btnCustomBake->Visible = false;
				rdbDisplayVColorPreview->Enabled = false;
			}
			
		}
		
		System::Void MainEditForm::pnlGL_GLPaint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)
		{
			MQCLI::Lock lock( m_drawLock );

			if (this->Visible == false) return;

			OpenTK::GLControl^ glCtrl = dynamic_cast<OpenTK::GLControl^>(sender);
			if (glCtrl == nullptr) return;

			glCtrl->MakeCurrent();

			System::Drawing::Graphics^ g = e->Graphics;
			int res = GridResolution;
			if ( 0 < res ) {

				System::Drawing::RectangleF rcCursor = GetGridCursorRectangle( m_mousePosition );
				bool rcCursorIsEmpty = (mousePosition == EmptyPosition) || IsEmptyRectangleF( rcCursor );

				m_docData->Paint( this, g, pnlGL->GLSize,  m_isMouseRangeSelecting, rcCursorIsEmpty );

				System::Drawing::Color curColor = System::Drawing::SystemColors::Highlight;
				EditData^ data = m_docData->ItemFromIndex( cmbSelMat->SelectedIndex );
				if ( data != nullptr ) {
					MQMaterial mat = MQ0x::GetMaterial( m_docData->MQDocument, data->UniqueID );
					if ( mat != NULL ) {
						MQColor mqCol = mat->GetColor();
						curColor = System::Drawing::Color::FromArgb( int(0xFF * mqCol.r), int(0xFF * mqCol.g), int(0xFF * mqCol.b) );
					}
				}

				// draw cursor
				if ( rcCursorIsEmpty == false ) {
					System::Drawing::SolidBrush^ brush = gcnew System::Drawing::SolidBrush(curColor);
					g->FillRectangle( brush, rcCursor );
					delete brush;
				}

				// draw grid
				if ( chkDisplayGrid->Checked ) {
					System::Drawing::Pen^ pen = gcnew System::Drawing::Pen( System::Drawing::Color::FromArgb(0x80,0x80,0x80), 1 );
					float Width = (float)pnlGL->GLSize.Width;
					float Height = (float)pnlGL->GLSize.Height;
					for ( int index = 1; index < res; index++ ) {
						float left = (pnlGL->GLSize.Width * index) / float(res);
						float top = (pnlGL->GLSize.Height * index) / float(res);
						g->DrawLine( pen, left, 0.0f, left, Height ); // v
						g->DrawLine( pen, 0.0f, top, Width, top ); // h
					}
					delete pen;
				}

				// draw drag area
				if ( m_mouseDragStartPosition != m_mousePosition ) {
					System::Drawing::RectangleF rcSelecting = GetGridCursorRectangle( m_mousePosition, m_mouseDragStartPosition );
					if ( IsEmptyRectangleF(rcSelecting) == false ) {
						System::Drawing::Pen^ pen = gcnew System::Drawing::Pen( curColor, 2 );
						g->DrawRectangle(pen, rcSelecting.Left, rcSelecting.Top, rcSelecting.Width, rcSelecting.Height);
						delete pen;
						System::Drawing::Brush^ brush = gcnew System::Drawing::SolidBrush(System::Drawing::Color::FromArgb(63, curColor));
						g->FillRectangle(brush, rcSelecting);
						delete brush;
					}
				}

				// draw cursor border
				if ( rcCursorIsEmpty == false ) {
					System::Drawing::Pen^ pen = gcnew System::Drawing::Pen(System::Drawing::SystemColors::Highlight, 2);
					g->DrawRectangle(pen, rcCursor.Left, rcCursor.Top, rcCursor.Width, rcCursor.Height);
					delete pen;
				}
			}

		}

		System::Void MainEditForm::txt_KeyUp(System::Object^  sender, System::Windows::Forms::KeyEventArgs^  e) {
			if (e->KeyCode == Keys::Escape) m_docData->RefreshDialog(this, -1);
			else if (e->KeyCode == Keys::Enter) {
				System::Windows::Forms::TextBox^ txtBox = dynamic_cast<System::Windows::Forms::TextBox^>(sender);
				if (txtBox != nullptr) pluginData_InputChanged(sender, nullptr);
			}
		}

		System::Void MainEditForm::txt_AbsIntValidating( Object^ sender, System::ComponentModel::CancelEventArgs^ ) {
			System::Windows::Forms::Control^ ctrl = dynamic_cast<System::Windows::Forms::Control^>(sender);
			if ( ctrl == nullptr ) return;
			System::Int32 val;
			if ( System::Int32::TryParse( ctrl->Text, val ) ) {
				val = System::Math::Abs( val );
				if ( ctrl->Tag != nullptr ) {
					try { ctrl->Text = val.ToString(ctrl->Tag->ToString()); return; }
					catch (System::Exception^) {}
				}
				ctrl->Text = val.ToString();
			}
			else m_docData->RefreshDialog(this, -1);
		}

		System::Void MainEditForm::txt_FloatValidating( Object^ sender, System::ComponentModel::CancelEventArgs^ ) {
			System::Windows::Forms::Control^ ctrl = dynamic_cast<System::Windows::Forms::Control^>(sender);
			if ( ctrl == nullptr ) return;
			float val;
			if ( float::TryParse( ctrl->Text, val ) ) {
				if ( ctrl->Tag != nullptr ) {
					try { ctrl->Text = val.ToString(ctrl->Tag->ToString()); return; }
					catch (System::Exception^) {}
				}
				ctrl->Text = val.ToString();
			}
			else m_docData->RefreshDialog(this, -1);
		}

		System::Void MainEditForm::txt_ColorValidating(Object^ sender, System::ComponentModel::CancelEventArgs^) {
			System::Windows::Forms::Control^ ctrl = dynamic_cast<System::Windows::Forms::Control^>(sender);
			if ( ctrl == nullptr ) return;
			System::Int32 colorInt;
			if ( System::Int32::TryParse( ctrl->Text, System::Globalization::NumberStyles::AllowHexSpecifier, nullptr, colorInt ) ) {
				if ( ctrl->Tag != nullptr ) {
					try { ctrl->Text = colorInt.ToString(ctrl->Tag->ToString()); return; }
					catch (System::Exception^) {}
				}
				ctrl->Text = colorInt.ToString("X8");
			}
			else m_docData->RefreshDialog(this, -1); // restore
		}

		System::Void MainEditForm::chkSelMatTrans_CheckedChanged(System::Object^ sender, System::EventArgs^ e) {
			if (chkSelMatTrans->CheckState != System::Windows::Forms::CheckState::Indeterminate) chkSelMatTrans->ThreeState = false;
			pluginData_InputChanged(sender, e);
		}

		System::Void MainEditForm::pluginData_InputChanged(System::Object^, System::EventArgs^) { m_docData->OnInputChanged(this); }

		System::Void MainEditForm::cmbResolution_SelectedIndexChanged(System::Object^, System::EventArgs^) {
			m_mouseDragStartPosition = m_mousePosition = EmptyPosition;
			m_isMouseRangeSelecting = false;
		}

		System::Void MainEditForm::pnlGL_GLMouseMove(System::Object^, System::Windows::Forms::MouseEventArgs^ e) {
		
			if (pnlGL->CanFocus == false) return;
			
			const bool reg_isMouseRangeSelecting = m_isMouseRangeSelecting;
			const bool reg_isMouseCanceled = m_isMouseCanceled;
			System::Drawing::Point reg_mousePosition = m_mousePosition;
			System::Drawing::Point reg_mouseDragStartPosition = m_mouseDragStartPosition;

			bool isInputChanged = false;
			const bool isLeftDown = (e->Button&System::Windows::Forms::MouseButtons::Left) == System::Windows::Forms::MouseButtons::Left;
			const bool isRightDown = (e->Button&System::Windows::Forms::MouseButtons::Right) == System::Windows::Forms::MouseButtons::Right;
			const bool isContinueDrag = (isLeftDown == true) && (isRightDown == false);
			const bool isButtonFree = (isLeftDown == false) && (isRightDown == false);
			if (isButtonFree) m_isMouseCanceled = false;
			if (isContinueDrag && m_isMouseRangeSelecting) m_mousePosition = mousePosition;
			else
			{
				if (m_isMouseRangeSelecting)
				{
					if (isButtonFree)
					{
						// accept d&d
						int res = GridResolution;
						EditData^ data = m_docData->ItemFromIndex( cmbSelMat->SelectedIndex );
						if ( (0 < res) && (data != nullptr) ) {
							System::Drawing::Rectangle rcResolution( 0, 0, res, res );
							if ( rcResolution.Contains(m_mousePosition) && rcResolution.Contains(m_mouseDragStartPosition) ) {
								System::Drawing::Point ptMin( std::min(m_mousePosition.X, m_mouseDragStartPosition.X), std::min(m_mousePosition.Y, m_mouseDragStartPosition.Y) );
								System::Drawing::Point ptMax( std::max(m_mousePosition.X, m_mouseDragStartPosition.X), std::max(m_mousePosition.Y, m_mouseDragStartPosition.Y) );
								const double left = double(ptMin.X) / res;
								const double right = double(ptMax.X+1) / res;
								const double top = double(ptMin.Y) / res;
								const double bottom = double(ptMax.Y+1) / res;
								txtSclU->Text = (right-left).ToString(txtSclU->Tag->ToString());
								txtSclV->Text = (bottom-top).ToString(txtSclV->Tag->ToString());
								txtMovU->Text = left.ToString(txtMovU->Tag->ToString());
								txtMovV->Text = top.ToString(txtMovV->Tag->ToString());
								isInputChanged = true;
							}
						}
					}
					else m_isMouseCanceled = true;
				}
				m_mouseDragStartPosition = m_mousePosition = mousePosition;
			}

			// state update
			const bool newState = isContinueDrag && (m_isMouseCanceled==false);
			if ((m_isMouseRangeSelecting==false)&&newState) this->ActiveControl = nullptr;
			m_isMouseRangeSelecting = newState;
		
			lblStatus->Text = System::String::Format("{0}, {1}", m_mousePosition.X, m_mousePosition.Y);

			if (isInputChanged == false)
			{
				if ((reg_isMouseRangeSelecting != m_isMouseRangeSelecting) || (reg_isMouseCanceled != m_isMouseCanceled) ||
					(reg_mousePosition != m_mousePosition) || (reg_mouseDragStartPosition != m_mouseDragStartPosition))
				{
					pnlGL->Invalidate();
				}
			}
			else pluginData_InputChanged(nullptr,nullptr);
		}

	}
}