using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;


namespace UniHumanoid
{
    class BoneNode : IEnumerable<BoneNode>
    {
        public HumanBodyBones Bone { get; private set; }

        public List<BoneNode> Children = new List<BoneNode>();

        public int[] Muscles;

        public BoneNode(HumanBodyBones bone, params int[] muscles)
        {
            Bone = bone;
            Muscles = muscles;
        }

        public IEnumerator<BoneNode> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        public void Add(BoneNode child)
        {
            Children.Add(child);
        }
    }

    class BoneTreeViewItem : TreeViewItem
    {
        //HumanBodyBones m_bone;

        public BoneTreeViewItem(int id, int depth, HumanBodyBones bone) : base(id, depth, bone.ToString())
        {
            //m_bone = bone;
        }
    }

    class MuscleTreeViewItem : TreeViewItem
    {
        public int Muscle
        {
            get;
            private set;
        }

        public MuscleTreeViewItem(int id, int depth, int muscle) : base(id, depth, HumanTrait.MuscleName[muscle])
        {
            Muscle = muscle;
        }
    }

    class BoneTreeView : TreeView
    {
        static BoneNode Skeleton = new BoneNode(HumanBodyBones.Hips)
        {
            new BoneNode(HumanBodyBones.Spine, 0, 1, 2){
                new BoneNode(HumanBodyBones.Chest, 3, 4, 5){
                    new BoneNode(HumanBodyBones.UpperChest, 6, 7, 8){
                        new BoneNode(HumanBodyBones.Neck, 9, 10, 11){
                            new BoneNode(HumanBodyBones.Head, 12, 13, 14){
                                new BoneNode(HumanBodyBones.LeftEye, 15, 16),
                                new BoneNode(HumanBodyBones.RightEye, 17, 18)
                            }
                        },
                        new BoneNode(HumanBodyBones.LeftShoulder, 37, 38){
                            new BoneNode(HumanBodyBones.LeftUpperArm, 39, 40, 41){
                                new BoneNode(HumanBodyBones.LeftLowerArm, 42, 43){
                                    new BoneNode(HumanBodyBones.LeftHand, 44, 45)
                                }
                            }
                        },
                        new BoneNode(HumanBodyBones.RightShoulder, 46, 47){
                            new BoneNode(HumanBodyBones.RightUpperArm, 48, 49, 50){
                                new BoneNode(HumanBodyBones.RightLowerArm, 51, 52){
                                    new BoneNode(HumanBodyBones.RightHand, 53, 54)
                                }
                            }
                        }
                    }
                }
            },
            new BoneNode(HumanBodyBones.LeftUpperLeg, 21, 22, 23){
                new BoneNode(HumanBodyBones.LeftLowerLeg, 24, 25){
                    new BoneNode(HumanBodyBones.LeftFoot, 26, 27){
                        new BoneNode(HumanBodyBones.LeftToes, 28)
                    }
                }
            },
            new BoneNode(HumanBodyBones.RightUpperLeg, 29, 30, 31){
                new BoneNode(HumanBodyBones.RightLowerLeg, 32, 33){
                    new BoneNode(HumanBodyBones.RightFoot, 34, 35){
                        new BoneNode(HumanBodyBones.RightToes, 36)
                    }
                }
            }
        };

        //Animator m_animator;
        HumanPoseHandler m_handler;
        HumanPose m_pose;

        bool m_updated;

        public void Begin()
        {
            m_handler.GetHumanPose(ref m_pose);
        }

        public void End()
        {
            if (m_updated)
            {
                m_handler.SetHumanPose(ref m_pose);
            }
            m_updated = false;
        }

        public BoneTreeView(TreeViewState treeViewState, MultiColumnHeader header, HumanPoseHandler handler)
            : base(treeViewState, header)
        {
            m_handler = handler;
            Reload();
        }

        protected override TreeViewItem BuildRoot()
        {
            return new TreeViewItem { id = 0, depth = -1 };
        }

        protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
        {
            var rows = GetRows() ?? new List<TreeViewItem>(200);

            // We use the GameObject instanceIDs as ids for items as we want to 
            // select the game objects and not the transform components.
            rows.Clear();

            var item = CreateTreeViewItemForBone(HumanBodyBones.Hips);
            root.AddChild(item);
            rows.Add(item);

            if (IsExpanded(item.id))
            {
                AddChildrenRecursive(Skeleton, item, rows);
            }
            else
            {
                item.children = CreateChildListForCollapsedParent();
            }

            SetupDepthsFromParentsAndChildren(root);

            return rows;
        }

