using System.Collections.Generic;
using UnityEngine;
using UnityEditor.IMGUI.Controls;
using System;
using System.Linq;
using UnityEngine.Experimental.U2D.Animation;
using UnityEngine.UIElements;

namespace UnityEditor.U2D.Animation
{
    internal interface ISpriteVisibilityToolModel
    {
        ISpriteVisibilityToolView view { get; }
        CharacterCache character { get; }
        bool previousVisibility { get; set; }
        bool allVisibility { get; set; }
        SkinningMode mode { get; }
        bool hasCharacter { get; }
        UndoScope UndoScope(string description);
        SpriteCache selectedSprite { get; }
        SpriteCategoryListCacheObject spriteCategoryList { get; }
    }

    internal interface ISpriteVisibilityToolView
    {
        void Setup();
        void SetSelection(SpriteCache sprite);
        void SetSelectionIds(IList<int> selectedIds);
    }

    internal class SpriteVisibilityToolData : CacheObject
    {
        [SerializeField]
        bool m_AllVisibility = true;
        bool m_PreviousVisibility = true;

        public bool allVisibility
        {
            get { return m_AllVisibility; }
            set { m_PreviousVisibility = m_AllVisibility = value; }
        }

        public bool previousVisibility
        {
            get { return m_PreviousVisibility; }
            set { m_PreviousVisibility = value; }
        }
    }

    internal class SpriteVisibilityToolController
    {
        bool m_UpdateViewOnSelection = true;

        internal interface ISpriteVisibilityItem
        {
            bool visibility { get; set; }
            ICharacterOrder characterOrder { get;}
        }

        internal class SpriteVisibilityGroupItem : ISpriteVisibilityItem
        {
            public CharacterGroupCache group;
            public ISpriteVisibilityItem[] childItems;
            bool ISpriteVisibilityItem.visibility
            {
                get { return group.isVisible; }
                set
                {
                    if (childItems != null)
                    {
                        foreach (var item in childItems)
                            item.visibility = value;
                    }
                    group.isVisible = value;
                }
            }
            
            public ICharacterOrder characterOrder { get { return group; } }
        }

        internal class SpriteVisibilitySpriteItem : ISpriteVisibilityItem
        {
            public CharacterPartCache sprite;
            bool ISpriteVisibilityItem.visibility
            {
                get { return sprite.isVisible; }
                set { sprite.isVisible = value; }
            }
            public ICharacterOrder characterOrder { get { return sprite; } }
        }

        ISpriteVisibilityToolModel m_Model;
        SkinningEvents m_Events;
        public event Action OnAvailabilityChangeListeners = () => {};

        public SpriteVisibilityToolController(ISpriteVisibilityToolModel model, SkinningEvents events)
        {
            m_Model = model;
            m_Events = events;
            m_Events.skinningModeChanged.AddListener(OnViewModeChanged);
        }

        public void Activate()
        {
            m_Events.selectedSpriteChanged.AddListener(OnSpriteSelectedChanged);
            m_Model.view.Setup();
            m_Model.view.SetSelection(m_Model.selectedSprite);
            if (m_Model.previousVisibility != m_Model.allVisibility)
            {
                SetAllCharacterSpriteVisibility();
                m_Model.previousVisibility = m_Model.allVisibility;
            }
        }

        public void Deactivate()
        {
            m_Events.selectedSpriteChanged.RemoveListener(OnSpriteSelectedChanged);
        }

        public void Dispose()
        {
            m_Events.skinningModeChanged.RemoveListener(OnViewModeChanged);
        }

        void OnViewModeChanged(SkinningMode mode)
        {
            OnAvailabilityChangeListeners();
            if (isAvailable && m_Model.previousVisibility != m_Model.allVisibility)
                SetAllCharacterSpriteVisibility();
        }

        private void OnSpriteSelectedChanged(SpriteCache sprite)
        {
            if (m_UpdateViewOnSelection)
                m_Model.view.SetSelection(sprite);
            m_UpdateViewOnSelection = true;
        }

        public bool isAvailable
        {
            get { return m_Model.mode == SkinningMode.Character; }
        }

