using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls;
using InABox.Core;
using InABox.Wpf;
using Syncfusion.Data;

namespace InABox.DynamicGrid;


public abstract class DynamicColumnBase : BaseObject, IDynamicColumnBase
{
    public void DoEntityChanged(string columnname, Dictionary<string,object?> changes)
    {
        EntityChanged?.Invoke(this, new DynamicColumnEntityChangedEventArgs(columnname, changes));
    }

    public event DynamicColumnEntityChangedEvent? EntityChanged;
    
    public object? Tag { get; set; }
    
}

public class DynamicGridOptions
{
    public event Action? OnChanged;

    private int _enabled = 0;
    private bool _changed = false;

    public bool Enabled
    {
        get => _enabled == 0;
        set
        {
            if (value)
                EndUpdate();
            else
                BeginUpdate();
        }
    }

    public DynamicGridOptions Clear()
    {
		BeginUpdate();
        AddRows = false;
        EditRows = false;
        DeleteRows = false;
        FilterRows = false;
        SelectColumns = false;
        ExportData = false;
        ImportData = false;
        MultiSelect = false;
        DragSource = false;
        DragTarget = false;
		ReorderRows = false;
        DirectEdit = false;
        ShowHelp = false;
        Print = false;
        RecordCount = false;
        HideDatabaseFilters = false;
        HideDirectEditButton = false;
		PageSize = 0;
		NonModalEditorHost = null;
		ReadOnly = false;
        return EndUpdate();
    }

    public DynamicGridOptions BeginUpdate()
    {
        ++_enabled;
        return this;
    }

    private DynamicGridOptions Changed()
    {
        if (_enabled == 0)
        {
            _changed = false;
            OnChanged?.Invoke();
        }
        else
        {
            _changed = true;
        }
        return this;
    }

    public DynamicGridOptions EndUpdate()
    {
        --_enabled;
        if(_changed)
        {
            Changed();
        }
        return this;
    }

    private bool _addRows;
    public bool AddRows
	{
		get => _addRows && !ReadOnly;
		set
		{
			if(_addRows != value)
			{
				_addRows = value;
				Changed();
			}
		}
	}
    private bool _editRows;
    public bool EditRows
	{
		get => _editRows && !ReadOnly;
		set
		{
			if(_editRows != value)
			{
				_editRows = value;
				Changed();
			}
		}
	}
    private bool _deleteRows;
    public bool DeleteRows
	{
		get => _deleteRows && !ReadOnly;
		set
		{
			if(_deleteRows != value)
			{
				_deleteRows = value;
				Changed();
			}
		}
	}
    private bool _filterRows;
    public bool FilterRows
	{
		get => _filterRows;
		set
		{
			if(_filterRows != value)
			{
				_filterRows = value;
				Changed();
			}
		}
	}
    private bool _selectColumns;
    public bool SelectColumns
	{
		get => _selectColumns;
		set
		{
			if(_selectColumns != value)
			{
				_selectColumns = value;
				Changed();
			}
		}
	}
    private bool _exportData;
    public bool ExportData
	{
		get => _exportData;
		set
		{
			if(_exportData != value)
			{
				_exportData = value;
				Changed();
			}
		}
	}
    private bool _importData;
    public bool ImportData
	{
		get => _importData;
		set
		{
			if(_importData != value)
			{
				_importData = value;
				Changed();
			}
		}
	}
    private bool _multiSelect;
    public bool MultiSelect
	{
		get => _multiSelect;
		set
		{
			if(_multiSelect != value)
			{
				_multiSelect = value;
				Changed();
			}
		}
	}
    private bool _dragSource;
    public bool DragSource
	{
		get => _dragSource;
		set
		{
			if(_dragSource != value)
			{
				_dragSource = value;
				Changed();
			}
		}
	}
    private bool _dragTarget;
    public bool DragTarget
	{
		get => _dragTarget;
		set
		{
			if(_dragTarget != value)
			{
				_dragTarget = value;
				Changed();
			}
		}
	}
    private bool _reorderRows;
	/// <summary>
	/// Allow re-ordering rows within this grid, including cut+paste functionality, and row dragging.
	/// This is never <see langword="true"/> if <see cref="EditRows"/> is not enabled.
	/// </summary>
	/// <remarks>
	/// <see cref="DragSource"/> and <see cref="DragTarget"/> deal with external dragging, whereas this deals with internal dragging.
	/// <br/>
	/// Note that this requires the <see cref="DynamicGrid{T}.MoveRows(CoreRow[], int)"/> function; the default implementation relies on the items
	/// being <see cref="ISequenceable"/>, but this can be overriden.
	/// </remarks>
	public bool ReorderRows
	{
		get => _reorderRows && EditRows;
		set
		{
			if(_reorderRows != value)
			{
				_reorderRows = value;
				Changed();
			}
		}
	}
    private bool _directEdit;
    public bool DirectEdit
	{
		get => _directEdit && !ReadOnly;
		set
		{
			if(_directEdit != value)
			{
				_directEdit = value;
				Changed();
			}
		}
	}
    private bool _showHelp;
    public bool ShowHelp
	{
		get => _showHelp;
		set
		{
			if(_showHelp != value)
			{
				_showHelp = value;
				Changed();
			}
		}
	}
    private bool _print;
    public bool Print
	{
		get => _print;
		set
		{
			if(_print != value)
			{
				_print = value;
				Changed();
			}
		}
	}
    private bool _recordCount;
    public bool RecordCount
	{
		get => _recordCount;
		set
		{
			if(_recordCount != value)
			{
				_recordCount = value;
				Changed();
			}
		}
	}
    private bool _hideDatabaseFilters;
    public bool HideDatabaseFilters
	{
		get => _hideDatabaseFilters;
		set
		{
			if(_hideDatabaseFilters != value)
			{
				_hideDatabaseFilters = value;
				Changed();
			}
		}
	}
    private bool _hideDirectEditButton;
    public bool HideDirectEditButton
	{
		get => _hideDirectEditButton;
		set
		{
			if(_hideDirectEditButton != value)
			{
				_hideDirectEditButton = value;
				Changed();
			}
		}
	}

