using System;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using InABox.Clients;
using InABox.Core;
using InABox.Scripting;
using Syncfusion.UI.Xaml.Grid;

namespace InABox.DynamicGrid;

public delegate DynamicGridStyle OnGetDynamicGridRowStyle(CoreRow row, DynamicGridStyle defaultstyle);

public interface IBaseDynamicGridStyle
{
    double FontSize { get; set; }
    FontStyle FontStyle { get; set; }
    FontWeight FontWeight { get; set; }
    Brush Background { get; set; }
    Brush Foreground { get; set; }
}

public interface IDynamicGridStyle : IBaseDynamicGridStyle
{
    DependencyProperty FontSizeProperty { get; }
    DependencyProperty FontStyleProperty { get; }
    DependencyProperty FontWeightProperty { get; }
    DependencyProperty BackgroundProperty { get; }
    DependencyProperty ForegroundProperty { get; }
}

public abstract class DynamicGridStyle : Style, IDynamicGridStyle
{
    public DynamicGridStyle(Type T) : base(T)
    {
        FontSize = 12D;
        FontWeight = FontWeights.Normal;
        FontStyle = FontStyles.Normal;
        Background = new SolidColorBrush(Colors.White);
        Foreground = new SolidColorBrush(Colors.Black);
    }

    public DynamicGridStyle(DynamicGridStyle source) : base(source.TargetType, source)
    {
        FontSize = source.FontSize;
        FontWeight = source.FontWeight;
        FontStyle = source.FontStyle;
        Background = source.Background;
        Foreground = source.Foreground;
    }

    public abstract DependencyProperty FontSizeProperty { get; }

    public abstract DependencyProperty FontStyleProperty { get; }

    public abstract DependencyProperty FontWeightProperty { get; }

    public abstract DependencyProperty BackgroundProperty { get; }

    public abstract DependencyProperty ForegroundProperty { get; }

    public double FontSize
    {
        get => Get<double>(FontSizeProperty, 8);
        set => Set(FontSizeProperty, value);
    }

    public FontStyle FontStyle
    {
        get => Get(FontStyleProperty, FontStyles.Italic);
        set => Set(FontStyleProperty, value);
    }

    public FontWeight FontWeight
    {
        get => Get(FontWeightProperty, FontWeights.DemiBold);
        set => Set(FontWeightProperty, value);
    }

    public Brush Background
    {
        get => Get<Brush>(BackgroundProperty, new SolidColorBrush(Colors.Yellow));
        set => Set(BackgroundProperty, value);
    }


    public Brush Foreground
    {
        get => Get<Brush>(ForegroundProperty, new SolidColorBrush(Colors.Green));
        set => Set(ForegroundProperty, value);
    }

    private void Set<T>(DependencyProperty property, T value)
    {
        var setter = Setters.FirstOrDefault(x => x is Setter && ((Setter)x).Property.Equals(property)) as Setter;
        if (setter == null)
        {
            Setters.Add(new Setter(property, value));
        }
        else
        {
            if (!setter.IsSealed)
                setter.Value = value;
        }
    }

    private T Get<T>(DependencyProperty property, T defaultvalue)
    {
        var setter = Setters.FirstOrDefault(x => x is Setter && ((Setter)x).Property.Equals(property)) as Setter;
        return setter != null ? (T)setter.Value : defaultvalue;
    }
}

public abstract class DynamicGridStyle<T> : DynamicGridStyle
{
    public DynamicGridStyle(IDynamicGridStyle? source) : base(typeof(T))
    {
        if (source != null)
        {
            FontSize = source.FontSize;
            FontStyle = source.FontStyle;
            FontWeight = source.FontWeight;
            Background = source.Background;
            Foreground = source.Foreground;
        }
    }
}

public abstract class DynamicGridRowStyleSelector : StyleSelector, IBaseDynamicGridStyle
{
    private static bool initialized;

    private static ScriptDocument? helper;

    private static DynamicGridStyle defaultstyle;

    protected abstract string? SectionName { get; }

    public CoreTable Data { get; set; }
    
    public double FontSize
    {
        get => defaultstyle.FontSize;
        set => defaultstyle.FontSize = value;
    }

    public FontStyle FontStyle
    {
        get => defaultstyle.FontStyle;
        set => defaultstyle.FontStyle = value;
    }

    public FontWeight FontWeight
    {
        get => defaultstyle.FontWeight;
        set => defaultstyle.FontWeight = value;
    }

    public Brush Background
    {
        get => defaultstyle.Background;
        set => defaultstyle.Background = value;
    }

    public Brush Foreground
    {
        get => defaultstyle.Foreground;
        set => defaultstyle.Foreground = value;
    }

    protected abstract DynamicGridStyle CreateStyle();