        void SetAllCharacterSpriteVisibility()
        {
            if (m_Model.hasCharacter)
            {
                using (m_Model.UndoScope(TextContent.spriteVisibility))
                {
                    var parts = m_Model.character.parts;

                    foreach (var part in parts)
                        part.isVisible = m_Model.allVisibility;

                    var groups = m_Model.character.groups;
                    foreach (var group in groups)
                        group.isVisible = m_Model.allVisibility;
                }
            }
        }

        public void SetAllVisibility(bool visibility)
        {
            using (m_Model.UndoScope(TextContent.spriteVisibility))
            {
                m_Model.allVisibility = visibility;
                SetAllCharacterSpriteVisibility();
            }
        }

        public bool GetAllVisibility()
        {
            return m_Model.allVisibility;
        }

        public List<TreeViewItem> BuildTreeView()
        {
            var rows = new List<TreeViewItem>();
            var character = m_Model.character;
            if (character != null)
            {
                var parts = character.parts;
                var groups = character.groups;
                var items = CreateTreeGroup(-1, groups, parts, 0);
                foreach (var item in items)
                    rows.Add(item);
                var groupParts = parts.Where(x => x.parentGroup < 0);
                foreach (var part in groupParts)
                {
                    var ii = CreateTreeViewItem(part, groups, 0);
                    rows.Add(ii);
                }
            }
            rows.Sort((x, y) =>
            {
                var x1 = (TreeViewItemBase<ISpriteVisibilityItem>)x;
                var y1 = (TreeViewItemBase<ISpriteVisibilityItem>)y;
                return SpriteVisibilityItemOrderSort(x1.customData, y1.customData);
            });
            
            return rows;
        }

        int SpriteVisibilityItemOrderSort(ISpriteVisibilityItem x, ISpriteVisibilityItem y)
        {
            return x.characterOrder.order.CompareTo(y.characterOrder.order);
        }
        
        private List<TreeViewItem> CreateTreeGroup(int level, CharacterGroupCache[] groups, CharacterPartCache[] parts, int depth)
        {
            var items = new List<TreeViewItem>();
            for (int j = 0; j < groups.Length; ++j)
            {
                if (groups[j].parentGroup == level)
                {
                    var item = new TreeViewItemBase<ISpriteVisibilityItem>(groups[j].GetInstanceID(), depth, groups[j].name, new SpriteVisibilityGroupItem()
                    {
                        group = groups[j],
                    });
                    items.Add(item);
                    var children = new List<ISpriteVisibilityItem>();
                    // find all sprite that has this group
                    var groupParts = parts.Where(x => x.parentGroup == j);
                    foreach (var part in groupParts)
                    {
                        var ii = CreateTreeViewItem(part, groups, depth + 1);
                        items.Add(ii);
                        var visibilityItem = ii as TreeViewItemBase<ISpriteVisibilityItem>;
                        if (visibilityItem != null)
                            children.Add(visibilityItem.customData);
                    }

                    var childItemes = CreateTreeGroup(j, groups, parts, depth + 1);
                    foreach (var iii in childItemes)
                    {
                        items.Add(iii);
                        var visibilityItem = iii as TreeViewItemBase<ISpriteVisibilityItem>;
                        if (visibilityItem != null)
                            children.Add(visibilityItem.customData);
                    }
                    (item.customData as SpriteVisibilityGroupItem).childItems = children.ToArray();
                }
            }
            return items;
        }

        private TreeViewItem CreateTreeViewItem(CharacterPartCache part, CharacterGroupCache[] groups, int depth)
        {
            var name = part.sprite.name;
            return new TreeViewItemBase<ISpriteVisibilityItem>(part.sprite.GetInstanceID(), depth, name,
                new SpriteVisibilitySpriteItem()
                {
                    sprite = part,
                });
        }

        public bool GetCharacterPartVisibility(TreeViewItem item)
        {
            var i = item as TreeViewItemBase<ISpriteVisibilityItem>;
            if (i != null)
                return i.customData.visibility;
            return false;
        }