	private ISubPanelHost? _nonModalEditorHost;
	public ISubPanelHost? NonModalEditorHost
	{
		get => _nonModalEditorHost;
		set
		{
			if(_nonModalEditorHost != value)
			{
				_nonModalEditorHost = value;
				Changed();
			}
		}
	}

	private int _pageSize = 0;
	/// <summary>
	/// The page size for loading data in pages; set to 0 for no paging functionality.
	/// </summary>
	public int PageSize
	{
		get => _pageSize;
		set
		{
			if(_pageSize != value)
			{
				_pageSize = value;
				Changed();
			}
		}
	}

	public bool _readOnly = false;
	/// <summary>
	/// Specifies whether this grid is "read-only"; if this is <see langword="true"/>, then the <see cref="AddRows"/>, <see
	/// cref="EditRows"/>, <see cref="DeleteRows"/> and <see cref="DirectEdit"/> will be disabled.
	/// </summary>
	/// <remarks>
	/// Setting this property can improve performance, since it allows the grid to not load <see cref="LookupFactory.RequiredColumns(Type)"/>.
	/// </remarks>
	public bool ReadOnly
	{
		get => _readOnly;
		set
		{
			if(_readOnly != value)
			{
				_readOnly = value;
				Changed();
			}
		}
	}
}

public delegate bool OnFilterRecord(CoreRow row);

public delegate void OnCreateItem(object sender, BaseObject item);

public delegate bool OnAfterCreateItem(object sender, BaseObject item);

public delegate T OnCreateItem<T>();

public delegate void OnDefineLookup(ILookupEditorControl editor);

public delegate void OnGridCustomiseEditor(DynamicEditorGrid sender, DynamicGridColumn column, BaseEditor editor);

public delegate void OnFormCustomiseEditor(IDynamicEditorForm sender, object[] items, DynamicGridColumn column, BaseEditor editor);

public delegate void ValidateEvent<T>(DynamicGrid<T> sender, T[] items, List<string> errors)
	where T : BaseObject, new();

/// <summary>
/// 
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sender"></param>
/// <param name="items">The array of items being edited; <see langword="null"/> is synonymous with an empty array.</param>
/// <param name="column"></param>
/// <param name="editor"></param>
public delegate void OnCustomiseEditor<T>(IDynamicEditorForm sender, T[]? items, DynamicGridColumn column, BaseEditor editor);