        void AddChildrenRecursive(BoneNode bone, TreeViewItem item, IList<TreeViewItem> rows)
        {
            int childCount = bone.Children.Count;
            item.children = new List<TreeViewItem>(childCount);
            if (bone.Muscles != null)
            {
                foreach (var muscle in bone.Muscles)
                {
                    var childItem = new MuscleTreeViewItem(muscle + 20000, -1, muscle);
                    item.AddChild(childItem);
                    rows.Add(childItem);
                }
            }

            foreach (var child in bone.Children)
            {
                var childItem = CreateTreeViewItemForBone(child.Bone);
                item.AddChild(childItem);
                rows.Add(childItem);

                //if (child.Children.Count > 0)
                {
                    if (IsExpanded(childItem.id))
                    {
                        AddChildrenRecursive(child, childItem, rows);
                    }
                    else
                    {
                        childItem.children = CreateChildListForCollapsedParent();
                    }
                }
            }
        }

        static TreeViewItem CreateTreeViewItemForBone(HumanBodyBones bone)
        {
            return new TreeViewItem((int)bone, -1, Enum.GetName(typeof(HumanBodyBones), bone));
        }

        protected override void RowGUI(RowGUIArgs args)
        {
            for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
            {
                CellGUI(args.GetCellRect(i), args.GetColumn(i), ref args);
            }
        }

        void CellGUI(Rect cellRect, int index, ref RowGUIArgs args)
        {
            // Center cell rect vertically (makes it easier to place controls, icons etc in the cells)
            CenterRectUsingSingleLineHeight(ref cellRect);

            switch (index)
            {
                case 0:
                    {
                        // Default icon and label
                        args.rowRect = cellRect;
                        base.RowGUI(args);
                    }
                    break;

                case 1:
                    {
                        var muscleItem = args.item as MuscleTreeViewItem;
                        if (muscleItem != null)
                        {
                            var muscleIndex = muscleItem.Muscle;
                            var muscles = m_pose.muscles;
                            var value = EditorGUI.Slider(cellRect, GUIContent.none, muscles[muscleIndex], -1f, 1f);
                            if (value != muscles[muscleIndex])
                            {
                                muscles[muscleIndex] = value;
                                m_updated = true;
                            }
                        }
                        else
                        {

                        }
                    }
                    break;
            }
        }

        public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState()
        {
            var columns = new[]
            {
                new MultiColumnHeaderState.Column
                {
                    headerContent = new GUIContent("Name"),
                    headerTextAlignment = TextAlignment.Left,
                    width = 250,
                    minWidth = 60,
                    autoResize = false,
                    allowToggleVisibility = false
                },
                new MultiColumnHeaderState.Column
                {
                    headerContent = new GUIContent("Muscle value"),
                    headerTextAlignment = TextAlignment.Left,
                    width = 110,
                    minWidth = 60,
                    autoResize = true
                },
            };
            return new MultiColumnHeaderState(columns);
        }
    }


    [CustomEditor(typeof(MuscleInspector))]
    public class MuscleInspectorEditor : Editor
    {
        [NonSerialized] bool m_Initialized;
        [SerializeField] TreeViewState m_TreeViewState; // Serialized in the window layout file so it survives assembly reloading
        //[SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState;
        SearchField m_SearchField;
        BoneTreeView m_TreeView;

        MuscleInspector m_target;
        HumanPoseHandler m_handler;


        MultiColumnHeader GetHeaderState()
        {
            //bool firstInit = m_MultiColumnHeaderState == null;

            var headerState = BoneTreeView.CreateDefaultMultiColumnHeaderState();
            /*
            if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_MultiColumnHeaderState, headerState))
            {
                MultiColumnHeaderState.OverwriteSerializedFields(m_MultiColumnHeaderState, headerState);
            }
            m_MultiColumnHeaderState = headerState;
            */
            var multiColumnHeader = new MultiColumnHeader(headerState);
            multiColumnHeader.ResizeToFit();
            return multiColumnHeader;
        }

        void OnEnable()
        {
            var mi = this.target as MuscleInspector;
            var animator = mi.GetComponent<Animator>();
            if (animator != null
            && animator.avatar != null
            && animator.avatar.isValid
            && animator.avatar.isHuman
            )
            {
                Debug.LogFormat("MuscleInspectorEditor.OnEnable");
                m_handler = new HumanPoseHandler(animator.avatar, animator.transform);

                m_TreeView = new BoneTreeView(new TreeViewState(), GetHeaderState(), m_handler);
            }
        }

        void OnDisable()
        {
            if (m_handler != null)
            {
                m_handler.Dispose();
                m_handler = null;
            }
        }

        public override void OnInspectorGUI()
        {
            if (m_TreeView == null)
            {
                EditorGUILayout.HelpBox("Animator required", MessageType.Error);
                return;
            }

            var rect = GUILayoutUtility.GetRect(0, 10000, 0, m_TreeView.totalHeight);
            m_TreeView.Begin();
            m_TreeView.OnGUI(rect);
            m_TreeView.End();
        }
    }
}