        public void SetCharacterPartVisibility(TreeViewItem item, bool visible, bool isolate)
        {
            var i = item as TreeViewItemBase<ISpriteVisibilityItem>;
            if (i != null)
            {
                var characterPart = i.customData;
                var character = m_Model.character;
                using (m_Model.UndoScope(TextContent.spriteVisibility))
                {
                    if (isolate)
                    {
                        foreach (var characterPartCache in character.parts)
                            characterPartCache.isVisible = !visible;
                        foreach (var group in character.groups)
                            group.isVisible = !visible;
                    }
                    
                    characterPart.visibility = visible;
                }
            }
        }

        public void SetSelectedSprite(IList<TreeViewItem> rows, IList<int> selectedIds)
        {
            SpriteCache newSelected = null;
            if (selectedIds.Count > 0)
            {
                var selected = rows.FirstOrDefault(x =>
                {
                    var item = ((TreeViewItemBase<ISpriteVisibilityItem>)x).customData as SpriteVisibilitySpriteItem;
                    if (item != null && item.sprite.sprite.GetInstanceID() == selectedIds[0])
                        return true;
                    return false;
                }) as TreeViewItemBase<ISpriteVisibilityItem>;
                if (selected != null)
                    newSelected = ((SpriteVisibilitySpriteItem)selected.customData).sprite.sprite;
            }

            using (m_Model.UndoScope(TextContent.selectionChange))
            {
                m_UpdateViewOnSelection = false;
                m_Events.selectedSpriteChanged.Invoke(newSelected);
                if (newSelected == null)
                    m_Model.view.SetSelectionIds(selectedIds);
            }
        }

        public int GetTreeViewSelectionID(SpriteCache sprite)
        {
            if (sprite != null)
                return sprite.GetInstanceID();
            return 0;
        }

        public SpriteCategory GetCategoryForSprite(TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var customData = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
            var characterPart = customData != null ? customData.sprite : null;
            if (characterPart != null)
            {
                var spriteLib = m_Model.spriteCategoryList;
                foreach (var category in spriteLib.categories)
                {
                    if (category.labels.FindIndex(x => x.spriteId == characterPart.sprite.id) != -1)
                        return category;
                }
            }

            return new SpriteCategory()
            {
                name = "",
                labels = new List<SpriteCategoryLabel>()
            };
        }

        public string[] GetCategoryStrings()
        {
            return m_Model.spriteCategoryList.categories.Select(x => x.name).ToArray();
        }

        public string GetSpriteLabelName(SpriteCategory category, TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var customData = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
            var characterPart = customData != null ? customData.sprite : null;
            var index = -1;
            if (characterPart != null)
            {
                index = category.labels.FindIndex(x => x.spriteId == characterPart.sprite.id);
            }
            if (index >= 0)
                return category.labels[index].name;
            return "";
        }

        int GetCategoryIndexForSprite(SpriteCategory category, TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var customData = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
            var characterPart = customData != null ? customData.sprite : null;
            var index = -1;
            if (characterPart != null)
            {
                index = category.labels.FindIndex(x => x.spriteId == characterPart.sprite.id);
            }
            return index;
        }

        public void RemoveSpriteFromCategory(TreeViewItem treeViewItem)
        {
            var spriteLibCategory = GetCategoryForSprite(treeViewItem);
            var index = GetCategoryIndexForSprite(spriteLibCategory, treeViewItem);
            if (index >= 0)
            {
                var labels = spriteLibCategory.labels.ToList();
                labels.RemoveAt(index);
                spriteLibCategory.labels = labels;
                m_Events.spriteLibraryChanged.Invoke();
            }
        }

        public void SetCategoryForSprite(string categoryName, TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var spriteData = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
            if (spriteData != null)
            {
                var category = GetCategoryForSprite(treeViewItem);
                var labelName = GetSpriteLabelName(category, treeViewItem);
                if (string.IsNullOrEmpty(labelName))
                    labelName = spriteData.sprite.sprite.name;
                SetCategoryForSprite(categoryName, labelName, spriteTreeViewItem.customData);
            }
        }

        public void SetCategoryForSprite(string categoryName, string labelName, TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            SetCategoryForSprite(categoryName, labelName, spriteTreeViewItem.customData);
        }

