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;
using System.Runtime.InteropServices;

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

        private const string IK_MOT_A = "motPlayer";
        private const string IK_MOT_UA = "motBox";
        private const string IK_MOT_CTRL = "motCtrl";
        private const string IK_MOT_DEF = "motDef";
        private const string IK_MOT_DEF_RUN = "motDefRun";
        private const string IK_MOT_CURV = "motCurv";
        private const string IK_MOT_CURV_DISABLE = "motCurvDisable";

        #endregion

        #region Public Delegate

        public delegate void SelectedMotionChangedEventHandler(Mix.Tool.Graphics.Motion motion);

        #endregion

        #region Private Member

        private Mix.Tool.Graphics.ObjectModel model = null;
        private TreeNode tnMixer = null;
        private TreeNode tnUnassigned = null;
        StringFormat nodeStrFmt = new StringFormat();

        #endregion

        #region Public Method

        /// <summary>
        /// 現在選択されているモーションの取得
        /// </summary>
        public Mix.Tool.Graphics.Motion SelectedMotion
        {
            get
            {
                TreeNode treeNode = this.tvMot.SelectedNode;
                if (treeNode == null)
                {
                    return null;
                }

                Mix.Tool.Graphics.Motion motion;

                if (treeNode.Tag is Mix.Tool.Graphics.Motion)
                {
                    motion = treeNode.Tag as Mix.Tool.Graphics.Motion;
                }
                else if (treeNode.Tag is Mix.Tool.Graphics.MotionCurve)
                {
                    motion = treeNode.Parent.Tag as Mix.Tool.Graphics.Motion;
                }
                else
                {
                    motion = null;
                }

                return motion;
            }
        }

        /// <summary>
        /// 選択モーション変更イベント
        /// </summary>
        public event SelectedMotionChangedEventHandler SelectedMotionChanged = null;

        /// <summary>
        /// モデルの設定
        /// </summary>
        public Mix.Tool.Graphics.ObjectModel Model
        {
            set
            {
                this.tnMixer = null;
                this.tnUnassigned = null;
                this.tvMot.Nodes.Clear();

                this.model = value;

                if (this.model != null)
                {
                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // ミキサー
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.tnMixer = this.tvMot.Nodes.Add(Properties.Resources.MOTION_MIXER_STR);
                    this.tnMixer.ImageKey = IK_MOT_A;
                    this.tnMixer.SelectedImageKey = IK_MOT_A;
                    this.tnMixer.Tag = null;

                    foreach (Mix.Tool.Graphics.MotionController motCtrl in this.model.MotionControllers)
                    {
                        AddMotionCtrlTreeNode(motCtrl);
                    }

                    this.tnMixer.Expand();

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // 未分類
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.tnUnassigned = this.tvMot.Nodes.Add(Properties.Resources.MOTION_UNA_STR);
                    this.tnUnassigned.ImageKey = IK_MOT_UA;
                    this.tnUnassigned.SelectedImageKey = IK_MOT_UA;
                    this.tnUnassigned.Tag = this.model.UnassignedMotionController;

                    AddMotionTreeNodes(this.tnUnassigned, this.model.UnassignedMotionController);

                    this.SetControlsEnabled(true);
                }
                else
                {
                    this.SetControlsEnabled(false);
                }
            }
        }

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

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

            this.nodeStrFmt.Alignment = StringAlignment.Near;
            this.nodeStrFmt.LineAlignment = StringAlignment.Center;
            this.nodeStrFmt.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.NoWrap;
            this.nodeStrFmt.Trimming = StringTrimming.None;

            this.imageList.Images.Add(IK_MOT_A, Properties.Resources.MotionPlayer);
            this.imageList.Images.Add(IK_MOT_UA, Properties.Resources.MotionBox);
            this.imageList.Images.Add(IK_MOT_CTRL, Properties.Resources.MotionController);
            this.imageList.Images.Add(IK_MOT_DEF, Properties.Resources.Motion);
            this.imageList.Images.Add(IK_MOT_DEF_RUN, Properties.Resources.Motion_Run);
            this.imageList.Images.Add(IK_MOT_CURV, Properties.Resources.MotionCurve);
            this.imageList.Images.Add(IK_MOT_CURV_DISABLE, Properties.Resources.MotionCurve_Disable);

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

            this.SetControlsEnabled(false);
        }

        /// <summary>
        /// ポーリング
        /// </summary>
        public void Poll()
        {
            if (this.tvMot.Nodes.Count > 0)
            {
                foreach (TreeNode treeNode in this.tvMot.Nodes)
                {
                    this.UpdateTree(treeNode);
                }
            }
        }

        #endregion

        #region Private Control Method

        private void MotionOutlinerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.model = null;
        }

        private void tvMot_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                TreeViewHitTestInfo htInfo = this.tvMot.HitTest(e.Location);

                if (htInfo.Location == TreeViewHitTestLocations.Image)
                {
                    if (htInfo.Node.Tag is Mix.Tool.Graphics.MotionCurve)
                    {
                        ////////////////////////////////////////////////////////////////////////////////////////////////////
                        // モーションカーブの有効設定
                        ////////////////////////////////////////////////////////////////////////////////////////////////////

                        Mix.Tool.Graphics.MotionCurve hitMot = htInfo.Node.Tag as Mix.Tool.Graphics.MotionCurve;
                        hitMot.Enabled = !hitMot.Enabled;

                        UpdateTreeNodeImage(htInfo.Node);

                        this.InvokeSelectedMotionChanged(e.Node);
                    }
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                if (e.Node.Bounds.Contains(e.Location) == true)
                {
                    this.tvMot.SelectedNode = e.Node;
                }
            }
        }

        private void tvMot_AfterSelect(object sender, TreeViewEventArgs e)
        {
            this.InvokeSelectedMotionChanged(e.Node);
        }

        private void tvMot_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (e.Node.Tag is Mix.Tool.Graphics.MotionController)
            {
                //モーションコントローラー
                Mix.Tool.Graphics.MotionController motCtrl = e.Node.Tag as Mix.Tool.Graphics.MotionController;
                if (motCtrl.Assigned == false)
                {
                    //未分類
                    e.CancelEdit = true;
                }
            }
            else if (e.Node.Tag is Mix.Tool.Graphics.Motion)
            {
                //モーション
                ;
            }
            else
            {
                e.CancelEdit = true;
            }
        }

        private void tvMot_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (e.Label == null)
            {
                return;
            }

            if ((e.Node.Tag is Mix.Tool.Graphics.MotionController) == true)
            {
                Mix.Tool.Graphics.MotionController motCtrl = e.Node.Tag as Mix.Tool.Graphics.MotionController;

                motCtrl.Name = e.Label;
                e.CancelEdit = true;
                e.Node.Text = motCtrl.Name;
            }
            else if ((e.Node.Tag is Mix.Tool.Graphics.Motion) == true)
            {
                Mix.Tool.Graphics.Motion mot = e.Node.Tag as Mix.Tool.Graphics.Motion;

                mot.Name = e.Label;
                e.CancelEdit = true;
                e.Node.Text = mot.Name;
            }
        }

        private void tvMot_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F2)
            {
                TreeNode treeNode = this.tvMot.SelectedNode;
                if (treeNode != null)
                {
                    treeNode.BeginEdit();
                }
            }
        }

        private void tvMot_ItemDrag(object sender, ItemDragEventArgs e)
        {
            //移動しようとしているノードを
            this.tvMot.SelectedNode = e.Item as TreeNode;
            this.tvMot.Focus();

            //ドラッグ開始
            this.tvMot.DoDragDrop(e.Item, DragDropEffects.All);
        }

        private void tvMot_DragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move)
                {
                    e.Effect = DragDropEffects.Move;
                }
                else
                {
                    e.Effect = DragDropEffects.None;
                }
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }

            if (e.Effect != DragDropEffects.None)
            {
                TreeNode tnTarget = this.tvMot.GetNodeAt(this.tvMot.PointToClient(new Point(e.X, e.Y)));
                TreeNode tnSource = e.Data.GetData(typeof(TreeNode)) as TreeNode;

                if (TestDragDrop(tnSource, tnTarget) == true)
                {
                    if (tnTarget.IsSelected == false)
                    {
                        this.tvMot.SelectedNode = tnTarget;
                    }
                }
                else
                {
                    e.Effect = DragDropEffects.None;
                }
            }
        }

        private void tvMot_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                TreeNode tnTarget = this.tvMot.GetNodeAt(this.tvMot.PointToClient(new Point(e.X, e.Y)));
                TreeNode tnSource = e.Data.GetData(typeof(TreeNode)) as TreeNode;

                if (TestDragDrop(tnSource, tnTarget) == true)
                {
                    if (tnSource.Tag is Mix.Tool.Graphics.MotionController)
                    {
                        //削除
                        this.tvMot.Nodes.Remove(tnSource);

                        //挿入
                        tnTarget.Parent.Nodes.Insert(tnTarget.Index, tnSource);
                        tnTarget.Parent.Expand();

                        //モーションコントローラーにプリオリティを割り振り、ソート
                        this.AssignMotionControllerPriority();
                    }
                    else if (tnSource.Tag is Mix.Tool.Graphics.Motion)
                    {
                        Mix.Tool.Graphics.Motion mot = tnSource.Tag as Mix.Tool.Graphics.Motion;
                        Mix.Tool.Graphics.MotionController sourceMotCtrl = tnSource.Parent.Tag as Mix.Tool.Graphics.MotionController;
                        Mix.Tool.Graphics.MotionController targetMotCtrl = tnTarget.Tag as Mix.Tool.Graphics.MotionController;

                        //削除
                        tnSource.Parent.Nodes.Remove(tnSource);
                        sourceMotCtrl.Motions.Remove(mot);

                        //追加
                        targetMotCtrl.Motions.Add(mot);
                        tnSource.Text = mot.Name;
                        tnTarget.Nodes.Add(tnSource);
                        tnTarget.Expand();

                        //移動したノードを選択
                        this.tvMot.SelectedNode = tnSource;
                    }
                }
                else
                {
                    e.Effect = DragDropEffects.None;
                }
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void tvMot_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {
            Mix.Tool.Graphics.MotionCurve motCurve = e.Node.Tag as Mix.Tool.Graphics.MotionCurve;

            if ((motCurve != null) &&
                (motCurve.Enabled == false))
            {
                e.Graphics.DrawString(e.Node.Text, this.Font, SystemBrushes.ControlDark, e.Bounds, this.nodeStrFmt);
            }
            else
            {
                e.Graphics.DrawString(e.Node.Text, this.Font, SystemBrushes.WindowText, e.Bounds, this.nodeStrFmt);
//                e.DrawDefault = true;
            }

//            e.Graphics.DrawRectangle(System.Drawing.Pens.Red, e.Bounds);
//            e.Graphics.DrawRectangle(System.Drawing.Pens.Blue, e.Node.Bounds);
        }

        private void ctxMenu_Opening(object sender, CancelEventArgs e)
        {
            Point pos = this.tvMot.PointToClient(System.Windows.Forms.Cursor.Position);
            TreeNode treeNode = this.tvMot.GetNodeAt(pos);

            if ((treeNode != null) &&
                (treeNode.Bounds.Contains(pos) == true))
            {
                if (treeNode.Tag != null)
                {
                    if (treeNode.Tag is Mix.Tool.Graphics.MotionController)
                    {
                        //モーションコントローラーノード
                        Mix.Tool.Graphics.MotionController motCtrl = treeNode.Tag as Mix.Tool.Graphics.MotionController;

                        if (motCtrl.Assigned == true)
                        {
                            //モーションコントローラー
                            this.cmAddCtrl.Enabled = false;
                            this.cmRemove.Enabled = true;
                            this.cmRename.Enabled = true;
                        }
                        else
                        {
                            //未分類のモーションコントローラー
                            e.Cancel = true;
                        }
                    }
                    else if (treeNode.Tag is Mix.Tool.Graphics.Motion)
                    {
                        //モーションノード
                        Mix.Tool.Graphics.MotionController motCtrl = treeNode.Parent.Tag as Mix.Tool.Graphics.MotionController;

                        this.cmAddCtrl.Enabled = false;
                        this.cmRemove.Enabled = motCtrl.Assigned; //未分類のモーションは削除できない
                        this.cmRename.Enabled = true;
                    }
                    else
                    {
                        //モーションカーブノード
                        e.Cancel = true;
                    }
                }
                else
                {
                    //コントローラーノード
                    this.cmAddCtrl.Enabled = true;
                    this.cmRemove.Enabled = false;
                    this.cmRename.Enabled = false;
                }
            }
            else
            {
                //マウスカーソルの位置にノードが存在しない
                e.Cancel = true;
            }
        }

        private void cmAddCtrl_Click(object sender, EventArgs e)
        {
            TreeNode treeNode = this.tvMot.SelectedNode;
            Mix.Tool.Graphics.MotionController motCtrl = this.model.MotionControllers.Add();

            this.AddMotionCtrlTreeNode(motCtrl);
            this.AssignMotionControllerPriority();
        }

        private void cmRemove_Click(object sender, EventArgs e)
        {
            TreeNode treeNode = this.tvMot.SelectedNode;

            if (treeNode.Tag is Mix.Tool.Graphics.MotionController)
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // モーションコントローラーの削除
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                Mix.Tool.Graphics.MotionController uaMotCtrl = this.model.UnassignedMotionController;
                Mix.Tool.Graphics.MotionController motCtrl = treeNode.Tag as Mix.Tool.Graphics.MotionController;

                //モーションが存在している場合は警告を出す
                if (motCtrl.Motions.Count > 0)
                {
                    if (Mix.Tool.MessageDialog.Show(Properties.Resources.MES_REMOVE_MOTION_CTRL_STR,
                        Properties.Resources.TITLE,
                        MessageDialogButtons.YesNo,
                        MessageDialogIcon.Exclamation) == DialogResult.No)
                    {
                        return;
                    }
                }

                //ノードを削除
                treeNode.Nodes.Clear();
                treeNode.Parent.Nodes.Remove(treeNode);

                while (motCtrl.Motions.Count > 0)
                {
                    Mix.Tool.Graphics.Motion motion = motCtrl.Motions[0];

                    //モーションコントローラーから削除
                    motCtrl.Motions.Remove(motion);

                    //未分類に追加
                    uaMotCtrl.Motions.Add(motion);
                    AddMotionTreeNode(this.tnUnassigned, motion);
                }

                //モデルからモーションコントローラーを削除
                this.model.MotionControllers.Remove(motCtrl);

                //解放
                motCtrl.Dispose();
            }
            else if (treeNode.Tag is Mix.Tool.Graphics.Motion)
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // モーションの削除
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                Mix.Tool.Graphics.MotionController uaMotCtrl = this.model.UnassignedMotionController;
                Mix.Tool.Graphics.MotionController motCtrl = treeNode.Parent.Tag as Mix.Tool.Graphics.MotionController;
                Mix.Tool.Graphics.Motion motion = treeNode.Tag as Mix.Tool.Graphics.Motion;

                //削除
                treeNode.Parent.Nodes.Remove(treeNode);
                motCtrl.Motions.Remove(motion);

                //未分類に追加
                uaMotCtrl.Motions.Add(motion);
                AddMotionTreeNode(this.tnUnassigned, motion);
            }
        }

        private void cmRename_Click(object sender, EventArgs e)
        {
            TreeNode treeNode = this.tvMot.SelectedNode;

            treeNode.BeginEdit();
        }

        #endregion

        #region Private Other Method

        private void SetControlsEnabled(bool state)
        {
            foreach (Control ctrl in this.Controls)
            {
                ctrl.Enabled = state;
            }
        }

        private void UpdateTree(TreeNode treeNode)
        {
            Mix.Tool.Graphics.Motion mot = treeNode.Tag as Mix.Tool.Graphics.Motion;

            if (mot != null)
            {
                if (treeNode.ImageKey.Length == 0)
                {
                    UpdateTreeNodeImage(treeNode);
                }
                else
                {
                    bool isPlay = mot.State == Mix.Tool.Graphics.MotionState.Play;

                    if (((treeNode.ImageKey.Equals(IK_MOT_DEF) == true) && (isPlay == true)) ||
                        ((treeNode.ImageKey.Equals(IK_MOT_DEF_RUN) == true) && (isPlay == false)))
                    {
                        UpdateTreeNodeImage(treeNode);
                    }
                }
            }

            foreach (TreeNode childTreeNode in treeNode.Nodes)
            {
                this.UpdateTree(childTreeNode);
            }
        }

        private void InvokeSelectedMotionChanged(TreeNode treeNode)
        {
            if (this.SelectedMotionChanged == null)
            {
                return;
            }

            Mix.Tool.Graphics.Motion selectedMot;

            if (treeNode.Tag is Mix.Tool.Graphics.Motion)
            {
                selectedMot = treeNode.Tag as Mix.Tool.Graphics.Motion;
            }
            else if (treeNode.Tag is Mix.Tool.Graphics.MotionCurve)
            {
                selectedMot = treeNode.Parent.Tag as Mix.Tool.Graphics.Motion;
            }
            else
            {
                selectedMot = null;
            }

            this.SelectedMotionChanged.Invoke(selectedMot);
        }

        private void AssignMotionControllerPriority()
        {
            uint priority = 0;

            for (int i = (this.tnMixer.Nodes.Count - 1); i >= 0; i--)
            {
                TreeNode tnMotCtrl = this.tnMixer.Nodes[i];
                Mix.Tool.Graphics.MotionController motCtrl = tnMotCtrl.Tag as Mix.Tool.Graphics.MotionController;

                motCtrl.Priority = priority++;
            }

            this.model.MotionControllers.Sort();
        }

        private static bool TestDragDrop(TreeNode tnSource, TreeNode tnTarget)
        {
            if ((tnTarget == null) ||
                (tnSource == tnTarget) ||
                (tnTarget.Nodes.Contains(tnSource) == true))
            {
                return false;
            }

            if ((tnSource.Tag is Mix.Tool.Graphics.MotionController) == true)
            {
                Mix.Tool.Graphics.MotionController srcMotCtrl = tnSource.Tag as Mix.Tool.Graphics.MotionController;

                if ((srcMotCtrl.Assigned == true) &&
                    ((tnTarget.Tag is Mix.Tool.Graphics.MotionController) == true))
                {
                    Mix.Tool.Graphics.MotionController targetMotCtrl = tnTarget.Tag as Mix.Tool.Graphics.MotionController;
                    if (targetMotCtrl.Assigned == true)
                    {
                        return true;
                    }
                }
            }
            else if ((tnSource.Tag is Mix.Tool.Graphics.Motion) == true)
            {
                if ((tnTarget.Tag is Mix.Tool.Graphics.MotionController) == true)
                {
                    return true;
                }
            }

            return false;
        }

        private void AddMotionCtrlTreeNode(Mix.Tool.Graphics.MotionController motCtrl)
        {
            TreeNode tnMotCtrl;

            tnMotCtrl = this.tnMixer.Nodes.Add(motCtrl.Name);
            tnMotCtrl.ImageKey = IK_MOT_CTRL;
            tnMotCtrl.SelectedImageKey = IK_MOT_CTRL;
            tnMotCtrl.Tag = motCtrl;

            AddMotionTreeNodes(tnMotCtrl, motCtrl);

            this.tnMixer.Expand();
        }

        private static void AddMotionTreeNodes(TreeNode tnMotCtrl, Mix.Tool.Graphics.MotionController motCtrl)
        {
            foreach (Mix.Tool.Graphics.Motion mot in motCtrl.Motions)
            {
                AddMotionTreeNode(tnMotCtrl, mot);
            }

            tnMotCtrl.Expand();
        }

        private static void AddMotionTreeNode(TreeNode tnMotCtrl, Mix.Tool.Graphics.Motion mot)
        {
            Mix.Tool.Graphics.MotionController motCtrl = tnMotCtrl.Tag as Mix.Tool.Graphics.MotionController;

            TreeNode tnMot;
            TreeNode tnCurve;

            motCtrl.Motions.Add(mot);

            tnMot = tnMotCtrl.Nodes.Add(mot.Name);
            tnMot.Tag = mot;

            UpdateTreeNodeImage(tnMot);

            foreach (Mix.Tool.Graphics.MotionCurve curve in mot.Curves)
            {
                tnCurve = tnMot.Nodes.Add(curve.LinkNodeName);
                tnCurve.Tag = curve;

                UpdateTreeNodeImage(tnCurve);
            }
        }

        private static void UpdateTreeNodeImage(TreeNode treeNode)
        {
            if (treeNode.Tag is Mix.Tool.Graphics.Motion)
            {
                Mix.Tool.Graphics.Motion tagMot = treeNode.Tag as Mix.Tool.Graphics.Motion;
                bool isPlay = tagMot.State == Mix.Tool.Graphics.MotionState.Play;

                treeNode.ImageKey = treeNode.SelectedImageKey = (isPlay == true) ? IK_MOT_DEF_RUN : IK_MOT_DEF;
            }
            else if (treeNode.Tag is Mix.Tool.Graphics.MotionCurve)
            {
                Mix.Tool.Graphics.MotionCurve tagMotCurve = treeNode.Tag as Mix.Tool.Graphics.MotionCurve;

                treeNode.ImageKey = treeNode.SelectedImageKey = (tagMotCurve.Enabled == true) ? IK_MOT_CURV : IK_MOT_CURV_DISABLE;
            }
        }

        #endregion
    }
}
