#define scrolling
//#define newgrid

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using InABox.Core;
using Syncfusion.Data;

namespace InABox.DynamicGrid;


public enum DynamicGridButtonPosition
{
    Left,
    Right
}


public interface IBaseDynamicGrid
{
    bool IsReady { get; }

    Thickness Margin { get; set; }

    DynamicGridColumns VisibleColumns { get; }
    DynamicActionColumns ActionColumns { get; }

    IList<DynamicColumnBase> ColumnList { get; }

    CoreTable Data { get; set; }
    CoreRow[] SelectedRows { get; set; }

    double RowHeight { get; set; }
    double HeaderHeight { get; set; }

    double FontSize { get; set; }
    void Refresh(bool columns, bool data);

    /// <summary>
    /// To be called when the grid is no longer needed.
    /// </summary>
    /// <remarks>
    ///     <b>Note:</b> There is no requirement to call this method, it just allows the grid to decide to stop loading data.
    /// </remarks>
    void Shutdown();

    void DoChanged();

    event OnFilterRecord OnFilterRecord;

    event OnDoubleClick? OnDoubleClick;

    delegate void ReconfigureEvent(DynamicGridOptions options);
    event ReconfigureEvent? OnReconfigure;

    public abstract void Reconfigure();

    /// <summary>
    /// Add <paramref name="onReconfigure"/> to <see cref="OnReconfigure"/>, and then call <see cref="Reconfigure"/>.
    /// </summary>
    /// <remarks>
    /// Probably should only be called once per grid, otherwise there will be multiple event handlers bound.<br/>
    /// If you want to reconfigure without specifiying
    /// a new reconfigure event, use <see cref="Reconfigure"/>.
    /// </remarks>
    /// <param name="onReconfigure"></param>
    public void Reconfigure(ReconfigureEvent onReconfigure);

    public DynamicGridOptions Options { get; }

    void AddVisualFilter(string column, string value, FilterType filtertype = FilterType.Contains);

    Button AddButton(string? caption, ImageSource? image, string? tooltip, DynamicGridButtonClickEvent action, DynamicGridButtonPosition position = DynamicGridButtonPosition.Left);

    Button AddButton(string? caption, ImageSource? image, DynamicGridButtonClickEvent action, DynamicGridButtonPosition position = DynamicGridButtonPosition.Left);

    void UpdateButton(Button button, ImageSource? image, string? text, string? tooltip = null);

    event OnPrintData OnPrintData;

    event BeforeRefreshEventHandler BeforeRefresh;
    
    event AfterRefreshEventHandler AfterRefresh;

    int DesiredWidth();

    void UpdateRow<TType>(CoreRow row, string column, TType value, bool refresh = true);

    void UpdateRow<T, TType>(CoreRow row, Expression<Func<T, TType>> column, TType value, bool refresh = true);

    void InvalidateRow(CoreRow row);

    object? GetData(CoreRow row, DynamicColumnBase column);

    IDynamicGridColumnFilter? GetColumnFilter(DynamicColumnBase column);
}

public interface IDynamicGrid : IBaseDynamicGrid
{
    event OnCreateItem OnCreateItem;
    
    event EntitySaveEvent? OnAfterSave;
    event EntitySaveEvent? OnBeforeSave;
    
    event GenerateColumnsEvent? OnGenerateColumns;
    event SaveColumnsEvent? OnSaveColumns;
    event GetAvailableColumnsEvent? GetAvailableColumns;

    event OnDefineFilter OnDefineFilter;

    event OnCustomiseEditor? OnCustomiseEditor;
    event OnAfterEditorValueChanged? OnAfterEditorValueChangedEvent;

    DynamicGridColumns MasterColumns { get; }

    void AddHiddenColumn(string column);

    void InitialiseEditorForm(IDynamicEditorForm editor, object[] items, Func<Type, CoreTable>? pageDataHandler = null, bool preloadPages = false);
    bool EditItems(object[] items, Func<Type, CoreTable?>? PageDataHandler = null, bool PreloadPages = false);

    DynamicGridColumns ExtractColumns(IColumns columns);
}

public interface IDynamicGrid<T> : IDynamicGrid
    where T : BaseObject, new()
{
    event OnCustomiseEditor<T>? OnCustomiseEditor;

    T CreateItem();

    T LoadItem(CoreRow row);

    void SaveItem(T item);

    void SaveItems(IEnumerable<T> items);

    void DeleteItems(CoreRow[] rows);

    bool EditItems(T[] items, Func<Type, CoreTable?>? PageDataHandler = null, bool PreloadPages = false);

    /// <summary>
    /// Update/sync the data in <paramref name="rows"/> with that of <paramref name="objs"/>.
    /// </summary>
    void UpdateRows(CoreRow[] rows, T[] objs, bool invalidateRows = true);
}