        void SetCategoryForSprite(string categoryName, string labelName, ISpriteVisibilityItem spriteVisibilityItem)
        {
            categoryName = categoryName.Trim();
            labelName = labelName.Trim();
            var customData = spriteVisibilityItem as SpriteVisibilitySpriteItem;
            var characterPart = customData != null ? customData.sprite : null;
            if (characterPart != null && characterPart.sprite != null)
            {
                var spriteLib = m_Model.spriteCategoryList;
                using (m_Model.UndoScope(TextContent.spriteCategoryChanged))
                {
                    spriteLib.AddSpriteToCategory(categoryName, new SpriteCategoryLabel()
                    {
                        name = labelName,
                        spriteId = characterPart.sprite.id
                    }
                    );
                    m_Events.spriteLibraryChanged.Invoke();
                }
            }
        }

        public bool IsLabelDuplicate(SpriteCategory category, string labelName)
        {
            var hash = SpriteLibraryUtility.GetStringHash(labelName);
            return category.labels.Count(x => x.name == labelName || hash == SpriteLibraryUtility.GetStringHash(x.name)) > 1;
        }

        public void SetCategoryLabelName(string labelname, TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var customData = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
            var characterPart = customData != null ? customData.sprite : null;
            if (characterPart != null && characterPart.sprite != null)
            {
                var spriteLib = m_Model.spriteCategoryList;
                using (m_Model.UndoScope(TextContent.spriteCategoryIndexChanged))
                {
                    spriteLib.ChangeSpriteLabelName(labelname, characterPart.sprite.id);
                    m_Events.spriteLibraryChanged.Invoke();
                }
            }
        }

        public bool SupportCateogry(TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var customData = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
            return customData != null;
        }

        public bool SupportConvertToCatgory(TreeViewItem treeViewItem)
        {
            var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
            var customData = spriteTreeViewItem.customData as SpriteVisibilityGroupItem;
            return customData != null;
        }

        private bool SupportConvertToCatgory(ISpriteVisibilityItem treeViewItem)
        {
            return treeViewItem is SpriteVisibilityGroupItem;
        }

        private void ConvertLayerToCategory(ISpriteVisibilityItem treeViewItem)
        {
            if (SupportConvertToCatgory(treeViewItem))
            {
                var groupItem = treeViewItem as SpriteVisibilityGroupItem;
                foreach (var item in groupItem.childItems)
                    ConvertLayerToCategory(item);
            }
            else
            {
                var groupItem = treeViewItem as SpriteVisibilitySpriteItem;
                var name = groupItem.sprite.sprite.name;
                SetCategoryForSprite(name, name, groupItem);
            }
        }

        public void ClearCategory(TreeViewItem treeViewItem)
        {
            if (SupportConvertToCatgory(treeViewItem))
            {
                var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
                var customData = spriteTreeViewItem.customData as SpriteVisibilityGroupItem;
                AddGroupToCategory("", customData);
            }
            else
            {
                var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
                var item = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
                SetCategoryForSprite("", "", item);
            }
        }

        public void ConvertLayerToCategory(TreeViewItem treeViewItem)
        {
            if (SupportConvertToCatgory(treeViewItem))
            {
                var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
                ConvertLayerToCategory(spriteTreeViewItem.customData);
            }
            else
            {
                var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
                var groupItem = spriteTreeViewItem.customData as SpriteVisibilitySpriteItem;
                var name = groupItem.sprite.sprite.name;
                SetCategoryForSprite(name, name, groupItem);
            }
        }

        public void ConvertToCategory(TreeViewItem treeViewItem)
        {
            if (SupportConvertToCatgory(treeViewItem))
            {
                var spriteTreeViewItem = treeViewItem as TreeViewItemBase<ISpriteVisibilityItem>;
                var groupItem = spriteTreeViewItem.customData as SpriteVisibilityGroupItem;
                AddGroupToCategory(groupItem.group.name, groupItem);
            }
        }

        void AddGroupToCategory(string categoryName, SpriteVisibilityGroupItem groupItem)
        {
            foreach (var item in groupItem.childItems)
            {
                if (item is SpriteVisibilityGroupItem)
                    AddGroupToCategory(categoryName, item as SpriteVisibilityGroupItem);
                else if (item is SpriteVisibilitySpriteItem)
                {
                    var spriteItem = (SpriteVisibilitySpriteItem)item;
                    SetCategoryForSprite(categoryName, spriteItem.sprite.sprite.name, item);
                }
            }
        }

