using InABox.Clients;
using InABox.Core;
using sun.tools.tree;
using Syncfusion.Data;
using Syncfusion.UI.Xaml.Grid;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms.VisualStyles;

namespace InABox.DynamicGrid;

internal interface IDynamicGridGridUIComponent<T>
{
    IList<DynamicColumnBase> ColumnList { get; }

    int RowHeight { get; }
}

internal static class DynamicGridGridUIComponentExtension
{
    public static Dictionary<int, double> CalculateColumnSizes<T>(this IDynamicGridGridUIComponent<T> component, double width)
    {
        var fAvailWidth = width - (SystemParameters.VerticalScrollBarWidth);
        
        //if (Data.Rows.Count * (DataGrid.RowHeight + 1) + DataGrid.HeaderRowHeight > height + 0.5F)
        //if (height < DataGrid.AutoScroller.VScrollBar.Maximum)
        //    fAvailWidth -= (SystemParameters.VerticalScrollBarWidth + 0.75);


        double fCurWidth = 0.0F;
        var NumAutoCols = 0;

        var colWidths = new Dictionary<int, double>();
        for (var i = 0; i < component.ColumnList.Count; i++)
        {
            var col = component.ColumnList[i];
            if (col is DynamicImageColumn dic)
            {
                colWidths[i] = component.RowHeight;
                fCurWidth += colWidths[i];
            }
            else if (col is DynamicTextColumn dxc)
            {
                colWidths[i] = dxc.Width;
                if (dxc.Width != 0)
                    fCurWidth += Math.Max(0.0F, dxc.Width);
                else
                    NumAutoCols++;
            }
            else if (col is DynamicTemplateColumn dtc)
            {
                colWidths[i] = dtc.Width;
                if (dtc.Width != 0)
                    fCurWidth += Math.Max(0.0F, dtc.Width);
                else
                    NumAutoCols++;
            }
            else if (col is DynamicGridColumn dgc)
            {
                colWidths[i] = dgc.Width;
                if (dgc.Width != 0)
                    fCurWidth += Math.Max(0.0F, dgc.Width);
                else
                    NumAutoCols++;
            }
        }

        if (NumAutoCols > 0)
        {
            var fAutoWidth = (fAvailWidth - fCurWidth) / NumAutoCols;
            if (fAutoWidth < 100)
                fAutoWidth = 100;
            for (var i = 0; i < component.ColumnList.Count; i++)
                if (colWidths[i] == 0)
                    colWidths[i] = fAutoWidth;
        }

        return colWidths;
    }

    public static int DesiredWidth<T>(this IDynamicGridGridUIComponent<T> component)
    {
        var result = 0;
        for (var i = 0; i < component.ColumnList.Count; i++)
        {
            var col = component.ColumnList[i];
            if (col is DynamicActionColumn)
            {
                result += (int)component.RowHeight;
            }
            else if (col is DynamicGridColumn)
            {
                var dgc = (DynamicGridColumn)col;
                result += dgc.Width > 0 ? dgc.Width : 300;
            }
        }

        return result;
    }