    private CoreRow? GetRow(object item)
    {
        try
        {
            var row = (item as DataRowBase)?.RowData as DataRowView;
            if (row != null)
            {
                var index = row.Row.Table.Rows.IndexOf(row.Row);
                return Data.Rows[index];
            }

            return null;
        }
        catch
        {
            return null;
        }
    }


    private void Initialize()
    {
        if (initialized)
            return;

        defaultstyle = CreateStyle();

        if(SectionName is not null)
        {
            var stylescript = ClientFactory.ClientType == null
                ? null
                : new Client<Script>()
                    .Load(new Filter<Script>(x => x.Section).IsEqualTo(SectionName).And(x => x.ScriptType).IsEqualTo(ScriptType.RowStyle))
                    .FirstOrDefault();
            if (stylescript != null)
            {
                helper = new ScriptDocument(stylescript.Code);
                if (!helper.Compile())
                    helper = null;
            }
        }

        initialized = true;
    }

    public event OnGetDynamicGridRowStyle? GetStyle;

    private DynamicGridStyle ExecuteHelper(CoreRow row, DynamicGridStyle style)
    {
        var result = style;

        try
        {
            if(helper is null)
            {
                throw new Exception("Helper is null");
            }
            helper.SetValue("Row", row);
            helper.SetValue("Background", style.Background);
            helper.SetValue("Foreground", style.Foreground);
            helper.SetValue("Style", style.FontStyle);
            helper.SetValue("Weight", style.FontWeight);
            helper.SetValue("Size", style.FontSize);

            if (helper.Execute())
            {
                result = CreateStyle();
                result.Background = (Brush)helper.GetValue("Background");
                result.Foreground = (Brush)helper.GetValue("Foreground");
                result.FontStyle = (FontStyle)helper.GetValue("Style");
                result.FontWeight = (FontWeight)helper.GetValue("Weight");
                result.FontSize = (double)helper.GetValue("Size");
            }
        }
        catch (Exception e)
        {
            Logger.Send(LogType.Information, "", "Unable to Invoke Row Style Helper: " + e.Message);
        }

        return result;
    }

    public override Style SelectStyle(object item, DependencyObject container)
    {
        Initialize();

        if (Data == null)
            return defaultstyle;

        if (item == null)
            return defaultstyle;

        var row = GetRow(item);

        if (row == null)
            return defaultstyle;

        var style = GetStyle != null ? GetStyle(row, defaultstyle) : defaultstyle;

        if (helper != null)
            return ExecuteHelper(row, style);

        return style;
    }
}

public abstract class DynamicGridRowStyleSelector<T> : DynamicGridRowStyleSelector
{
    protected override string SectionName => typeof(T).EntityName();
}

public class DynamicGridRowStyleSelector<TEntity, TStyle> : DynamicGridRowStyleSelector<TEntity> where TStyle : DynamicGridStyle, new()
{
    protected override DynamicGridStyle CreateStyle()
    {
        return new TStyle();
    }
}

public class SimpleDynamicGridRowStyleSelector<TStyle> : DynamicGridRowStyleSelector where TStyle : DynamicGridStyle, new()
{
    protected override string? SectionName => null;

    protected override DynamicGridStyle CreateStyle()
    {
        return new TStyle();
    }
}

public class DynamicGridCellStyleParameters
{
    public DynamicColumnBase Column { get; }
    
    public object DefaultValue { get; }

    public DynamicGridCellStyleParameters(DynamicColumnBase column, object? defaultValue)
    {
        Column = column;
        DefaultValue = defaultValue;
    }
}

public class DynamicGridGridCellStyleConverter<T> : IValueConverter
{

    private readonly IBaseDynamicGrid _grid;
    
    private readonly Func<CoreRow, DynamicColumnBase, T> _converter;
    
    public DynamicGridGridCellStyleConverter(IBaseDynamicGrid grid, Func<CoreRow, DynamicColumnBase, T> converter)
    {
        _grid = grid ?? throw new ArgumentNullException(nameof(grid));
        _converter = converter ?? throw new ArgumentNullException(nameof(converter));
    }

    private CoreRow? GetRow(object item)
    {
        try
        {
            if (item is DataRowView row)
            {
                var index = row.Row.Table.Rows.IndexOf(row.Row);
                return _grid.Data.Rows[index];
            }
            return null;
        }
        catch
        {
            return null;
        }
    }
    
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var row = GetRow(value);
        if (row == null)
            return DependencyProperty.UnsetValue;

        var param = parameter as DynamicGridCellStyleParameters;
        if (param == null)
            return DependencyProperty.UnsetValue;
        
        //var column = parameter as DynamicColumnBase;
        //if (column is null)
        //    return DependencyProperty.UnsetValue;
        
        return _converter.Invoke(row,param.Column) ?? param.DefaultValue;
    }
    
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