        public void RemoveUnusedCategory()
        {
            var spriteLib = m_Model.spriteCategoryList;
            var changed = false;

            var spriteIds = m_Model.character.parts.Select(partCache => partCache.sprite.id);
            for (var i = 0; i < spriteLib.categories.Count; ++i)
            {
                if (spriteLib.categories[i].labels.Count == 0 ||
                    !spriteLib.categories[i].labels.Any(label => spriteIds.Contains(label.spriteId)))
                {
                    spriteLib.categories.RemoveAt(i);
                    --i;
                    changed = true;
                }
            }
            if (changed)
                m_Events.spriteLibraryChanged.Invoke();
        }
    }

    internal class SpriteVisibilityTool : IVisibilityTool, ISpriteVisibilityToolModel
    {
        SpriteVisibilityToolView m_View;
        SpriteVisibilityToolController m_Controller;

        private SpriteVisibilityToolData m_Data;
        private SkinningCache m_SkinningCache;
        public SkinningCache skinningCache { get { return m_SkinningCache; } }

        public SpriteVisibilityTool(SkinningCache s)
        {
            m_SkinningCache = s;
            m_Data = skinningCache.CreateCache<SpriteVisibilityToolData>();
            m_Controller = new SpriteVisibilityToolController(this, skinningCache.events);
            m_View = new SpriteVisibilityToolView()
            {
                GetController = () => m_Controller
            };
        }

        public void Setup()
        {}

        public void Dispose()
        {
            m_Controller.Dispose();
        }

        public VisualElement view { get { return m_View; } }
        public string name { get { return L10n.Tr(TextContent.sprite); } }

        public void Activate()
        {
            m_Controller.Activate();
        }

        public void Deactivate()
        {
            m_Controller.Deactivate();
        }

        public bool isAvailable
        {
            get { return m_Controller.isAvailable; }
        }


        public void SetAvailabilityChangeCallback(Action callback)
        {
            m_Controller.OnAvailabilityChangeListeners += callback;
        }

        ISpriteVisibilityToolView ISpriteVisibilityToolModel.view { get {return m_View;} }

        bool ISpriteVisibilityToolModel.hasCharacter { get { return skinningCache.hasCharacter; } }
        SpriteCache ISpriteVisibilityToolModel.selectedSprite { get { return skinningCache.selectedSprite; } }
        CharacterCache ISpriteVisibilityToolModel.character { get { return skinningCache.character; } }
        bool ISpriteVisibilityToolModel.previousVisibility { get { return m_Data.previousVisibility; } set { m_Data.previousVisibility = value; } }
        bool ISpriteVisibilityToolModel.allVisibility { get { return m_Data.allVisibility; } set { m_Data.allVisibility = value; } }
        SkinningMode ISpriteVisibilityToolModel.mode { get { return skinningCache.mode; } }
        SpriteCategoryListCacheObject ISpriteVisibilityToolModel.spriteCategoryList { get { return skinningCache.spriteCategoryList; }}

        UndoScope ISpriteVisibilityToolModel.UndoScope(string description)
        {
            return skinningCache.UndoScope(description);
        }
    }

    internal class SpriteVisibilityToolView : VisibilityToolViewBase, ISpriteVisibilityToolView
    {
        public Func<SpriteVisibilityToolController> GetController = () => null;

