/************************************************/
/*                                              */
/*     Copyright (c) 2018 - 2021 monitor1394    */
/*     https://github.com/monitor1394           */
/*                                              */
/************************************************/

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace XCharts
{
    [CustomPropertyDrawer(typeof(Serie), true)]
    public class SerieDrawer : BasePropertyDrawer
    {
        private bool m_IsPolar = false;
        private List<bool> m_DataFoldout = new List<bool>();
        public override string ClassName { get { return "Serie"; } }
        public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label)
        {
            pos.width -= 9;
            base.OnGUI(pos, prop, label);
            var type = prop.FindPropertyRelative("m_Type");
            if (!MakeFoldout(prop, "m_Show"))
            {
                var orderButton = 48;
                var gap = 4;
                var drawRect = pos;
                drawRect.x += EditorGUIUtility.labelWidth + gap;
                drawRect.width = pos.width - drawRect.x + ChartEditorHelper.BOOL_WIDTH - orderButton;
                EditorGUI.PropertyField(drawRect, type, GUIContent.none);
            }
            else
            {
                var chart = prop.serializedObject.targetObject as BaseChart;
                m_IsPolar = chart is PolarChart;
                var serieType = (SerieType)type.enumValueIndex;
                ++EditorGUI.indentLevel;
                PropertyField(prop, "m_Type");
                PropertyField(prop, "m_Name");
                switch (serieType)
                {
                    case SerieType.Line:
                        PropertyField(prop, "m_Stack");
                        if (m_IsPolar)
                        {
                            PropertyField(prop, "m_PolarIndex");
                        }
                        else
                        {
                            PropertyField(prop, "m_XAxisIndex");
                            PropertyField(prop, "m_YAxisIndex");
                        }
                        PropertyFieldLimitMin(prop, "m_MinShow", 0);
                        PropertyFieldLimitMin(prop, "m_MaxShow", 0);
                        PropertyFieldLimitMin(prop, "m_MaxCache", 0);
                        PropertyField(prop, "m_LineType");
                        PropertyField(prop, "m_SampleDist");
                        PropertyField(prop, "m_SampleType");
                        PropertyField(prop, "m_SampleAverage");
                        PropertyField(prop, "m_Clip");
                        PropertyField(prop, "m_Ignore");
                        PropertyField(prop, "m_IgnoreValue");
                        PropertyField(prop, "m_ShowAsPositiveNumber");
                        PropertyField(prop, "m_Large");
                        PropertyField(prop, "m_LargeThreshold");
                        PropertyField(prop, "m_Symbol");
                        PropertyField(prop, "m_LineStyle");
                        PropertyField(prop, "m_LineArrow");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_AreaStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Bar:
                        PropertyField(prop, "m_Stack");
                        if (m_IsPolar)
                        {
                            PropertyField(prop, "m_PolarIndex");
                        }
                        else
                        {
                            PropertyField(prop, "m_XAxisIndex");
                            PropertyField(prop, "m_YAxisIndex");
                        }
                        PropertyFieldLimitMin(prop, "m_MinShow", 0);
                        PropertyFieldLimitMin(prop, "m_MaxShow", 0);
                        PropertyFieldLimitMin(prop, "m_MaxCache", 0);
                        PropertyField(prop, "m_BarType");
                        PropertyField(prop, "m_BarPercentStack");
                        PropertyField(prop, "m_BarWidth");
                        PropertyField(prop, "m_BarGap");
                        PropertyField(prop, "m_BarZebraWidth");
                        PropertyField(prop, "m_BarZebraGap");
                        PropertyField(prop, "m_Clip");
                        PropertyField(prop, "m_Ignore");
                        PropertyField(prop, "m_IgnoreValue");
                        PropertyField(prop, "m_ShowAsPositiveNumber");
                        PropertyField(prop, "m_Large");
                        PropertyField(prop, "m_LargeThreshold");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Pie:
                        PropertyField(prop, "m_RoseType");
                        PropertyField(prop, "m_Space");
                        PropertyTwoFiled(prop, "m_Center");
                        PropertyTwoFiled(prop, "m_Radius");
                        PropertyField(prop, "m_RoundCap");
                        PropertyField(prop, "m_Ignore");
                        PropertyField(prop, "m_IgnoreValue");
                        PropertyField(prop, "m_AvoidLabelOverlap");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Ring:
                        PropertyTwoFiled(prop, "m_Center");
                        PropertyTwoFiled(prop, "m_Radius");
                        PropertyField(prop, "m_StartAngle");
                        PropertyField(prop, "m_RingGap");
                        PropertyField(prop, "m_RoundCap");
                        PropertyField(prop, "m_Clockwise");
                        PropertyField(prop, "m_TitleStyle");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Radar:
                        PropertyField(prop, "m_RadarType");
                        PropertyField(prop, "m_RadarIndex");
                        PropertyField(prop, "m_Symbol");
                        PropertyField(prop, "m_LineStyle");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_AreaStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Scatter:
                    case SerieType.EffectScatter:
                        PropertyField(prop, "m_Clip");
                        PropertyField(prop, "m_Symbol");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Heatmap:
                        PropertyField(prop, "m_Ignore");
                        PropertyField(prop, "m_IgnoreValue");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Gauge:
                        PropertyField(prop, "m_GaugeType");
                        PropertyTwoFiled(prop, "m_Center");
                        PropertyTwoFiled(prop, "m_Radius");
                        PropertyField(prop, "m_Min");
                        PropertyField(prop, "m_Max");
                        PropertyField(prop, "m_StartAngle");
                        PropertyField(prop, "m_EndAngle");
                        PropertyFieldLimitMax(prop, "m_SplitNumber", 36);
                        PropertyField(prop, "m_RoundCap");
                        PropertyField(prop, "m_TitleStyle");
                        PropertyField(prop, "m_GaugeAxis");
                        PropertyField(prop, "m_GaugePointer");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        PropertyField(prop, "m_Emphasis");
                        break;
                    case SerieType.Liquid:
                        PropertyField(prop, "m_VesselIndex");
                        PropertyField(prop, "m_Min");
                        PropertyField(prop, "m_Max");
                        PropertyField(prop, "m_WaveLength");
                        PropertyField(prop, "m_WaveHeight");
                        PropertyField(prop, "m_WaveSpeed");
                        PropertyField(prop, "m_WaveOffset");
                        PropertyField(prop, "m_ItemStyle");
                        PropertyField(prop, "m_Label");
                        break;
                }
                PropertyField(prop, "m_Animation");
                //PropertyListField(prop, "m_Data");
                DrawData(pos, prop, serieType, ref m_DrawRect);
                --EditorGUI.indentLevel;
            }
        }

        private void DrawData(Rect pos, SerializedProperty prop, SerieType serieType, ref Rect drawRect)
        {
            SerializedProperty m_Datas = prop.FindPropertyRelative("m_Data");
            SerializedProperty m_DataDimension = prop.FindPropertyRelative("m_ShowDataDimension");
            SerializedProperty m_ShowDataName = prop.FindPropertyRelative("m_ShowDataName");
            SerializedProperty m_ShowDataIcon = prop.FindPropertyRelative("m_ShowDataIcon");
            int index = InitToggle(prop);
            drawRect.width = EditorGUIUtility.labelWidth + 10;
            m_DataFoldout[index] = EditorGUI.Foldout(drawRect, m_DataFoldout[index], "Data");
            drawRect.width = pos.width;

            AddSingleLineHeight();
            if (m_DataFoldout[index])
            {
                EditorGUI.indentLevel++;

                float nameWid = 45;
#if UNITY_2019_3_OR_NEWER
                    var gap = 2;
                    var namegap = 3;
#else
                var gap = 0;
                var namegap = 0;
#endif
                EditorGUI.PropertyField(new Rect(drawRect.x, drawRect.y, pos.width - 2 * nameWid - 2, pos.height), m_DataDimension);
                var nameRect = new Rect(pos.width - 2 * nameWid + 14 + gap, drawRect.y, nameWid - gap, pos.height);
                if (XChartsSettings.editorBlockEnable)
                {
                    nameRect.x += ChartEditorHelper.BLOCK_WIDTH;
                }
                if (GUI.Button(nameRect, new GUIContent("Name")))
                {
                    m_ShowDataName.boolValue = !m_ShowDataName.boolValue;
                }
                var iconRect = new Rect(pos.width - nameWid + 14, drawRect.y, nameWid + namegap, pos.height);
                if (XChartsSettings.editorBlockEnable)
                {
                    iconRect.x += ChartEditorHelper.BLOCK_WIDTH;
                }
                if (GUI.Button(iconRect, new GUIContent("More...")))
                {
                    m_ShowDataIcon.boolValue = !m_ShowDataIcon.boolValue;
                }
                AddSingleLineHeight();
                var listSize = m_Datas.arraySize;
                listSize = EditorGUI.IntField(drawRect, "Size", listSize);
                AddSingleLineHeight();

                if (listSize < 0) listSize = 0;
                if (m_DataDimension.intValue < 1) m_DataDimension.intValue = 1;
                int dimension = m_DataDimension.intValue;
                bool showName = m_ShowDataName.boolValue;
                bool showIcon = m_ShowDataIcon.boolValue;
                bool showSelected = (serieType == SerieType.Pie);
                if (listSize != m_Datas.arraySize)
                {
                    while (listSize > m_Datas.arraySize) m_Datas.arraySize++;
                    while (listSize < m_Datas.arraySize) m_Datas.arraySize--;
                }
                if (listSize > 30 && !XChartsSettings.editorShowAllListData)
                {
                    int num = listSize > 10 ? 10 : listSize;
                    for (int i = 0; i < num; i++)
                    {
                        DrawDataElement(ref drawRect, dimension, m_Datas, showName, showIcon, showSelected, i, pos.width);
                    }
                    if (num >= 10)
                    {
                        EditorGUI.LabelField(drawRect, "...");
                        AddSingleLineHeight();
                        DrawDataElement(ref drawRect, dimension, m_Datas, showName, showIcon, showSelected, listSize - 1, pos.width);
                    }
                }
                else
                {
                    for (int i = 0; i < m_Datas.arraySize; i++)
                    {
                        DrawDataElement(ref drawRect, dimension, m_Datas, showName, showIcon, showSelected, i, pos.width);
                    }
                }
                AddHeight(EditorGUIUtility.standardVerticalSpacing);
                EditorGUI.indentLevel--;
            }
        }

        private void DrawDataElement(ref Rect drawRect, int dimension, SerializedProperty m_Datas, bool showName,
            bool showDetail, bool showSelected, int index, float currentWidth)
        {
            var lastX = drawRect.x;
            var lastWid = drawRect.width;
            var lastFieldWid = EditorGUIUtility.fieldWidth;
            var lastLabelWid = EditorGUIUtility.labelWidth;
            var serieData = m_Datas.GetArrayElementAtIndex(index);
            var sereName = serieData.FindPropertyRelative("m_Name");
            var selected = serieData.FindPropertyRelative("m_Selected");

            var data = serieData.FindPropertyRelative("m_Data");
            var fieldCount = dimension + (showName ? 1 : 0);

            if (fieldCount <= 1)
            {
                while (2 > data.arraySize)
                {
                    var value = data.arraySize == 0 ? index : 0;
                    data.arraySize++;
                    data.GetArrayElementAtIndex(data.arraySize - 1).floatValue = value;
                }
                SerializedProperty element = data.GetArrayElementAtIndex(1);
                if (showSelected)
                {
                    drawRect.width = drawRect.width - 18;
                    EditorGUI.PropertyField(drawRect, element, new GUIContent("Element " + index));
                    drawRect.x = currentWidth - 45;
                    EditorGUI.PropertyField(drawRect, selected, GUIContent.none);
                    drawRect.x = lastX;
                    drawRect.width = lastWid;
                }
                else
                {
                    EditorGUI.PropertyField(drawRect, element, new GUIContent("Element " + index));
                }
                AddHeight(EditorGUI.GetPropertyHeight(element));
            }
            else
            {
#if UNITY_2019_3_OR_NEWER
                var gap = 2;
                var namegap = 3;
#else
                var gap = 0;
                var namegap = 0;
#endif
                EditorGUI.LabelField(drawRect, "Element " + index);
                var startX = drawRect.x + EditorGUIUtility.labelWidth - EditorGUI.indentLevel * 15 + gap;
                var dataWidTotal = (currentWidth - (startX + 20.5f + 1));
                var dataWid = dataWidTotal / fieldCount;
                var xWid = dataWid - 4;
                for (int i = 0; i < dimension; i++)
                {
                    var dataCount = i < 1 ? 2 : i + 1;
                    while (dataCount > data.arraySize)
                    {
                        var value = data.arraySize == 0 ? index : 0;
                        data.arraySize++;
                        data.GetArrayElementAtIndex(data.arraySize - 1).floatValue = value;
                    }
                    drawRect.x = startX + i * xWid;
                    drawRect.width = dataWid + 40;
                    SerializedProperty element = data.GetArrayElementAtIndex(dimension <= 1 ? 1 : i);
                    EditorGUI.PropertyField(drawRect, element, GUIContent.none);
                }
                if (showName)
                {
                    drawRect.x = startX + (fieldCount - 1) * xWid;
                    drawRect.width = dataWid + 40 + dimension * namegap;
                    EditorGUI.PropertyField(drawRect, sereName, GUIContent.none);
                }
                AddSingleLineHeight();
                drawRect.x = lastX;
                drawRect.width = lastWid;
                EditorGUIUtility.fieldWidth = lastFieldWid;
                EditorGUIUtility.labelWidth = lastLabelWid;
            }
            if (showDetail)
            {
                EditorGUI.indentLevel += 2;
                var m_Icon = serieData.FindPropertyRelative("m_IconStyle");
                var m_EnableLabel = serieData.FindPropertyRelative("m_EnableLabel");
                var m_Label = serieData.FindPropertyRelative("m_Label");
                var m_EnableItemStyle = serieData.FindPropertyRelative("m_EnableItemStyle");
                var m_ItemStyle = serieData.FindPropertyRelative("m_ItemStyle");
                var m_EnableEmphasis = serieData.FindPropertyRelative("m_EnableEmphasis");
                var m_Emphasis = serieData.FindPropertyRelative("m_Emphasis");
                var m_EnableSymbol = serieData.FindPropertyRelative("m_EnableSymbol");
                var m_Symbol = serieData.FindPropertyRelative("m_Symbol");
                EditorGUI.PropertyField(drawRect, m_Icon);
                AddHeight(EditorGUI.GetPropertyHeight(m_Icon));
                EditorGUI.PropertyField(drawRect, m_Symbol);
                ChartEditorHelper.MakeBool(drawRect, m_EnableSymbol, 1, "(enable)");
                AddHeight(EditorGUI.GetPropertyHeight(m_Symbol));
                EditorGUI.PropertyField(drawRect, m_Label);
                ChartEditorHelper.MakeBool(drawRect, m_EnableLabel, 1, "(enable)");
                AddHeight(EditorGUI.GetPropertyHeight(m_Label));
                EditorGUI.PropertyField(drawRect, m_ItemStyle);
                ChartEditorHelper.MakeBool(drawRect, m_EnableItemStyle, 1, "(enable)");
                AddHeight(EditorGUI.GetPropertyHeight(m_ItemStyle));
                EditorGUI.PropertyField(drawRect, m_Emphasis);
                ChartEditorHelper.MakeBool(drawRect, m_EnableEmphasis, 1, "(enable)");
                AddHeight(EditorGUI.GetPropertyHeight(m_Emphasis));
                EditorGUI.indentLevel -= 2;
            }
        }

        private int InitToggle(SerializedProperty prop)
        {
            int index = 0;
            var sindex = prop.propertyPath.LastIndexOf('[');
            var eindex = prop.propertyPath.LastIndexOf(']');
            if (sindex >= 0 && eindex >= 0)
            {
                var str = prop.propertyPath.Substring(sindex + 1, eindex - sindex - 1);
                int.TryParse(str, out index);
            }
            if (index >= m_DataFoldout.Count)
            {
                m_DataFoldout.Add(false);
            }
            return index;
        }
    }
}