using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml.Controls;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Foundation;

namespace Sonex.Client.Controls.DataGridView;

// Odpowiedzialnosc pliku: pipeline renderowania i rysowanie warstw DataGridView na CanvasControl.

public sealed partial class DataGridView
{
    private bool _isRenderRequestQueued;
    private bool _hasPendingRenderRequest;

    // Jedno wejście do odświeżenia: najpierw przeliczenia, na końcu invalidacja canvasu.
    internal void RequestRender()
    {
        if (_isReleased)
        {
            return;
        }

        _hasPendingRenderRequest = true;

        if (_isRenderRequestQueued)
        {
            return;
        }

        var dispatcherQueue = DispatcherQueue;
        if (dispatcherQueue == null)
        {
            FlushRenderRequest();
            return;
        }

        _isRenderRequestQueued = true;
        if (!dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, FlushRenderRequest))
        {
            _isRenderRequestQueued = false;
            FlushRenderRequest();
        }
    }

    private void FlushRenderRequest()
    {
        _isRenderRequestQueued = false;

        if (_isReleased || !_hasPendingRenderRequest)
        {
            return;
        }

        _hasPendingRenderRequest = false;
        RenderFrame();
    }

    private void RenderFrame()
    {
        GenerateHeaders();

        CalcHeaders();
        CalcHorizontalScroll();
        CalcVerticalScroll();
        UpdateCornerMenuButtonLayout();

        _Canvas?.Invalidate();
    }


    private CanvasTextFormat HeaderTextFormat = new CanvasTextFormat()
    {
        FontSize = 14f,
        FontFamily = "Calibri",
        FontWeight = FontWeights.Normal,

        HorizontalAlignment = CanvasHorizontalAlignment.Left,
        VerticalAlignment = CanvasVerticalAlignment.Center,
        WordWrapping = CanvasWordWrapping.NoWrap,
        TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
        TrimmingSign = CanvasTrimmingSign.Ellipsis,

        //Direction = CanvasTextDirection.LeftToRightThenBottomToTop,
    };
    private CanvasTextFormat IconTextFormat = new CanvasTextFormat()
    {
        FontSize = 14f,
        FontFamily = "Segoe Fluent Icons",
        FontWeight = FontWeights.Normal,

        HorizontalAlignment = CanvasHorizontalAlignment.Center,
        VerticalAlignment = CanvasVerticalAlignment.Center,
        WordWrapping = CanvasWordWrapping.NoWrap,
        TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
        TrimmingSign = CanvasTrimmingSign.Ellipsis,


        //Direction = CanvasTextDirection.LeftToRightThenBottomToTop,
    };

    private CanvasTextFormat IndexTextFormat = new CanvasTextFormat()
    {
        FontSize = 14f,
        FontFamily = "Calibri",
        FontWeight = FontWeights.Normal,

        HorizontalAlignment = CanvasHorizontalAlignment.Center,
        VerticalAlignment = CanvasVerticalAlignment.Center,
        WordWrapping = CanvasWordWrapping.NoWrap,
        TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
        TrimmingSign = CanvasTrimmingSign.Ellipsis,
    };

    // Główne wejście renderu: zachowuje stałą kolejność rysowania wszystkich warstw.
    private void Canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
    {
        Microsoft.Graphics.Canvas.CanvasDrawingSession ds = args.DrawingSession;
        ds.Clear(backColor.Color);

        DrawHeaderLayer(ds);
        DrawCellLayer(ds);

        ds.Transform = Matrix3x2.Identity;
        DrawStaticOverlayLayer(ds);
        DrawColumnDraggingOverlay(ds);
        DrawLoadingBackGroundAsync(ds);
    }

    // Rysuje warstwę nagłówków dla wszystkich widocznych kolumn.
    private void DrawHeaderLayer(CanvasDrawingSession ds)
    {
        if (_itemType == null)
            return;

        for (int x = _Columns.Count - 1; x >= 0; x--)
        {
            var column = _Columns[x];
            if (column.IsOnScreen)
            {
                ds.Transform = Matrix3x2.CreateTranslation((float)(column.LeftOnScreen), 0);
                OnDrawHeader(ds, column);
            }
        }

        ds.Transform = Matrix3x2.Identity;
    }

    // Rysuje warstwę komórek dla widocznego fragmentu danych.
    private void DrawCellLayer(CanvasDrawingSession ds)
    {
        if (_ItemsSourceFiltred == null || _ItemsSourceFiltred.Count <= 0)
            return;

        for (int x = _Columns.Count - 1; x >= 0; x--)
        {
            var column = _Columns[x];
            if (column.IsOnScreen)
            {
                for (int i = 0; i < _MaxIndexOnScreen; i++)
                {
                    int rowIndex = i + _TopRowIndex;
                    ds.Transform = Matrix3x2.CreateTranslation((float)(column.LeftOnScreen), _HeaderHeight + (i * _RowsHeight));
                    column.OnDrawCell(ds, rowIndex);
                }
            }
        }
    }

    // Rysuje elementy stałe nakładane po nagłówkach i komórkach.
    private void DrawStaticOverlayLayer(CanvasDrawingSession ds)
    {
        DrawIndex(ds);
        DrawVerticleLines(ds);
        DrawHorizontalLines(ds);
        DrawNoData(ds);
    }

    // Rysuje overlay przeciąganej kolumny ponad pozostałymi warstwami.
    private void DrawColumnDraggingOverlay(CanvasDrawingSession ds)
    {
        if (ColumnDraging == null || !ColumnDraging.IsDraging || _ScrollMoverHorizontal.IsWorking)
            return;

        DrawPointer(ds, ColumnDragingDropPoint, _HeaderHeight + 1);
        ds.DrawLine((float)ColumnDragingDropPoint, 0, (float)ColumnDragingDropPoint, _HeaderHeight, ActiveCellborderColor.Color, 0.5f);
        ds.DrawRectangle((float)ColumnDraging.DragingOffset, 36, (float)ColumnDraging.Width, _HeaderHeight - 1, borderColor.Color, 1f);
        ds.FillRectangle((float)ColumnDraging.DragingOffset, 36, (float)ColumnDraging.Width, _HeaderHeight - 1, HeaderBackColor.Color);
        ds.DrawText(ColumnDraging.Title, (float)ColumnDraging.DragingOffset + 6f, 36, (float)ColumnDraging.Width - 12f, _HeaderHeight - 0, textColor.Color, HeaderTextFormat);
    }

    // Rysuje wskaźnik miejsca upuszczenia podczas przeciągania kolumny.
    private void DrawPointer(CanvasDrawingSession ds, double x, double y)
    {
        ds.FillEllipse((float)ColumnDragingDropPoint, (float)y + 6f, 4, 4, ActiveCellborderColor.Color);

        var points = new Vector2[]
        {
            new((float)x, (float)y),
            new((float)(x + 3), (float)(y + 4)),
            new((float)(x - 3), (float)(y + 4))
        };
        using var triangle = CanvasGeometry.CreatePolygon(ds, points);
        ds.FillGeometry(triangle, ActiveCellborderColor.Color);
    }

    // Nakłada tło blokady nad całą kontrolką, gdy render ma być nieaktywny.
    private void DrawLoadingBackGroundAsync(CanvasDrawingSession ds)
    {
        if (IsEnabled) return;
        var rect = new Rect(0, 0, _Canvas.ActualWidth, _Canvas.ActualHeight);

        ds.FillRectangle(rect, LoadingBackColor);
    }

    // Składa render pojedynczego nagłówka z kolejnych etapów rysowania.
    public void OnDrawHeader(CanvasDrawingSession ds, Column column)
    {
        if (DrawDraggedHeaderPlaceholder(ds, column))
            return;

        DrawHeaderBackground(ds, column);
        DrawHeaderSelection(ds, column);
        DrawHeaderResizeHandle(ds, column);
        DrawHeaderContent(ds, column);
    }

    // Zostawia tło w oryginalnym miejscu nagłówka podczas przeciągania.
    private bool DrawDraggedHeaderPlaceholder(CanvasDrawingSession ds, Column column)
    {
        if (!column.IsDraging)
            return false;

        ds.FillRectangle(0, 0, (float)column.Width, _HeaderHeight, HeaderBackColor.Color);
        return true;
    }

    // Rysuje bazowe tło nagłówka zależnie od stanu wciśnięcia.
    private void DrawHeaderBackground(CanvasDrawingSession ds, Column column)
    {
        if (column.IsPressed)
        {
            ds.FillRectangle(0, 0, (float)column.Width, _HeaderHeight, HeaderBackColorPressed.Color);
            return;
        }

        ds.FillRectangle(0, 0, (float)column.Width, _HeaderHeight, HeaderBackColor.Color);
    }

    // Nakłada styl zaznaczenia aktywnego nagłówka.
    private void DrawHeaderSelection(CanvasDrawingSession ds, Column column)
    {
        if (!IsHeaderSelected(column))
            return;

        ds.FillRectangle(0, 0, (float)column.Width, _HeaderHeight, ActiveRowFillColor.Color);
        ds.DrawLine(0, _HeaderHeight - 1.0f, (float)(column.Width), _HeaderHeight - 1.0f, ActiveCellborderColor.Color, 2f);
    }

    // Rysuje uchwyt resize nagłówka w stanie hover albo drag.
    private void DrawHeaderResizeHandle(CanvasDrawingSession ds, Column column)
    {
        using var strokeStyle = new CanvasStrokeStyle
        {
            DashStyle = CanvasDashStyle.Solid,
            StartCap = CanvasCapStyle.Round,
            EndCap = CanvasCapStyle.Round,
            LineJoin = CanvasLineJoin.Round
        };

        if (!column.IsResizing && IsHeaderHovered(column) && isHeaderSeparatorHover)
        {
            ds.DrawLine((float)(column.Width), (float)(_HeaderHeight * 0.5 - 6), (float)(column.Width), (float)(_HeaderHeight * 0.5 + 6), indexBorderColor.Color, 3f, strokeStyle);
            return;
        }

        if (column.IsResizing)
            ds.DrawLine((float)(column.Width), (float)(_HeaderHeight * 0.5 - 6), (float)(column.Width), (float)(_HeaderHeight * 0.5 + 6), ActiveCellborderColor.Color, 3f, strokeStyle);
    }

    // Rysuje tytuł nagłówka i rezerwuje miejsce na menu, gdy jest potrzebne.
    private void DrawHeaderContent(CanvasDrawingSession ds, Column column)
    {
        if (ShouldDrawHeaderMenu(column))
        {
            DrawHeaderMenuIcon(ds, column);
            ds.DrawText(column.Title, 6f, 0, (float)column.Width - 32f, _HeaderHeight - 0, GetHeaderTitleColor(column), HeaderTextFormat);
            return;
        }

        ds.DrawText(column.Title, 6f, 0, (float)column.Width - 12f, _HeaderHeight - 0, GetHeaderTitleColor(column), HeaderTextFormat);
    }

    // Rysuje ikonę menu lub filtra dla aktywnego nagłówka.
    private void DrawHeaderMenuIcon(CanvasDrawingSession ds, Column column)
    {
        var iconColor = column.IsFiltered ? ActiveCellborderColor.Color : HeaderIconColor;

        if (isHeaderMenuHover)
        {
            ds.DrawText("\uE71C", (float)(column.Width - 26), (_HeaderHeight * 0.5f) - 11.5f, 22f, 22f, SelectedHeaderIconColor, IconTextFormat);
            return;
        }

        if (column.IsFiltered)
        {
            ds.DrawText("\uE71C", (float)(column.Width - 26), (_HeaderHeight * 0.5f) - 11.5f, 22f, 22f, HoverHeaderIconColor, IconTextFormat);
            return;
        }

        ds.DrawText("\uE71C", (float)(column.Width - 26), (_HeaderHeight * 0.5f) - 11.5f, 22f, 22f, iconColor, IconTextFormat);
    }

    // Zwraca kolor tytułu zgodny ze stanem zaznaczenia nagłówka.
    private Windows.UI.Color GetHeaderTitleColor(Column column)
    {
        return IsHeaderSelected(column) ? selectedIndexTextColor : indexTextColor.Color;
    }

    // Określa, czy nagłówek powinien pokazać obszar menu.
    private bool ShouldDrawHeaderMenu(Column column)
    {
        return IsHeaderHovered(column) || column.IsFiltered;
    }

    // Sprawdza, czy dany nagłówek jest aktualnie pod kursorem.
    private bool IsHeaderHovered(Column column)
    {
        return HeaderHover != null && HeaderHover.Name == column.Name;
    }

    // Sprawdza, czy dany nagłówek należy do zaznaczenia.
    private bool IsHeaderSelected(Column column)
    {
        return column.isSelected || _SelectRange.IsInclude(column);
    }

    // Rysuje kolumnę indeksów oraz jej wewnętrzne elementy.
    private void DrawIndex(CanvasDrawingSession ds)
    {
        ds.FillRectangle(0f, 0f, (float)_IndexColWidth, (float)_Canvas.ActualHeight, HeaderBackColor.Color);

        DrawIndexRows(ds);
        DrawIndexBorders(ds);
    }

    // Rysuje numery wierszy i ich zaznaczenie w kolumnie indeksu.
    private void DrawIndexRows(CanvasDrawingSession ds)
    {
        int j = (int)_ScrollVertical.Value + 1;
        for (float y = _HeaderHeight; y <= _Canvas.ActualHeight; y += _RowsHeight)
        {
            if ((_SelectedRows != null && _SelectedRows[j - 1]) || _SelectRange.IsInclude(j - 1))
            {
                ds.FillRectangle(0f, y, (float)_IndexColWidth, _RowsHeight, ActiveRowFillColor.Color);
                ds.DrawLine((float)_IndexColWidth, y, (float)_IndexColWidth, y + _RowsHeight, ActiveCellborderColor.Color, 2f);
                ds.DrawText((j++).ToString(), 0f, y, (float)_IndexColWidth, _RowsHeight, selectedIndexTextColor, IndexTextFormat);

            }
            else
            {
                ds.DrawText((j++).ToString(), 0f, y, (float)_IndexColWidth, _RowsHeight, indexTextColor.Color, IndexTextFormat);

            }
        }
    }

    // Rysuje granice kolumny indeksu i linię pod nagłówkami.
    private void DrawIndexBorders(CanvasDrawingSession ds)
    {
        ds.DrawLine((float)(_IndexColWidth) + 0.5f, 0, (float)(_IndexColWidth) + 0.5f, (float)_Canvas.ActualHeight, borderColor.Color, 0.5f);
        ds.DrawLine((float)(_IndexColWidth + _PinedColWidth) + 0.5f, 0, (float)(_IndexColWidth + _PinedColWidth) + 0.5f, (float)_Canvas.ActualHeight, borderColor.Color, 0.5f);
        ds.DrawLine(0f, _HeaderHeight - 0.5f, (float)_Canvas.ActualWidth, _HeaderHeight - 0.5f, borderColor.Color, 0.5f);
        ds.DrawLine(0f, _HeaderHeight - 0.5f, (float)_Canvas.ActualWidth, _HeaderHeight - 0.5f, ActiveCellborderColor.Color, 0.5f);
    }

    // Rysuje pionowe podziały pomiędzy widocznymi kolumnami.
    private void DrawVerticleLines(CanvasDrawingSession ds)
    {
        for (int i = _Columns.Count - 1; i >= 0; i--)
        {
            var column = _Columns[i];
            if (!column.IsOnScreen)
                continue;
            ds.DrawLine((float)(column.LeftOnScreen + column.Width - 0.5f), 0f, (float)(column.LeftOnScreen + column.Width - 0.5f), (float)_Canvas.ActualHeight, borderColor.Color, 0.5f);
        }
    }

    // Rysuje poziome podziały pomiędzy kolejnymi wierszami.
    private void DrawHorizontalLines(CanvasDrawingSession ds)
    {
        int j = (int)_ScrollVertical.Value + 1;

        for (float y = _HeaderHeight; y <= _Canvas.ActualHeight; y += _RowsHeight)
        {
            ds.DrawLine(0f, y - 0.5f, (float)_Canvas.ActualWidth, y - 0.5f, borderColor.Color, 0.5f);
        }
    }

    // Pokazuje stan pusty, gdy po filtracji nie ma danych do narysowania.
    private void DrawNoData(CanvasDrawingSession ds)
    {
        if (_ItemsSourceFiltred != null && _ItemsSourceFiltred.Count > 0) return;
        var rect = new Rect((_Canvas.ActualWidth * 0.5d - 150d), (_Canvas.ActualHeight * 0.5d - 40d), 300d, 80d);
        ds.FillRectangle(rect, backColor.Color);
        ds.DrawRectangle(rect, ActiveCellborderColor.Color, 0.5f);
        ds.DrawText("No data available to display", rect, indexTextColor.Color, TextFormatCenter);

    }


}
