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.Drawing.Drawing2D;
using System.Windows.Forms.VisualStyles;

namespace Mix.Tool.ModelEditor
{
    public partial class OutlinerForm : Mix.Tool.Docking.DockContent
    {
        const string IK_NODE_MODEL = "model";
        const string IK_NODE_EMPTY = "empty";
        const string IK_NODE_MESH = "mesh";
        const string IK_NODE_BONE = "bone";
        const string IK_NODE_MESH_DISABLE = "mesh_disable";

        private Mix.Tool.Graphics.Model model = null;
        StringFormat tvNodeTextFormat = new StringFormat();
        private bool nodeDoubleClick = false;

        /// <summary>
        /// オブジェクトイベントデータクラス
        /// </summary>
        public class ObjectEventArg : EventArgs
        {
            private object obj = null;

            public ObjectEventArg(object _obj)
            {
                this.obj = _obj;
            }

            /// <summary>
            /// ノードの取得
            /// </summary>
            public object Object
            {
                get { return this.obj; }
            }
        };

        /// <summary>
        /// オブジェクトイベントハンドラー
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void ObjectEventHandler(object sender, ObjectEventArg e);

        /// <summary>
        /// オブジェクト選択変更イベント
        /// </summary>
        public event ObjectEventHandler SelectedObjectChanged = null;