public delegate void OnCustomiseEditor(IDynamicEditorForm sender, object[]? items, DynamicGridColumn column, BaseEditor editor);

public delegate void OnLoadEditorButtons<T>(T item, DynamicEditorButtons buttons);

public delegate void OnReconfigureEditors(DynamicEditorGrid sender);

public delegate void OnCreateEditorControl(string column, BaseEditor editor, IDynamicEditorControl control);

public class AfterEditorValueChangedArgs
{
    public string ColumnName { get; set; }

    public Dictionary<string, object?> ChangedValues { get; set; }

    public AfterEditorValueChangedArgs(string columnName, Dictionary<string, object?> changedValues)
    {
        ColumnName = columnName;
        ChangedValues = changedValues;
    }
}

public delegate Dictionary<string, object?>? OnAfterEditorValueChanged(DynamicEditorGrid sender, AfterEditorValueChangedArgs args);

//public delegate void OnGridChanged(IDynamicGrid sender);

public delegate void OnLoadPage(IDynamicEditorPage page);

public delegate void OnSelectPage(DynamicEditorGrid sender, BaseObject[]? items);

public delegate void OnUnloadPage(IDynamicEditorPage page, bool saved);

public delegate void OnCustomiseColumns(object sender, DynamicGridColumns columns);

public delegate BaseEditor? OnGetEditor(DynamicGridColumn column);

public delegate decimal OnGetEditorSequence(DynamicGridColumn column);

public delegate IFilter? OnDefineLookupFilter(Type type, string column);

public delegate IFilter? OnDefineFilter(Type type);

public delegate IList<string>? OnValidateData(IDynamicEditorForm sender, BaseObject[] items);

public delegate void OnPrintData(object sender);

public delegate void EntitySaveEvent(IDynamicEditorForm editor, BaseObject[] items);

public delegate bool DynamicGridButtonClickEvent(Button button, CoreRow[] rows);

public delegate void OnContextMenuOpening<TKey>(CoreTreeNode<TKey>? node, ContextMenu menu);


public class DynamicGridSelectionEventArgs : CancelEventArgs
{
    public DynamicGridSelectionEventArgs(CoreRow[]? rows)
    {
        Rows = rows;
    }

    public CoreRow[]? Rows { get; }
}

public delegate void SelectItemHandler(object sender, DynamicGridSelectionEventArgs e);

public delegate void OnDoubleClick(object sender, HandledEventArgs args);

public class DynamicGridCellClickEventArgs : HandledEventArgs
{
    public CoreRow? Row { get; set; }

    public DynamicColumnBase? Column { get; set; }

    public DynamicGridCellClickEventArgs(CoreRow? row, DynamicColumnBase? column)
    {
        Row = row;
        Column = column;
    }
}

public delegate void OnCellDoubleClick(object sender, DynamicGridCellClickEventArgs args);

public class BeforeRefreshEventArgs : CancelEventArgs { }
public delegate void BeforeRefreshEventHandler(object sender, BeforeRefreshEventArgs args);

public class AfterRefreshEventArgs : EventArgs { }
public delegate void AfterRefreshEventHandler(object sender, AfterRefreshEventArgs args);

public class GenerateColumnsEventArgs
{
	public DynamicGridColumns Columns { get; private set; }

	public GenerateColumnsEventArgs(DynamicGridColumns columns)
	{
		Columns = columns;
	}
}
public delegate void GenerateColumnsEvent(object sender, GenerateColumnsEventArgs args);

public class SaveColumnsEventArgs
{
	public DynamicGridColumns Columns { get; private set; }

	public SaveColumnsEventArgs(DynamicGridColumns columns)
	{
		Columns = columns;
	}
}
public delegate void SaveColumnsEvent(object sender, SaveColumnsEventArgs args);

public class GetAvailableColumnsEventArgs(IEnumerable<DynamicGridColumn> columns)
{
    public IEnumerable<DynamicGridColumn> Columns { get; set; } = columns;
}
public delegate void GetAvailableColumnsEvent(GetAvailableColumnsEventArgs args);