        public SpriteVisibilityToolView()
        {
            var columns = new MultiColumnHeaderState.Column[4];
            columns[0] = new MultiColumnHeaderState.Column
            {
                headerContent = VisibilityTreeViewBase.VisibilityIconStyle.visibilityOnIcon,
                headerTextAlignment = TextAlignment.Center,
                width = 32,
                minWidth = 32,
                maxWidth = 32,
                autoResize = false,
                allowToggleVisibility = true
            };
            columns[1] = new MultiColumnHeaderState.Column
            {
                headerContent = EditorGUIUtility.TrTextContent(TextContent.name),
                headerTextAlignment = TextAlignment.Center,
                width = 130,
                minWidth = 100,
                autoResize = true,
                allowToggleVisibility = false
            };
            columns[2] = new MultiColumnHeaderState.Column
            {
                headerContent = EditorGUIUtility.TrTextContent(TextContent.category),
                headerTextAlignment = TextAlignment.Center,
                width = 70,
                minWidth = 50,
                autoResize = true,
                allowToggleVisibility = false
            };
            columns[3] = new MultiColumnHeaderState.Column
            {
                headerContent = EditorGUIUtility.TrTextContent(TextContent.label),
                headerTextAlignment = TextAlignment.Center,
                width = 50,
                minWidth = 30,
                autoResize = true,
                allowToggleVisibility = false
            };
            var multiColumnHeaderState = new MultiColumnHeaderState(columns);
            var multiColumnHeader = new VisibilityToolColumnHeader(multiColumnHeaderState)
            {
                GetAllVisibility = InternalGetAllVisibility,
                SetAllVisibility = InternalSetAllVisibility,
                canSort = false,
                height = 20,
                visibilityColumn = 0
            };

            m_TreeView = new SpriteTreeView(m_TreeViewState, multiColumnHeader)
            {
                GetController = InternalGetController
            };
            SetupSearchField();
        }

        SpriteVisibilityToolController InternalGetController()
        {
            return GetController();
        }

        bool InternalGetAllVisibility()
        {
            return GetController().GetAllVisibility();
        }

        void InternalSetAllVisibility(bool visibility)
        {
            GetController().SetAllVisibility(visibility);
        }

        public void Setup()
        {
            ((SpriteTreeView)m_TreeView).Setup();
        }

        public void SetSelection(SpriteCache sprite)
        {
            ((SpriteTreeView)m_TreeView).SetSelection(sprite);
        }

        public void SetSelectionIds(IList<int> selectedIds)
        {
            ((SpriteTreeView)m_TreeView).SetSelectionIds(selectedIds);
        }
    }

    class SpriteTreeView : VisibilityTreeViewBase
    {
        public Func<SpriteVisibilityToolController> GetController = () => null;
        private static int k_CategorySeletionOffset = 4;
        public GUIStyle m_Style;
        public GUIContent m_WarningIcon;
        int m_SpriteCategoryRenameID;
        static string[] k_DefaultCategoryList = new string[]
        {
            TextContent.none,
            TextContent.newTrailingDots,
            TextContent.removeEmptyCategory,
            ""
        };
        private TreeViewItem m_CurrentEdittingItem;

        public SpriteTreeView(TreeViewState treeViewState, MultiColumnHeader multiColumnHeader)
            : base(treeViewState, multiColumnHeader)
        {
            columnIndexForTreeFoldouts = 1;
        }

        void SkinInit()
        {
            if (m_Style == null)
            {
                GUIStyle foldOut = "IN Foldout";
                m_Style = new GUIStyle(foldOut);
                m_Style.stretchWidth = false;
                m_Style.richText = false;
                m_Style.border = new RectOffset(-800, -10, 0, -10);
                m_Style.padding = new RectOffset(11, 16, 2, 2);
                m_Style.fixedWidth = 0;
                m_Style.alignment = TextAnchor.MiddleCenter;
                m_Style.clipping = TextClipping.Clip;
                m_Style.normal.background = foldOut.onFocused.background;
                m_Style.normal.scaledBackgrounds = foldOut.onFocused.scaledBackgrounds;
                m_Style.normal.textColor = foldOut.normal.textColor;
                m_Style.onNormal.background = m_Style.normal.background;
                m_Style.onNormal.scaledBackgrounds = m_Style.normal.scaledBackgrounds;
                m_Style.onNormal.textColor = m_Style.normal.textColor;
                m_Style.onActive.background = m_Style.normal.background;
                m_Style.onActive.scaledBackgrounds = m_Style.normal.scaledBackgrounds;
                m_Style.onActive.textColor = m_Style.normal.textColor;
                m_Style.active.background = m_Style.normal.background;
                m_Style.active.scaledBackgrounds = m_Style.normal.scaledBackgrounds;
                m_Style.active.textColor = m_Style.normal.textColor;
                m_Style.onFocused.background = m_Style.normal.background;
                m_Style.onFocused.scaledBackgrounds = m_Style.normal.scaledBackgrounds;
                m_Style.onFocused.textColor = m_Style.normal.textColor;
                m_Style.focused.background = m_Style.normal.background;
                m_Style.focused.scaledBackgrounds = m_Style.normal.scaledBackgrounds;
                m_Style.focused.textColor = m_Style.normal.textColor;
                m_WarningIcon = EditorGUIUtility.TrIconContent("console.warnicon.sml", "Duplicate label name found or label hash value clash. Please use a different name");
                m_SpriteCategoryRenameID = EditorGUIUtility.GetControlID("SpriteCategoryRenameControlId".GetHashCode(), FocusType.Passive);
            }
        }

