using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Mix.Tool.ModelEditor
{
    public partial class MotionEditorForm : Mix.Tool.Docking.DockContent
    {
        #region Private Constant

        private const string IK_PLAY = "play";
        private const string IK_PAUSE = "stop";
        private const string IK_LOOP = "loop";
        private const string IK_ONSHOT = "onshot";

        private const decimal NUD_BLEND_MIN = 0;
        private const decimal NUD_BLEND_MAX = 1.0m;
        private const int TB_BLEND_MIN = 0;
        private const int TB_BLEND_MAX = 100;

        #endregion

        #region Private Member

        private Mix.Tool.Graphics.Motion mot = null;
        private bool updateRTControlsEnabled = true;

        #endregion

        #region Public Method

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MotionEditorForm()
        {
            InitializeComponent();

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // 初期化
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            this.playImageList.Images.Add(IK_PLAY, Properties.Resources.MotionPlay);
            this.playImageList.Images.Add(IK_PAUSE, Properties.Resources.MotionPause);

            this.loopImageList.Images.Add(IK_LOOP, Properties.Resources.MotionLoop);
            this.loopImageList.Images.Add(IK_ONSHOT, Properties.Resources.MotionSingle);

            this.btCtrlPlay.ImageKey = IK_PLAY;
            this.btCtrlLoop.ImageKey = IK_LOOP;

            this.nudBlend.Minimum = NUD_BLEND_MIN;
            this.nudBlend.Maximum = NUD_BLEND_MAX;
            this.tbBlend.Minimum = TB_BLEND_MIN;
            this.tbBlend.Maximum = TB_BLEND_MAX;

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // すべてのコントロールを無効にする
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            this.SetControlsEnabled(false, false);
        }

        /// <summary>
        /// 編集するモーションの設定
        /// </summary>
        public Mix.Tool.Graphics.Motion Motion
        {
            set
            {
                bool differMotion = (this.mot != value);

                this.mot = value;

                if (this.mot != null)
                {
                    this.SetControlsEnabled(true, differMotion);
                }
                else
                {
                    this.SetControlsEnabled(false, false);
                }
            }
        }

        /// <summary>
        /// ポーリング
        /// </summary>
        public void Poll()
        {
            if ((this.mot == null) ||
                (this.mot.Assigned == false))
            {
                return;
            }

            bool isPlay = this.mot.State == Mix.Tool.Graphics.MotionState.Play;

            //再生ボタンのイメージ更新
            if (((this.btCtrlPlay.ImageKey == IK_PLAY) && (isPlay == true)) ||
                ((this.btCtrlPlay.ImageKey == IK_PAUSE) && (isPlay == false)))
            {
                this.UpdatePlayButton();
            }

            //カレントタイムの更新
            if (this.timeline.CurrentTime != this.mot.Time)
            {
                this.timeline.CurrentTime = this.mot.Time;
            }
        }

        #endregion

        #region Private Control Method

        private void timeline_ChangeCurrentTime(Timeline.ChangeTimeEventArgs e)
        {
            if ((this.mot == null) ||
                (this.mot.Assigned == false))
            {
                return;
            }

            if (e.Type == Timeline.ChangeTimeEventArgs.Types.Click)
            {
                this.mot.Time = e.Time;

                if (this.mot.State == Mix.Tool.Graphics.MotionState.Play)
                {
                    this.mot.Pause();
                }
            }

            if ((this.nudCtrlTime.Minimum <= this.mot.Time) &&
               (this.nudCtrlTime.Maximum >= this.mot.Time))
            {
                this.nudCtrlTime.Value = Convert.ToDecimal(this.mot.Time);
            }
        }

        private void btCtrlPlay_Click(object sender, EventArgs e)
        {
            if ((this.mot == null) ||
                (this.mot.Assigned == false))
            {
                return;
            }

            if (this.mot.State != Mix.Tool.Graphics.MotionState.Play)
            {
                if (this.mot.LastTime == this.mot.Time)
                {
                    this.mot.Time = 0;
                }

                this.mot.Play();
            }
            else if(this.mot.State == Mix.Tool.Graphics.MotionState.Play)
            {
                this.mot.Pause();
            }

            this.UpdatePlayButton();
        }

        private void btCtrlStop_Click(object sender, EventArgs e)
        {
            this.mot.Stop();
        }

        private void btCtrlTop_Click(object sender, EventArgs e)
        {
            if ((this.mot == null) ||
                (this.mot.Assigned == false))
            {
                return;
            }

            this.mot.Time = 0;
        }

        private void btCtrlLoop_Click(object sender, EventArgs e)
        {
            if ((this.mot == null) ||
                (this.mot.Assigned == false))
            {
                return;
            }

            this.mot.Loop = !this.mot.Loop;
            this.UpdateLoopButton();
        }

        private void nudCtrlTime_KeyDown(object sender, KeyEventArgs e)
        {
            if ((this.mot == null) ||
                (this.mot.Assigned == false))
            {
                return;
            }

            this.mot.Stop();
        }

        private void nudCtrlTime_ValueChanged(object sender, EventArgs e)
        {
            this.timeline.CurrentTime = Convert.ToInt32(this.nudCtrlTime.Value);
        }

        private void nudSpeed_ValueChanged(object sender, EventArgs e)
        {
            if (this.mot != null)
            {
                this.mot.Speed = (float)Convert.ToDouble(this.nudSpeed.Value);
            }
        }

        private void nudLoopStart_ValueChanged(object sender, EventArgs e)
        {
            if (this.mot == null)
            {
                return;
            }

            this.mot.LoopStartTime = Convert.ToInt32(this.nudLoopStart.Value);
            this.nudLoopStart.Value = Convert.ToDecimal(this.mot.LoopStartTime);
            this.timeline.LoopStartTime = this.mot.LoopStartTime;
        }

        private void nudLoopEnd_ValueChanged(object sender, EventArgs e)
        {
            if (this.mot == null)
            {
                return;
            }

            this.mot.LoopEndTime = Convert.ToInt32(this.nudLoopEnd.Value);
            this.nudLoopEnd.Value = Convert.ToDecimal(this.mot.LoopEndTime);
            this.timeline.LoopEndTime = this.mot.LoopEndTime;
        }

        private void cbRTRotation_CheckedChanged(object sender, EventArgs e)
        {
            this.UpdateRootTransformFlags();
        }

        private void cbRTDirectionX_CheckedChanged(object sender, EventArgs e)
        {
            this.UpdateRootTransformFlags();
        }

        private void cbRTDirectionY_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void cbRTDirectionZ_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void cbRTTranslationXZ_CheckedChanged(object sender, EventArgs e)
        {
            this.UpdateRootTransformFlags();
        }

        private void cbRTTranslationY_CheckedChanged(object sender, EventArgs e)
        {
            this.UpdateRootTransformFlags();
        }

        private void tbBlend_ValueChanged(object sender, EventArgs e)
        {
            decimal dRatio = Convert.ToDecimal((float)this.tbBlend.Value / (float)TB_BLEND_MAX);

            if (NUD_BLEND_MIN > dRatio)
            {
                dRatio = NUD_BLEND_MIN;
            }
            else if (NUD_BLEND_MAX < dRatio)
            {
                dRatio = NUD_BLEND_MAX;
            }

            this.nudBlend.Value = dRatio;
        }

        private void nudBlend_ValueChanged(object sender, EventArgs e)
        {
            if (this.mot == null)
            {
                return;
            }

            float fRatio = (float)this.nudBlend.Value;
            int iRatio = (int)(fRatio * (float)TB_BLEND_MAX);

            if (this.tbBlend.Minimum > iRatio)
            {
                iRatio = this.tbBlend.Minimum;
            }
            else if (this.tbBlend.Maximum < iRatio)
            {
                iRatio = this.tbBlend.Maximum;
            }

            this.mot.BlendRatio = fRatio;
            this.tbBlend.Value = iRatio;
        }

        private void cbOutputNoInclude_CheckedChanged(object sender, EventArgs e)
        {
            if (this.mot != null)
            {
                this.mot.Default = this.cbOutputNoInclude.Checked == false;
                this.UpdateOutputControls();
            }
        }

        private void btOutputFilePath_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog dlg = new SaveFileDialog())
            {
                if (this.tbOutputFilePath.Text.Length > 0)
                {
                    try
                    {
                        dlg.InitialDirectory = System.IO.Path.GetDirectoryName(this.tbOutputFilePath.Text);
                    }
                    catch
                    {
                        dlg.InitialDirectory = System.IO.Path.GetPathRoot(this.tbOutputFilePath.Text);
                    }

                    dlg.FileName = System.IO.Path.GetFileName(this.tbOutputFilePath.Text);
                }
                else
                {
                    dlg.InitialDirectory = "";
                    dlg.FileName = this.mot.Name + Properties.Resources.FILE_EXT_MOT;
                }

                dlg.Filter = Properties.Resources.FILE_FILTER_MOT;
                dlg.FilterIndex = 1;

                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    this.tbOutputFilePath.Text = dlg.FileName;
                    this.mot.SaveFilePath = dlg.FileName;
                }
            }
        }

        #endregion

        #region Private Other Method

        private void SetControlsEnabled(bool state, bool differ)
        {
            if (state == true)
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // タイトル
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                this.Text = Properties.Resources.TITLE_MOTION_EDITOR + " (" + this.mot.Name + ")";

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 前回と異なるモーションが設定された場合のもの
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                if (differ == true)
                {
                    decimal loopStartTime = Convert.ToDecimal(this.mot.LoopStartTime);
                    decimal loopEndTime = Convert.ToDecimal(this.mot.LoopEndTime);

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // タイムライン
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.timeline.LastTime = this.mot.LastTime;
                    this.timeline.CurrentTime = this.mot.Time;
                    this.timeline.LoopStartTime = this.mot.LoopStartTime;
                    this.timeline.LoopEndTime = this.mot.LoopEndTime;
                    this.timeline.CenteringCurrentTime();

                    this.UpdatePlayButton();
                    this.UpdateLoopButton();

                    this.nudCtrlTime.Minimum = 0;
                    this.nudCtrlTime.Maximum = this.mot.LastTime;
                    this.nudCtrlTime.Value = this.mot.Time;

                    this.nudSpeed.Value = Convert.ToDecimal(this.mot.Speed);

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // 基本
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.partBasic.Enabled = true;

                    this.lbLoopRange.Enabled = true;
                    this.tlpLoopRange.Enabled = true;
                    this.nudLoopStart.Minimum = 0;
                    this.nudLoopStart.Maximum = this.mot.LastTime;
                    this.nudLoopEnd.Minimum = 0;
                    this.nudLoopEnd.Maximum = this.mot.LastTime;
                    this.nudLoopStart.Value = loopStartTime;
                    this.nudLoopEnd.Value = loopEndTime;

                    this.lbSpeed.Enabled = true;
                    this.tlpSpeed.Enabled = true;
                    this.nudSpeed.Value = Convert.ToDecimal(this.mot.Speed);
                    this.lbBlend.Enabled = true;
                    this.tlpBlend.Enabled = true;
                    this.nudBlend.Value = Convert.ToDecimal(this.mot.BlendRatio);

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // その他
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.partOther.Enabled = true;

                    this.UpdateOutputControls();
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // タムライン
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                this.timeline.Enabled = this.mot.Assigned;

                this.btCtrlPlay.Enabled = this.mot.Assigned;
                this.btCtrlStop.Enabled = this.mot.Assigned;
                this.btCtrlTop.Enabled = this.mot.Assigned;
                this.btCtrlLoop.Enabled = this.mot.Assigned;

                this.nudCtrlTime.Enabled = this.mot.Assigned;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // ルートトランスフォーム
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                Mix.Tool.Graphics.RootTransformFlags rtFlags = this.mot.RootTransform;

                this.BeginInitRootTransformControls();
                this.cbRTRotation.Checked = (rtFlags & Mix.Tool.Graphics.RootTransformFlags.Rotation) == Mix.Tool.Graphics.RootTransformFlags.Rotation;
                this.cbRTTranslationXZ.Checked = (rtFlags & Mix.Tool.Graphics.RootTransformFlags.TranslationXZ) == Mix.Tool.Graphics.RootTransformFlags.TranslationXZ;
                this.cbRTTranslationY.Checked = (rtFlags & Mix.Tool.Graphics.RootTransformFlags.TranslationY) == Mix.Tool.Graphics.RootTransformFlags.TranslationY;
                this.EndInitRootTransformControls();

                this.partRootTransform.Enabled = true;
                this.cbRTRotation.Enabled = true;
                this.cbRTTranslationXZ.Enabled = true;
                this.cbRTTranslationY.Enabled = true;

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // その他
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                this.cbOutputNoInclude.Enabled = this.mot.Assigned;
            }
            else
            {
                this.Text = Properties.Resources.TITLE_MOTION_EDITOR;

                foreach (Control ctrl in this.Controls)
                {
                    ctrl.Enabled = state;
                }
            }
        }

        private void UpdatePlayButton()
        {
            switch (this.mot.State)
            {
                case Mix.Tool.Graphics.MotionState.Play:
                    this.btCtrlPlay.ImageKey = IK_PAUSE;
                    break;
                case Mix.Tool.Graphics.MotionState.Pause:
                case Mix.Tool.Graphics.MotionState.Stop:
                    this.btCtrlPlay.ImageKey = IK_PLAY;
                    break;
            }

            this.btCtrlStop.Enabled = this.mot.State != Mix.Tool.Graphics.MotionState.Stop;
        }

        private void UpdateLoopButton()
        {
            this.btCtrlLoop.ImageKey = (this.mot.Loop == true) ? IK_LOOP : IK_ONSHOT;
        }

        private void BeginInitRootTransformControls()
        {
            this.updateRTControlsEnabled = false;
        }

        private void EndInitRootTransformControls()
        {
            this.updateRTControlsEnabled = true;
        }

        private void UpdateRootTransformFlags()
        {
            if (this.updateRTControlsEnabled == false)
            {
                return;
            }

            Mix.Tool.Graphics.RootTransformFlags flags = Mix.Tool.Graphics.RootTransformFlags.Init;

            if (this.cbRTRotation.Checked == true) { flags |= Mix.Tool.Graphics.RootTransformFlags.Rotation; }
            if (this.cbRTTranslationXZ.Checked == true) { flags |= Mix.Tool.Graphics.RootTransformFlags.TranslationXZ; }
            if (this.cbRTTranslationY.Checked == true) { flags |= Mix.Tool.Graphics.RootTransformFlags.TranslationY; }

            this.mot.RootTransform = flags;
        }

        private void UpdateOutputControls()
        {
            bool enabled = this.mot.Default == false;

            this.lbOutputFilePath.Enabled = enabled;
            this.tbOutputFilePath.Enabled = enabled;
            this.btOutputFilePath.Enabled = enabled;

            this.cbOutputNoInclude.Checked = enabled;
            this.tbOutputFilePath.Text = this.mot.SaveFilePath;
        }

        #endregion
    }
}
