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

namespace Ingvar.LiveWatch.Editor
{
    [Serializable]
    public class VariableCellGUI
    {
        public bool IsSelected { get; set; }
        public int RowIndex { get; set; }
        public int SelectedColumnIndex { get; set; }
        public bool IsNarrowMode { get; set; }
        public float ValueColumnWidth { get; set; }
        public WatchVariable Variable { get; set; }
        public Rect ValuesRect { get; set; }
        public VariableSelectedFitInfo SelectedFitInfo { get; set; }
        public bool PreferRightSelection { get; set; }
        public List<int> IndicesToDisplay { get; set; }

        private VariableSelectedFitInfo _currentFitInfo = new();

        private float currentSideOffset;
        private bool isRightSelection => PreferRightSelection ? SelectedFitInfo.CanFitRight : !SelectedFitInfo.CanFitLeft;

        public void DrawValueCellSegment(Rect rect, string value, List<int> contentIndices, float progress)
        {
            if (Event.current.type is not EventType.Repaint)
                return;

            currentSideOffset = 0;
            
            if (value == string.Empty)
            {
                return;
            }

            var isSelected = contentIndices.Contains(SelectedColumnIndex);

            EditorGUI.DrawRect(rect.Extrude(ExtrudeFlags.All, -1f), Colors.SegmentFrame);
            EditorGUI.DrawRect(rect.Extrude(ExtrudeFlags.All, -2.5f), RowIndex % 2 == 0 ? Colors.Background : Colors.BackgroundOdd);

            var innerRect = rect.Extrude(ExtrudeFlags.All, -4f);
            currentSideOffset = 4;

            var progressPixels = Mathf.FloorToInt(Mathf.Clamp(progress * innerRect.height, 2, innerRect.height));
            var progressRect = innerRect.CropFromPositionWithSize(CropEdge.BottomLocal, 0.5f, progressPixels);
            var progressColor = RowIndex % 2 == 0 ? Colors.SegmentFill : Colors.SegmentFillOdd;

            DrawFormatCells(progressRect, contentIndices, progressColor);
            DrawDividerCells(progressRect.Extrude(ExtrudeFlags.Left | ExtrudeFlags.Right, 4f), contentIndices);

            if (isSelected)
                return;

            var estimatedSize = Styles.VariableValue.CalcSize(WatchEditorServices.GUICache.GetContent(value));
            estimatedSize.x -= 4;
            var valueRect = innerRect;

            if (estimatedSize.x > valueRect.width)
            {
                var dotsRect = valueRect
                    .CropFromPositionWithSize(CropEdge.RightLocal, 0, 7)
                    .CropFromPositionWithSize(CropEdge.BottomLocal, valueRect.height/2f - 6, 3);
                
                GUI.DrawTexture(dotsRect, 
                    Textures.Dots);
                valueRect = valueRect.Extrude(ExtrudeFlags.Right, -6);
            }

            if (valueRect.width > 5)
            {
                var style = estimatedSize.x > valueRect.width ? Styles.VariableValueLeft : Styles.VariableValue;
                DrawLabelWitchSearchResult(valueRect, Variable.GetValueText(contentIndices[0]), contentIndices[0], style);
            }
        }
        
        public void DrawSelectedCell(Rect rect, string value, int index)
        {
            if (Event.current.type is not (EventType.Repaint or EventType.Layout))
                return;
            
            var availableRect = rect.Extrude(ExtrudeFlags.All, -1);
            var estimatedSize = Styles.VariableValueSelected.CalcSize(WatchEditorServices.GUICache.GetContent(value)) 
                                + Vector2.right * 10;
            var resultWidth = Mathf.Max(availableRect.width, estimatedSize.x);
            
            if (Event.current.type is not EventType.Repaint)
            {
                var cellRectRight = availableRect.CropFromStartToPosition(CropEdge.LeftLocal, resultWidth);
                _currentFitInfo.CanFitRight = cellRectRight.xMax <= ValuesRect.xMax;
                
                var cellRectLeft = availableRect.CropFromStartToPosition(CropEdge.RightLocal, resultWidth);
                _currentFitInfo.CanFitLeft = cellRectLeft.xMin >= ValuesRect.xMin;

                SelectedFitInfo.MergeWith(_currentFitInfo);
                return;
            }

            var edge = PreferRightSelection 
                ? SelectedFitInfo.CanFitRight ? CropEdge.LeftLocal : CropEdge.RightLocal
                : SelectedFitInfo.CanFitLeft ? CropEdge.RightLocal : CropEdge.LeftLocal;

            var distToValuesEdge = edge == CropEdge.LeftLocal
                ? ValuesRect.xMax - availableRect.xMin
                : availableRect.xMax - ValuesRect.xMin;
            
            if (distToValuesEdge > 40)
                resultWidth = Mathf.Min(resultWidth, distToValuesEdge - 5);
            
            var cellBackRect = availableRect.CropFromStartToPosition(edge, resultWidth);
            
            if (IsNarrowMode)
            {
                cellBackRect = cellBackRect.OffsetByX(Mathf.Max(rect.width, Constants.VariableSelectionLineMinWidth) 
                                                      * (edge == CropEdge.LeftLocal ? 1 : - 1));
                cellBackRect = cellBackRect.Extrude(
                    ExtrudeFlags.Top | ExtrudeFlags.Bottom, 
                    -Mathf.Abs(cellBackRect.height - estimatedSize.y)/2f);
            }
            
            EditorGUI.DrawRect(cellBackRect, IsNarrowMode ? Colors.CellBackgroundSelectedGraph : Colors.CellBackgroundSelected);

            DrawPreviewTriangles(cellBackRect);
            DrawSelectedCelLine(rect);
            
            var cellValueRect = cellBackRect;
            var isTextFit = estimatedSize.x < cellValueRect.width;
            var textStyle = isTextFit ? Styles.VariableValueSelected : Styles.VariableValueSelectedLeft;
            cellValueRect = cellValueRect.CropFromPositionToEnd(CropEdge.LeftLocal, isTextFit ? 0 : 5);
            DrawLabelWitchSearchResult(cellValueRect, value, index, textStyle);
        }
        