        public void Setup()
        {
            Reload();
        }

        public override void OnGUI(Rect rect)
        {
            if (Event.current.type == EventType.Repaint)
                SkinInit();

            if (Event.current.type == EventType.MouseUp && Event.current.button == 1)
            {
                var canCovert = false;
                foreach (var item in GetRows())
                {
                    if (IsSelected(item.id) && GetController().SupportConvertToCatgory(item))
                    {
                        canCovert = true;
                        break;
                    }
                }
                var genericMenu = new GenericMenu();
                if (canCovert)
                {
                    genericMenu.AddItem(new GUIContent(TextContent.convertGroupToCategory), false, ConvertGroupToCategory);
                }
                genericMenu.AddItem(new GUIContent(TextContent.convertLayerToCategory), false, ConvertLayerToCategory);
                genericMenu.AddItem(new GUIContent(TextContent.clearAllCategory), false, ClearAllCategory);
                genericMenu.ShowAsContext();
            }
            base.OnGUI(rect);
        }

        void ConvertGroupToCategory()
        {
            foreach (var item in GetRows())
            {
                if (IsSelected(item.id) && GetController().SupportConvertToCatgory(item))
                {
                    GetController().ConvertToCategory(item);
                }
            }
        }

        void ClearAllCategory()
        {
            foreach (var item in GetRows())
            {
                GetController().ClearCategory(item);
            }
        }

        void ConvertLayerToCategory()
        {
            foreach (var item in GetRows())
            {
                if (IsSelected(item.id))
                {
                    GetController().ConvertLayerToCategory(item);
                }
            }
        }

        void CellGUI(Rect cellRect, TreeViewItem item, int column, ref RowGUIArgs args)
        {
            CenterRectUsingSingleLineHeight(ref cellRect);
            switch (column)
            {
                case 0:
                    DrawVisibilityCell(cellRect, item);
                    break;
                case 1:
                    DrawNameCell(cellRect, item, ref args);
                    break;
                case 2:
                    DrawCategoryCell(cellRect, item, ref args);
                    break;
                case 3:
                    DrawIndexCell(cellRect, item, ref args);
                    break;
            }
        }

        void DrawVisibilityCell(Rect cellRect, TreeViewItem item)
        {
            var style = MultiColumnHeader.DefaultStyles.columnHeaderCenterAligned;
            var characterPartVisibility = GetController().GetCharacterPartVisibility(item);

            EditorGUI.BeginChangeCheck();

            var visible = GUI.Toggle(cellRect, characterPartVisibility, characterPartVisibility ? VisibilityIconStyle.visibilityOnIcon : VisibilityIconStyle.visibilityOffIcon, style);

            if (EditorGUI.EndChangeCheck())
            {
                GetController().SetCharacterPartVisibility(item, visible, Event.current.alt);
            }
        }

        void DrawNameCell(Rect cellRect, TreeViewItem item, ref RowGUIArgs args)
        {
            args.rowRect = cellRect;
            base.RowGUI(args);
        }