    public static bool CreateEditorColumn<T>(this IDynamicGridGridUIComponent<T> component, DynamicGridColumn column,
        [NotNullWhen(true)] out IDynamicGridEditorColumn? newcol,
        [NotNullWhen(true)] out IProperty? prop)
        where T : BaseObject
    {
        try
        {
            prop = DatabaseSchema.Property(typeof(T), column.ColumnName);
        }
        catch (Exception e)
        {
            Logger.Send(LogType.Error, ClientFactory.UserID,
                string.Format("Error constructing Column [{0}] : {1}\n{2}", column.ColumnName, e.Message, e.StackTrace));
            prop = null;
        }
        newcol = null;

        if (prop != null)
        {
            if (prop.Editor is IntegerEditor)
                newcol = new DynamicGridIntegerColumn<T>(column);
            else if (prop.Editor is CurrencyEditor)
                newcol = new DynamicGridCurrencyColumn<T>(column);
            else if (prop.Editor is DoubleEditor)
                newcol = new DynamicGridDoubleColumn<T>(column);
            else if (prop.Editor is DateTimeEditor)
                newcol = new DynamicGridDateTimeColumn<T>(column);
            else if (prop.Editor is DateEditor)
                newcol = new DynamicGridDateColumn<T>(column);
            else if (prop.Editor is TimeOfDayEditor)
                newcol = new DynamicGridTimeOfDayColumn<T>(column);
            else if (prop.Editor is TimestampEditor)
                newcol = new DynamicGridTimeStampColumn<T>(column);
            else if (prop.Editor is DurationEditor)
                newcol = new DynamicGridDurationColumn<T>(column);
            else if (prop.Editor is CheckBoxEditor)
                newcol = new DynamicGridCheckBoxColumn<T>(column);
            else if (prop.Editor is ColorEditor)
                newcol = new DynamicGridColorColumn<T>(column, column.Width, component.RowHeight);
            else if (prop.Editor is PopupEditor)
                newcol = new DynamicGridPopupColumn<T>(column);
            else if (prop.Editor is CodePopupEditor)
                newcol = new DynamicGridCodePopupColumn<T>(column);
            else if (prop.Editor is EnumLookupEditor)
                newcol = new DynamicGridEnumLookupColumn<T>(column);
            else if (prop.Editor is ComboLookupEditor)
                newcol = new DynamicGridComboLookupColumn<T>(column);
            else if (prop.Editor is LookupEditor)
                newcol = new DynamicGridLookupColumn<T>(column);
            else if (prop.Editor is MemoEditor)
                newcol = new DynamicGridMemoColumn<T>(column);
            else if (prop.Editor is CodeEditor)
                newcol = new DynamicGridCodeColumn<T>(column);                    
            else if (prop.Editor is UniqueCodeEditor)
                newcol = new DynamicGridUniqueCodeColumn<T>(column);
            else if (prop.Editor is TextBoxEditor)
                newcol = new DynamicGridTextBoxColumn<T>(column);

            return newcol is not null;
        }
        else
        {
            return false;
        }
    }

    private static bool FilterByPredicate(CoreRow row, string column, FilterPredicate predicate)
    {
        var value = row[column];
        var vStr = value?.ToString()?.ToLower() ?? "";
        var pValue = predicate.FilterValue;
        var pStr = pValue?.ToString()?.ToLower() ?? "";
        return predicate.FilterType switch
        {
            FilterType.Contains => vStr.Contains(pStr),
            FilterType.EndsWith => vStr.EndsWith(pStr),
            FilterType.Equals => vStr.Equals(pStr),
            FilterType.GreaterThan => vStr.CompareTo(pStr) > 0,
            FilterType.GreaterThanOrEqual => vStr.CompareTo(pStr) >= 0,
            FilterType.LessThan => vStr.CompareTo(pStr) < 0,
            FilterType.LessThanOrEqual => vStr.CompareTo(pStr) <= 0,
            FilterType.NotContains => !vStr.Contains(pStr),
            FilterType.NotEndsWith => !vStr.EndsWith(pStr),
            FilterType.NotEquals => !vStr.Equals(pStr),
            FilterType.NotStartsWith => !vStr.StartsWith(pStr),
            FilterType.StartsWith => vStr.StartsWith(pStr),
            _ => true,
        };
    }

    public static Func<CoreRow, bool>? ConvertColumnPredicates(DynamicGridColumn gridColumn, IEnumerable<FilterPredicate> predicates)
    {
        Func<CoreRow, bool>? rowPredicate = null;
        foreach (var predicate in predicates)
        {
            var p = (CoreRow row) => FilterByPredicate(row, gridColumn.ColumnName, predicate);
            if(rowPredicate is null)
            {
                rowPredicate = p;
            }
            else
            {
                var prevP = rowPredicate;
                if(predicate.PredicateType == PredicateType.And)
                {
                    rowPredicate = r => prevP(r) && p(r);
                }
                else
                {
                    rowPredicate = r => prevP(r) || p(r);
                }
            }
        }
        return rowPredicate;
    }
}