        public void DrawSelectedCelLine(Rect rect)
        {
            if (Event.current.type is not EventType.Repaint)
                return;

            var edge = PreferRightSelection 
                ? SelectedFitInfo.CanFitRight ? CropEdge.LeftLocal : CropEdge.RightLocal
                : SelectedFitInfo.CanFitLeft ? CropEdge.RightLocal : CropEdge.LeftLocal;
            
            var lineRect = IsNarrowMode 
                ? rect 
                : rect.CropFromPositionWithSize(edge, 1, 3);
            
            if (IsNarrowMode && lineRect.width < Constants.VariableSelectionLineMinWidth)
            {
                lineRect = new Rect(
                    lineRect.center.x - Constants.VariableSelectionLineMinWidth / 2,
                    lineRect.y,
                    Constants.VariableSelectionLineMinWidth,
                    lineRect.height);
            }

            EditorGUI.DrawRect(lineRect, IsNarrowMode ? Colors.CellSelectionLineGraph : Colors.CellSelectionLine);
        }

        private void DrawLabelWitchSearchResult(Rect labelRect, string text, int index, GUIStyle style, bool forceFullSelection = false)
        {
            if (Event.current.type is not EventType.Repaint)
                return;

            if (labelRect.width > ValueColumnWidth)
                labelRect = labelRect.FitInRect(ValuesRect);
            style.Draw(labelRect, WatchEditorServices.GUICache.GetContent(text), -1);
        }

        private void DrawFormatCells(Rect rect, List<int> contentIndices, Color fallbackColor)
        {
            {
                TryDrawFormatRect(rect
);
                return;
            }
            
            void TryDrawFormatRect(Rect formatRect
                )
            {
                if (fallbackColor != Color.clear)
                    EditorGUI.DrawRect(formatRect, fallbackColor);
            }
        }

        private void DrawDividerCells(Rect rect, List<int> contentIndices)
        {
            WatchEditorServices.CellDividerDrawer.Draw(rect, Mathf.FloorToInt(ValueColumnWidth));
        }
        
        
        private Rect GetCellRect(int index, Rect rect, List<int> contentIndices)
        {
            var cellWidth = ValueColumnWidth;
            
            if (index == 0)
                cellWidth -= currentSideOffset;
                
            if (index == contentIndices.Count - 1)
                cellWidth -= currentSideOffset;
                
            var cellRect = rect.CropFromPositionWithSize(
                CropEdge.LeftLocal, 
                index * ValueColumnWidth + (index == 0 ? 0 : -currentSideOffset),
                cellWidth);

            return cellRect;
        }

        private void DrawPreviewTriangles(Rect rect)
        {
            var isSelectedNumberValid = Variable.IsValidNumberValue(SelectedColumnIndex, out var selectedNumberValue);
            
            if (Variable.Values.IsOriginalAt(SelectedColumnIndex))
            {
                var prevToSelectedIndex = IndicesToDisplay.IndexOf(SelectedColumnIndex) - 1;

                if (prevToSelectedIndex >= 0)
                {
                    var isPrevNumberValid = Variable.IsValidNumberValue(IndicesToDisplay[prevToSelectedIndex], out var prevNumberValue);
                    var isTopAngle = isPrevNumberValid && prevNumberValue >= selectedNumberValue;
                    var isNextToSearchResult = false;
                    var triangleLeftRect = rect
                        .CropFromPositionWithSize(CropEdge.LeftLocal, IsNarrowMode ? 1 : (isRightSelection ? 3 : 0), 4)
                        .CropFromPositionWithSize(isTopAngle ? CropEdge.TopLocal : CropEdge.BottomLocal, IsSelected && !IsNarrowMode ? 3 : 0, 4);
                    GUI.DrawTexture(triangleLeftRect, isTopAngle 
                        ? (isNextToSearchResult ? Textures.BlueTriangleTopLeft : Textures.WhiteTriangleTopLeft)
                        : (isNextToSearchResult ? Textures.BlueTriangleBottomLeft : Textures.WhiteTriangleBottomLeft));
                }
            }

            var nextToSelectedIndex = IndicesToDisplay.IndexOf(SelectedColumnIndex) + 1;
            if (nextToSelectedIndex < IndicesToDisplay.Count && Variable.Values.IsOriginalAt(IndicesToDisplay[nextToSelectedIndex]))
            {
                var isNextNumberValid = Variable.IsValidNumberValue(IndicesToDisplay[nextToSelectedIndex], out var nextNumberValue);
                var isTopAngle = isNextNumberValid && nextNumberValue >= selectedNumberValue;
                var isNextToSearchResult = false;
                var triangleRightRect = rect
                    .CropFromPositionWithSize(CropEdge.RightLocal, isRightSelection || IsNarrowMode ? 0 : 3, 4)
                    .CropFromPositionWithSize(isTopAngle ? CropEdge.TopLocal : CropEdge.BottomLocal, IsSelected && !IsNarrowMode ? 3 : 0, 4);
                GUI.DrawTexture(triangleRightRect, isTopAngle 
                    ? (isNextToSearchResult ? Textures.BlueTriangleTopRight : Textures.WhiteTriangleTopRight) 
                    : (isNextToSearchResult ? Textures.BlueTriangleBottomRight : Textures.WhiteTriangleBottomRight));
            }
        }
    }
}