        void DrawCategoryCell(Rect cellRect, TreeViewItem item, ref RowGUIArgs args)
        {
            if (!GetController().SupportCateogry(item))
                return;
            if (m_CurrentEdittingItem != item)
            {
                List<string> displayedOptions = new List<string>(k_DefaultCategoryList);
                var allCategoryStrings = GetController().GetCategoryStrings();
                foreach (var s in allCategoryStrings)
                    displayedOptions.Add(s);
                var category = GetController().GetCategoryForSprite(item);
                var currentSelection = Array.FindIndex(allCategoryStrings, x => x == category.name);

                currentSelection = currentSelection < 0 ? 0 : currentSelection + k_CategorySeletionOffset;
                EditorGUI.BeginChangeCheck();
                currentSelection = EditorGUI.Popup(cellRect, currentSelection, displayedOptions.ToArray(), m_Style);
                if (EditorGUI.EndChangeCheck())
                {
                    if (currentSelection == 1)
                    {
                        m_CurrentEdittingItem = item;
                        DrawCategoryNameControl(cellRect, item);
                    }
                    else if (currentSelection == 2)
                    {
                        GetController().RemoveUnusedCategory();
                    }
                    else
                    {
                        var realSelectionIndex = currentSelection - k_CategorySeletionOffset;
                        var categoryName = realSelectionIndex >= 0 ? displayedOptions[currentSelection] : "";
                        GetController().SetCategoryForSprite(categoryName, item);
                    }
                }
            }
            else
            {
                if (GUIUtility.keyboardControl != m_SpriteCategoryRenameID)
                    m_CurrentEdittingItem = null;
                else
                    DrawCategoryNameControl(cellRect, item);
            }
        }

        void DrawCategoryNameControl(Rect cellRect, TreeViewItem item)
        {
            EditorGUI.BeginChangeCheck();
            if (Event.current.type == EventType.Layout)
            {
                GUI.SetNextControlName("SpriteCategoryRename");
                GUI.FocusControl("SpriteCategoryRename");
            }

            GUIUtility.keyboardControl = m_SpriteCategoryRenameID;
            string categoryName = EditorGUI.DelayedTextField(cellRect, GUIContent.none, m_SpriteCategoryRenameID, "");
            if (EditorGUI.EndChangeCheck())
            {
                if (Array.FindIndex(k_DefaultCategoryList, x => x == categoryName) == -1)
                    GetController().SetCategoryForSprite(categoryName, item);
                m_CurrentEdittingItem = null;
            }
        }

        void DrawIndexCell(Rect cellRect, TreeViewItem item, ref RowGUIArgs args)
        {
            if (!GetController().SupportCateogry(item))
                return;
            var category = GetController().GetCategoryForSprite(item);
            if (!string.IsNullOrEmpty(category.name))
            {
                var labelName = GetController().GetSpriteLabelName(category, item);
                var isDuplicate = GetController().IsLabelDuplicate(category, labelName);
                if (isDuplicate)
                {
                    GUI.Label(new Rect(cellRect.x, cellRect.y, 20, cellRect.height), m_WarningIcon);
                    cellRect.x += 20;
                    cellRect.width -= 20;
                }
                EditorGUI.BeginChangeCheck();
                string newlabelname = EditorGUI.DelayedTextField(cellRect, labelName);
                if (EditorGUI.EndChangeCheck())
                {
                    GetController().SetCategoryLabelName(newlabelname, item);
                }
            }
        }

        protected override void RowGUI(RowGUIArgs args)
        {
            var item = args.item;

            for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
            {
                CellGUI(args.GetCellRect(i), item, args.GetColumn(i), ref args);
            }
        }

        protected override Rect GetRenameRect(Rect rowRect, int row, TreeViewItem item)
        {
            Rect cellRect = GetCellRectForTreeFoldouts(rowRect);
            CenterRectUsingSingleLineHeight(ref cellRect);
            return base.GetRenameRect(cellRect, row, item);
        }

        protected override TreeViewItem BuildRoot()
        {
            var root = new TreeViewItem {id = 0, depth = -1, displayName = "Root"};
            var rows = GetController() != null ? GetController().BuildTreeView() : new List<TreeViewItem>();
            SetupParentsAndChildrenFromDepths(root, rows);
            return root;
        }

        protected override void SelectionChanged(IList<int> selectedIds)
        {
            GetController().SetSelectedSprite(GetRows(), selectedIds);
        }

        public void SetSelectionIds(IList<int> selectedIds)
        {
            SetSelection(selectedIds, TreeViewSelectionOptions.RevealAndFrame);
        }

        public void SetSelection(SpriteCache sprite)
        {
            var id = GetController().GetTreeViewSelectionID(sprite);
            SetSelection(new[] { id }, TreeViewSelectionOptions.RevealAndFrame);
        }
    }
}