        /// <summary>
        /// ソースの取得、または設定
        /// </summary>
        public Mix.Tool.Graphics.Model Source
        {
            set
            {
                this.tvNode.Nodes.Clear();

                this.model = value;

                if (this.model != null)
                {
                    this.CreateTree();

                    if (this.tvNode.Nodes.Count > 0)
                    {
                        if (this.model.SelectedNode != null)
                        {
                            TreeNode selectedNode = this.FindTreeNode(this.tvNode.Nodes[0], this.model.SelectedNode);
                            if (selectedNode != null)
                            {
                                this.tvNode.SelectedNode = selectedNode;
                            }
                            else
                            {
                                this.tvNode.SelectedNode = this.tvNode.Nodes[0];
                            }
                        }
                        else
                        {
                            this.tvNode.SelectedNode = this.tvNode.Nodes[0];
                        }
                    }

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

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

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

            this.tvNodeTextFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
            this.tvNodeTextFormat.Alignment = StringAlignment.Near;
            this.tvNodeTextFormat.LineAlignment = StringAlignment.Center;

            this.imageList.Images.Add(IK_NODE_MODEL, Properties.Resources.Node_Model);
            this.imageList.Images.Add(IK_NODE_EMPTY, Properties.Resources.Node_Empty);
            this.imageList.Images.Add(IK_NODE_MESH, Properties.Resources.Node_Mesh);
            this.imageList.Images.Add(IK_NODE_BONE, Properties.Resources.Node_Bone);
            this.imageList.Images.Add(IK_NODE_MESH_DISABLE, Properties.Resources.Node_Mesh_Disable);

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

            this.SetControlsEnabled(false);
        }

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

        /// <summary>
        /// 選択オブジェクト変更イベントを要求
        /// </summary>
        public void RequestSelectedObjectChangedEvent()
        {
            this.NotifySelectedObjectChanged(this.tvNode.SelectedNode);
        }

        /// <summary>
        /// ノードの選択状態を更新
        /// </summary>
        public void UpdateSelectedNode()
        {
            if (this.tvNode.Nodes.Count > 0)
            {
                if (this.model.SelectedNode != null)
                {
                    //ツリーをトラバースして選択状態にする
                    foreach (TreeNode treeNode in this.tvNode.Nodes)
                    {
                        this.UpdateSelectedNode(treeNode, this.model.SelectedNode);
                    }
                }
                else
                {
                    //ノードは選択されていないのでモデル(ルート)を選択状態にする
                    TreeNode rootNode = this.tvNode.Nodes[0];

                    this.UpdateTreeNodeImage(rootNode);
                    this.tvNode.SelectedNode = rootNode;
                }
            }
        }

        #region tvNode Method

        private void tvNode_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            TreeViewHitTestInfo htInfo = this.tvNode.HitTest(e.Location);

            if (e.Button == MouseButtons.Left)
            {
                if (htInfo.Location == TreeViewHitTestLocations.Image)
                {
                    if (htInfo.Node.Tag is Mix.Tool.Graphics.Node)
                    {
                        ////////////////////////////////////////////////////////////////////////////////////////////////////
                        // メッシュの表示、非表示
                        ////////////////////////////////////////////////////////////////////////////////////////////////////

                        if (htInfo.Node.Tag is Mix.Tool.Graphics.MeshNode)
                        {
                            Mix.Tool.Graphics.MeshNode meshNode = htInfo.Node.Tag as Mix.Tool.Graphics.MeshNode;

                            meshNode.Visible = (meshNode.Visible == true) ? false : true;
                            this.UpdateTreeNodeImage(htInfo.Node);
                        }
                    }
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 右クリックで選択可能にする( コンテキストメニュー用 )
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                if ((htInfo.Location == TreeViewHitTestLocations.Label) ||
                    (htInfo.Location == TreeViewHitTestLocations.Image))
                {
                    this.tvNode.SelectedNode = e.Node;
                }
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // ノード選択イベント
            ////////////////////////////////////////////////////////////////////////////////////////////////////
/*
            if ((htInfo.Location == TreeViewHitTestLocations.Label) ||
                (htInfo.Location == TreeViewHitTestLocations.Image))
            {
                this.NotifyNodeSelected(e.Node);
            }
*/        }

        private void tvNode_AfterSelect(object sender, TreeViewEventArgs e)
        {
            this.NotifySelectedObjectChanged(e.Node);
        }

        private void tvNode_MouseDown(object sender, MouseEventArgs e)
        {
            if ((e.Button == MouseButtons.Left) &&
                (e.Clicks == 2))
            {
                this.nodeDoubleClick = true;
            }
            else
            {
                this.nodeDoubleClick = false;
            }
        }

        private void tvNode_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            if (this.nodeDoubleClick == true)
            {
                this.nodeDoubleClick = false;
                e.Cancel = true;
            }
        }

        private void tvNode_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            if (this.nodeDoubleClick == true)
            {
                this.nodeDoubleClick = false;
                e.Cancel = true;
            }
        }

        private void tvNode_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {
            System.Drawing.Brush textBrush = null;

            if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
            {
                if ((e.State & TreeNodeStates.Focused) == TreeNodeStates.Focused)
                {
                    textBrush = System.Drawing.SystemBrushes.HighlightText;
                }
                else
                {
                    textBrush = System.Drawing.SystemBrushes.WindowText;
                }
            }
            else
            {
                textBrush = System.Drawing.SystemBrushes.WindowText;
            }

            e.Graphics.DrawString(e.Node.Text, tvNode.Font, textBrush, e.Bounds, tvNodeTextFormat);
        }

        #endregion

        #region Misc Method

        private TreeNode FindTreeNode(TreeNode treeNode, Mix.Tool.Graphics.Node node)
        {
            if (treeNode.Tag == node)
            {
                return treeNode;
            }

            TreeNode retTreeNode = null;

            foreach (TreeNode childTreeNode in treeNode.Nodes)
            {
                retTreeNode = FindTreeNode(childTreeNode, node);
                if (retTreeNode != null)
                {
                    break;
                }
            }

            return retTreeNode;
        }

        private void UpdateSelectedNode(TreeNode treeNode, Mix.Tool.Graphics.Node node)
        {
            if (treeNode.Tag is Mix.Tool.Graphics.MeshNode)
            {
                Mix.Tool.Graphics.Node compNode = treeNode.Tag as Mix.Tool.Graphics.Node;

                if (compNode == node)
                {
                    this.UpdateTreeNodeImage(treeNode);
                    this.tvNode.SelectedNode = treeNode;
                    return;
                }
            }

            foreach (TreeNode childTreeNode in treeNode.Nodes)
            {
                this.UpdateSelectedNode(childTreeNode, node);
            }
        }

        private void NotifySelectedObjectChanged(TreeNode treeNode)
        {
            if ((treeNode != null) &&
                (treeNode.Tag != null) &&
                (this.SelectedObjectChanged != null))
            {
                this.SelectedObjectChanged(this, new ObjectEventArg(treeNode.Tag));
            }
        }

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

        private void CreateTree()
        {
            TreeNode treeNode;

            treeNode = this.tvNode.Nodes.Add(this.model.Name);
            treeNode.ImageKey = treeNode.SelectedImageKey = IK_NODE_MODEL;
            treeNode.Tag = this.model;

            this.CreateTree(treeNode, this.model.RootNode);

            treeNode.Expand();
        }

        private void CreateTree(TreeNode parentTreeNode, Mix.Tool.Graphics.Node node)
        {
            TreeNode treeNode;

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // ノードのツリーノードを追加
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            if (parentTreeNode != null)
            {
                treeNode = parentTreeNode.Nodes.Add(node.Name);
                treeNode.Tag = node;
            }
            else
            {
                treeNode = this.tvNode.Nodes.Add(node.Name);
                treeNode.Tag = node;
            }

            this.UpdateTreeNodeImage(treeNode);

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // 子ノードを処理
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            foreach (Mix.Tool.Graphics.Node childNode in node.Childs)
            {
                this.CreateTree(treeNode, childNode);
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // ツリーノードを開く
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            treeNode.Expand();
        }

        private void UpdateTreeNodeImage(TreeNode treeNode)
        {
            if (treeNode.Tag is Mix.Tool.Graphics.Node)
            {
                //ノード
                Mix.Tool.Graphics.Node node = treeNode.Tag as Mix.Tool.Graphics.Node;

                if (node is Mix.Tool.Graphics.EmptyNode)
                {
                    //エンプティ
                    treeNode.ImageKey = treeNode.SelectedImageKey = IK_NODE_EMPTY;
                }
                else if (node is Mix.Tool.Graphics.MeshNode)
                {
                    //メッシュ
                    Mix.Tool.Graphics.MeshNode meshNode = node as Mix.Tool.Graphics.MeshNode;
                    treeNode.ImageKey = treeNode.SelectedImageKey = (meshNode.Visible == true) ? IK_NODE_MESH : IK_NODE_MESH_DISABLE;
                }
                else if (node is Mix.Tool.Graphics.BoneNode)
                {
                    //ボーン
                    treeNode.ImageKey = treeNode.SelectedImageKey = IK_NODE_BONE;
                }
            }
        }

        #endregion
    }
}
