// == 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 ==

using System.Windows.Forms;

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

    //! @brief MainEditFormのベースとなるGUIデザインを提供します。
    public partial class MainEditForm_Base : Form
    {
        //! @brief デバッグ出力用のテキストコンソールを要求できるか設定します。
        public bool ConsoleEnabled {
            get { return m_consoleEnabled; }
            set {
                if ((m_consoleEnabled = value) == false)
                {
                    foreach (System.Windows.Forms.Form form in OwnedForms)
                    {
                        TextLogForm t = form as TextLogForm;
                        if (t != null) { t.Close(); break; }
                    }
                }
            }
        }

        //! @brief デバッグ出力用のテキストコンソールを要求します。
        public TextLogForm Console
        {
            get
            {
                if (ConsoleEnabled == false) return null;
                TextLogForm ret = null;
                foreach (System.Windows.Forms.Form form in OwnedForms)
                {
                    ret = form as TextLogForm;
                    if ((ret != null) && (ret.IsDisposed == false)) return ret;
                }
                ret = new TextLogForm(this);
                ret.Show();
                return ret;
            }
        }

        //! @brief カンバスのリストアサイズを取得します。
        //! @details カンバスがクローズ状態の時は0以下の値が返ります。
        public int CanvasRestoreWidth {
            get { return m_canvasRestoreWidth; }
        }

        //! @brief 最小サイズの設定,取得をオーバーライド。
        //! @details 代入時に、カンバスの開閉に応じて、その時のスプリッターサイズを満たせるように調整されます。
        //! カンバスの開閉時にMinimumSizeは自動的にリセットされます（問題になる場合は変更してください）。
        public override System.Drawing.Size MinimumSize
        {
            get { return base.MinimumSize; }
            set
            {
                System.Drawing.Size sz = SplitterMinSize;
                sz.Width += splitter.Padding.Left + splitter.Padding.Right;
                sz.Width += splitter.Margin.Left + splitter.Margin.Right;
                value.Width = System.Math.Max(sz.Width, value.Width);
                value.Height = System.Math.Max(sz.Height, value.Height);
                base.MinimumSize = value;
            }
        }

        //! @brief スプリッターのMinSizeを取得します。
        //! @details 幅はカンバスの開閉状態によって返される値が変わります。高さは常に0です。
        public System.Drawing.Size SplitterMinSize
        {
            get
            {
                int width = m_initialPanel1Width;
                if ((m_canvasClosed == false) && (splitter.Panel2Collapsed == false)) width += splitter.Panel2MinSize + splitter.SplitterWidth;
                return new System.Drawing.Size(width, 0);
            }
        }
        
        //! @brief 静的コンストラクタ
        static MainEditForm_Base()
        {
            DotNetEx.CounterKey.FinalizedWithoutExplicitDispose += new System.EventHandler(FinalizedWithoutExplicitDispose);
            DotNetEx.CounterKey.OverPop += new System.EventHandler(OverPop);
        }

        //! @brief デフォルトコンストラクタ。
        //! @details ベースデザインには次のような機能が盛り込まれています。
        //! - InitializeComponent呼び出し
        //! - デザイン上の体裁修正
        //! - GUI操作感改善のためのキー入力および動作カスタマイズ
        public MainEditForm_Base()
        {
            m_exceptionCatcher = new ExceptionCatcher();
            m_tblEditEnabled = true;
            m_tblMaterialEditEnabled = true;
            m_consoleEnabled = false;
            m_canvasClosed = false;
            m_canvasRestoreWidth = -1;
            try
            {
                this.InitializeComponent();

                SuspendLayout();
                btnBakeVColor.MaximumSize = new System.Drawing.Size(0, txtBakeVColorBorder.Height);
                System.Drawing.Size min_sz = new System.Drawing.Size(0, System.Math.Min(btnMatClr.Height, chkSelMatTrans.Height));
			    chkSelMatTrans.MaximumSize = min_sz;
			    btnMatClr.MaximumSize = min_sz;
                btnToMatClr.MaximumSize = new System.Drawing.Size(0,cmbToMat.Height);

                int content_width = lblSelectedObject.Width;
                content_width += lblSelectedObject.Padding.Left + lblSelectedObject.Padding.Right;
                content_width += lblSelectedObject.Margin.Left + lblSelectedObject.Margin.Right;
                content_width += tblEdit.Padding.Left + tblEdit.Padding.Right;
                content_width += tblEdit.Margin.Left + tblEdit.Margin.Right;
                m_initialPanel1Width = content_width;
                splitter.Panel1MinSize = content_width;
                splitter.SplitterDistance = content_width;

                MinimumSize = System.Drawing.Size.Empty; // reset by overrided property

                ResumeLayout();

                m_TabControlList = new Control[]
                {
				    btnPilotSwitch,
				    chkRecursive,

				    cmbSelMat,
				    chkSelMatTrans,
				    btnMatClr,
				    btnToMatClr,
				    cmbToMat,
				    txtSclU, txtSclV,
				    txtMovU, txtMovV,
				    txtPadding,
			
				    chkClone,
				    btnTrans,

				    chkCmbTex,
				    chkCmbTexClear,
				    txtCmbTexClearColor,
				    chkCmbAlpha,
				    chkCmbAlphaClear,
				    txtCmbAlphaClearColor,
				    chkCmbBump,
				    chkCmbBumpClear,
				    txtCmbBumpClearColor,

				    txtBakeVColorW, txtBakeVColorH,
				    txtBakeVColorClear,
				    txtBakeVColorBorder,
				    btnBakeVColor,

				    cmbResolution,
				    chkDisplayTransparent,
				    chkDisplayGrid,
				    chkDisplayFill,
				    chkDisplayBorder,
				    chkDisplayPoly,
                };

			    m_displayGroup = new System.Collections.Generic.Dictionary<Label,TableLayoutPanel>();
                m_displayGroup.Add(lblSelectedObject, tblSelectedObject);
                m_displayGroup.Add(lblMaterialEdit, tblMaterialEdit);
                m_displayGroup.Add(lblObjectTrans, tblObjectTrans);
			    m_displayGroup.Add(lblCmbTex, tblCmbTex);
			    m_displayGroup.Add(lblBakeVColor, tblBakeVColor);
			    System.EventHandler ehGroupTitleClick = new System.EventHandler(lblDisplayGoup_Click);
                foreach (System.Windows.Forms.Label ctrl in m_displayGroup.Keys)
                {
                    if (ctrl != null)
                    {
                        ctrl.Tag = ctrl.Visible;
                        ctrl.Click += ehGroupTitleClick;
                    }
                }

			    System.String add_tmp;
			    m_ctrlHint = new System.Collections.Generic.Dictionary<System.Windows.Forms.Control,System.String>();
                add_tmp = "メタセコイアで現在選択中のオブジェクト。";
                m_ctrlHint.Add(lblSelectedObject, add_tmp);
                m_ctrlHint.Add(lblCurObjName, add_tmp);
                m_ctrlHint.Add(chkRecursive, "チェックすると、選択中のオブジェクトの子階層のオブジェクトも変換対象に含めます。");
                m_ctrlHint.Add(lblMaterialEdit, "マテリアル情報ごとに変換するデータを編集します。");
                add_tmp = "編集するマテリアルを選択します。メタセコイアのマテリアルパネルでも選択状態を変更できます。";
                m_ctrlHint.Add(lblSelMat, add_tmp);
                m_ctrlHint.Add(cmbSelMat, add_tmp);
                m_ctrlHint.Add(chkSelMatTrans, "チェックすると、選択オブジェクトでこのマテリアルが割り当てられている面のUVが処理されます。チェックしない時は、マテリアルに変換データが設定されていても、変換時に無視されます。");
                m_ctrlHint.Add(btnMatClr, "このマテリアルに関する変換情報をクリアします。クリアされるとこのマテリアルは変換時に無視されます。");
                add_tmp = "設定すると、選択オブジェクトでこのマテリアルが割り当てられている面のマテリアル設定を別のマテリアル設定に変更します。";
                m_ctrlHint.Add(lblToMat, add_tmp);
                m_ctrlHint.Add(cmbToMat, add_tmp);
                m_ctrlHint.Add(btnToMatClr, "このマテリアルの「変換時のマテリアル変更」の設定をクリアします。");
                add_tmp = "UV変換時のスケールを設定します。直接入力も出来ますが、カンバス上でドラッグ＆ドロップ操作しても設定できます。";
			    m_ctrlHint.Add(lblScl,  add_tmp);
			    m_ctrlHint.Add(txtSclU, add_tmp);
			    m_ctrlHint.Add(txtSclV, add_tmp);
                add_tmp = "UV変換時の移動量を設定します。直接入力も出来ますが、カンバス上でドラッグ＆ドロップ操作しても設定できます。";
			    m_ctrlHint.Add(lblMov,  add_tmp);
			    m_ctrlHint.Add(txtMovU, add_tmp);
			    m_ctrlHint.Add(txtMovV, add_tmp);
			    add_tmp = "スケール・移動後のパディング（端からの余白）をセットします。";
			    m_ctrlHint.Add(lblPadding, add_tmp);
			    m_ctrlHint.Add(txtPadding, add_tmp);
                add_tmp = "編集したマテリアル変換データをもとにオブジェクトのUV情報を変換します。";
                m_ctrlHint.Add(lblObjectTrans, add_tmp);
                m_ctrlHint.Add(btnTrans, add_tmp);
                m_ctrlHint.Add(chkClone, "オブジェクトを複製してから変換結果を書き込みます。変換するオブジェクトが変換前のまま残るので安全です。");
			    m_ctrlHint.Add(lblCmbTex, "変換でマテリアル変更する時、元テクスチャを先テクスチャに転写してテクスチャを自動統合します(試験実装)");
			    m_ctrlHint.Add(chkCmbTex, "変換先マテリアルの模様テクスチャに元マテリアルの模様テクスチャをスケール移動して転写します（変換時のマテリアル変更が有効で、変更先マテリアルと元マテリアルに模様テクスチャが設定されている場合。対応形式*.bmp/*.png）");
			    m_ctrlHint.Add(chkCmbAlpha, "変換先マテリアルの透明テクスチャに元マテリアルの透明テクスチャをスケール移動して転写します（変換時のマテリアル変更が有効で、変更先マテリアルと元マテリアルに透明テクスチャが設定されている場合。対応形式*.bmp/*.png）");
			    m_ctrlHint.Add(chkCmbBump, "変換先マテリアルの凸凹テクスチャに元マテリアルの凸凹テクスチャをスケール移動して転写します（変換時のマテリアル変更が有効で、変更先マテリアルと元マテリアルに凸凹テクスチャが設定されている場合。対応形式*.bmp/*.png）");
			    add_tmp = "有効にすると最初に転写する時、元々あった画像を指定の色でクリアします。";
			    m_ctrlHint.Add(chkCmbTexClear, add_tmp);
			    m_ctrlHint.Add(chkCmbAlphaClear, add_tmp);
			    m_ctrlHint.Add(chkCmbBumpClear, add_tmp);
			    add_tmp = "クリアする色（ARGB）";
			    m_ctrlHint.Add(txtCmbTexClearColor, add_tmp);
			    m_ctrlHint.Add(txtCmbAlphaClearColor, add_tmp);
			    m_ctrlHint.Add(txtCmbBumpClearColor, add_tmp);
			    add_tmp = "生成する画像のサイズを指定します。(px単位)";
			    m_ctrlHint.Add(txtBakeVColorW, add_tmp);
			    m_ctrlHint.Add(txtBakeVColorH, add_tmp);
			    m_ctrlHint.Add(lblBakeVColorSize, add_tmp);
			    add_tmp = "下地色を指定します（ARGB）";
			    m_ctrlHint.Add(lblBakeVColorClear, add_tmp);
			    m_ctrlHint.Add(txtBakeVColorClear, add_tmp);
			    add_tmp = "0より大きい値をセットすると、三角形の輪郭を太らせます。";
			    m_ctrlHint.Add(lblBakeVColorBorder, add_tmp);
			    m_ctrlHint.Add(txtBakeVColorBorder, add_tmp);
                m_ctrlHint.Add(lblBakeVColor, "頂点色を焼き込みます。OpenGLが使用可能なPCで作動します。");
                m_ctrlHint.Add(btnCustomBake, "プログラム可能なカスタムベイク処理を起動します。");
                m_ctrlHint.Add(btnBakeVColor, "現在の設定で頂点色ベイク画像をファイルに保存します。");
			    System.EventHandler ehCtrl_TryDisplayHint = new System.EventHandler(ctrl_TryDisplayHint);
			    foreach ( System.Windows.Forms.Control ctrl in m_ctrlHint.Keys ) {
				    if ( ctrl != null ) ctrl.MouseHover += ehCtrl_TryDisplayHint;
				    System.Windows.Forms.TextBox txt = ctrl as System.Windows.Forms.TextBox;
				    if ( txt != null ) ctrl.Enter += ehCtrl_TryDisplayHint;
			    }
            }
            catch
            {
                base.Dispose(true);
                throw;
            }
        }

        //! @brief ディスプレイグループのVisibleにアクセスするためのクラスです。DisplayGroupsVisible["コントロール名"]でそれぞれのコントロールのVisibleプロパティを設定できます。
        protected class MenuGroupVisibleAccessor
        {
            public MenuGroupVisibleAccessor(MainEditForm_Base owner)
            {
                m_owner = owner;
            }
            //! @brief デフォルトプロパティを経由してそれぞれのコントロールのVisibleを取得,設定します。
            public bool this[System.String controlName]
            {
                get
                {
                    bool ret = false;
                    foreach (System.Windows.Forms.Control ctrl in m_owner.m_displayGroup.Keys)
                    {
                        if (ctrl.Name == controlName)
                        {
                            try { ret = (bool)(ctrl.Tag); }
                            catch { ctrl.Tag = ret = ctrl.Visible; }
                            break;
                        }
                    }
                    return ret;
                }
                set
                {
                    foreach (System.Windows.Forms.Control ctrl in m_owner.m_displayGroup.Keys)
                    {
                        if (ctrl.Name == controlName)
                        {
                            ctrl.Tag = ctrl.Visible = value;
                            break;
                        }
                    }
                }
            }
            MainEditForm_Base m_owner;
        }

        //! @brief メニューグループのVisibleを設定するためのアクセサを取得します。
        protected MenuGroupVisibleAccessor MenuGroupVisibles { get { return new MenuGroupVisibleAccessor(this); } }

        //! @brief 編集対象のパネルのEnabledを一括で設定します。
        protected bool tblEditEnabled
        {
            get { return m_tblEditEnabled; }
            set
            {
                if (m_tblEditEnabled != value)
                {
                    m_tblEditEnabled = value;
                    foreach (System.Windows.Forms.TableLayoutPanel tbl in m_displayGroup.Values)
                    {
                        if (tbl == tblMaterialEdit)
                        {
                            if (tblMaterialEdit.Enabled != (m_tblMaterialEditEnabled && value))
                                tblMaterialEdit.Enabled = (m_tblMaterialEditEnabled && value);
                        }
                        else tbl.Enabled = value;
                    }
                }
            }
        }

        //! @brief privateなtblMaterialEditコントロールへのEnabledだけ公開します。
        protected bool tblMaterialEditEnabled
        {
            get { return m_tblMaterialEditEnabled; }
            set
            {
                if (m_tblMaterialEditEnabled != value)
                {
                    m_tblMaterialEditEnabled = value;
                    if (tblMaterialEdit.Enabled != (m_tblEditEnabled && value))
                        tblMaterialEdit.Enabled = (m_tblEditEnabled && value);
                }
            }
        }

        //! @brief カンバスを閉じます。
        //! @param [in] canvasRestoreWidth リストアサイズを明示的に指定する時に0より大きい値を指定します。
        protected void CanvasClose(int canvasRestoreWidth)
        {
            if (m_canvasClosed == true) return;
            try
            {
                splitter.Panel1.SuspendLayout();
                if (canvasRestoreWidth <= 0) m_canvasRestoreWidth = (splitter.Width - splitter.Panel1.Width);
                pnlGL.GLPaintSuspend = true;
                m_canvasClosed = true;
                MinimumSize = System.Drawing.Size.Empty;
                if (canvasRestoreWidth <= 0) Width -= m_canvasRestoreWidth;
                else m_canvasRestoreWidth = canvasRestoreWidth;
                splitter.Panel2Collapsed = true;
                btnBakeVColor.Enabled = pnlGL.Visible = pnlGL.Enabled = false;
                btnCanvasSwitch.BackgroundImage = Properties.Resources.fold_right_h;
            }
            finally { splitter.Panel1.ResumeLayout(); }
        }

        //! @brief カンバスを開きます。
        protected void CanvasOpen()
        {
            if (m_canvasClosed == false)
            {
                m_canvasRestoreWidth = -1;
                return;
            }

            int registedPanel1Width = -1;
            try
            {
                splitter.Panel1.SuspendLayout();

                btnCanvasSwitch.BackgroundImage = Properties.Resources.fold_left_h;
                if (0 < m_canvasRestoreWidth)
                {
                    registedPanel1Width = splitter.Panel1.Width;
                    Width += m_canvasRestoreWidth;
                    splitter.Width += m_canvasRestoreWidth;
                }
                m_canvasClosed = false;
                if (0 < m_canvasRestoreWidth) splitter.SplitterDistance = registedPanel1Width;
                pnlGL.Enabled = pnlGL.Visible = btnBakeVColor.Enabled = true;
                splitter.Panel2Collapsed = false;
                pnlGL.GLPaintSuspend = false;
                m_canvasRestoreWidth = -1;

                splitter.Panel1MinSize = m_initialPanel1Width;
                MinimumSize = System.Drawing.Size.Empty;
            }
            finally { splitter.Panel1.ResumeLayout(); }
            pnlGL.Invalidate();
        }

        protected override void OnLayout(LayoutEventArgs levent)
        {
            base.OnLayout(levent);
            if (m_canvasClosed) splitter.SplitterDistance = splitter.Size.Width;
        }

        private bool m_tblEditEnabled;
        private bool m_tblMaterialEditEnabled;
        private bool m_consoleEnabled;
        private bool m_canvasClosed;
        private int m_canvasRestoreWidth;
        private int m_initialPanel1Width;
        private ExceptionCatcher m_exceptionCatcher;
        private System.Windows.Forms.Control[] m_TabControlList;
        private System.Collections.Generic.Dictionary<System.Windows.Forms.Label,System.Windows.Forms.TableLayoutPanel> m_displayGroup;
        private System.Collections.Generic.Dictionary<System.Windows.Forms.Control,System.String> m_ctrlHint;

        private void btnCanvasSwitch_Click(object sender, System.EventArgs e)
        {
            if (m_canvasClosed) CanvasOpen(); else CanvasClose(-1);
        }

        private void chkDisplayTransparent_CheckedChanged(object sender, System.EventArgs e)
        {
            if (chkDisplayTransparent.Checked) pnlGL.GLBackgroundImage = Properties.Resources.check2x2;
            else pnlGL.GLBackgroundImage = null;
        }

        private void ctrl_TryDisplayHint(object sender, System.EventArgs e)
        {
			if ( m_ctrlHint == null ) return;
			System.Windows.Forms.Control ctrl = sender as System.Windows.Forms.Control;
			if ((ctrl != null) && m_ctrlHint.ContainsKey(ctrl)) lblHint.Text = m_ctrlHint[ctrl].ToString();
		}

        private void dlg_KeyDown(object sender, KeyEventArgs e)
        {
            e.TabFocusMoveProxy(m_TabControlList);
        }

        private void dlg_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)System.Windows.Forms.Keys.Tab) e.Handled = true;
        }

        private static void FinalizedWithoutExplicitDispose(object sender, System.EventArgs e)
        {
            TextLogFile.AppendLine("warning : CounterKey - finalized without explicit dispose");
        }

        private void lblDisplayGoup_Click(object sender, System.EventArgs e)
        {
            System.Windows.Forms.Label lbl = sender as System.Windows.Forms.Label;
			if ((lbl != null) && m_displayGroup.ContainsKey(lbl))
            {
                try
                {
                    SuspendLayout();
                    bool set = !(m_displayGroup[lbl].Visible);
                    m_displayGroup[lbl].Visible = set;
                    lbl.Tag = set;
                }
                finally { ResumeLayout(); }
			}
        }

        private static void OverPop(object sender, System.EventArgs e)
        {
            TextLogFile.AppendLine("warning : CounterKey - over pop");
        }

        private static void UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
        {
            TextLogFile.AppendException(e.ExceptionObject);
        }

        private static void ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            TextLogFile.AppendException(e.Exception);
        }
    }

    //! @}
}
