using InABox.Core;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Xceed.Wpf.Toolkit.PropertyGrid.Converters;

namespace InABox.DynamicGrid;

public delegate void FieldChangedEvent();

public interface IDynamicFormFieldControl
{
    public event FieldChangedEvent? FieldChangedEvent;
    
    void Deserialize(DFLoadStorageEntry storage);
    void Serialize(DFSaveStorageEntry storage);

    public object? GetValue();
    /// <summary>
    /// Sets the value in this control.
    /// </summary>
    /// <param name="value">The value to set. This will be the return value from a call to <see cref="DFLayoutFieldProperties.ParseValue(object)"/>.</param>
    public void SetValue(object? value);

    public object? GetEntityValue();

    /// <summary>
    /// Gets additional data for this field by the specific field name of <paramref name="field"/>.
    /// </summary>
    /// <param name="field">A name which specifies what data is requested.</param>
    /// <returns>The additional data.</returns>
    public object? GetData(string field);

    /// <summary>
    /// Check that the data is valid - if it is not, output a message for the user.
    /// This function gets called when the user completes a form, or edits an already completed form.
    /// </summary>
    /// <param name="message">The message to the user.</param>
    /// <returns><see langword="true"/> if the data is valid.</returns>
    public bool Validate([NotNullWhen(false)] out string? message);
}

public abstract class DynamicFormFieldControl<TField, TProperties, TValue, TSerialized> : DynamicFormControl<TField>, IDynamicFormFieldControl
    where TField : DFLayoutField<TProperties>
    where TProperties : DFLayoutFieldProperties<TValue, TSerialized>, new()
{
    public event FieldChangedEvent? FieldChangedEvent;

    public TField Field { get => Control; set => Control = value; }

    public void Serialize(DFSaveStorageEntry storage)
    {
        Field.Properties.SerializeValue(storage, GetSerializedValue());
    }
    public void Deserialize(DFLoadStorageEntry storage)
    {
        SetSerializedValue(Field.Properties.DeserializeValue(storage));
    }

    protected void ChangeField() => FieldChangedEvent?.Invoke();

    /// <summary>
    /// Checks whether the user has supplied a field - for use with <see cref="Validate(out string?)"/>.
    /// </summary>
    /// <returns><see langword="true"/> if the user has not supplied a value.</returns>
    protected abstract bool IsEmpty();

    public virtual bool Validate([NotNullWhen(false)] out string? message)
    {
        if(Field.Properties.Required && IsEmpty())
        {
            message = $"Field [{Field.Name}] is required!";
            return false;
        }
        message = null;
        return true;
    }

    protected override void AfterSetControl(TField control)
    {
        base.AfterSetControl(control);
        if (!string.IsNullOrWhiteSpace(control.Properties.Expression)
            || (!control.Properties.Property.IsNullOrWhiteSpace() && control.Properties.ReadOnlyProperty))
        {
            IsEnabled = false;
        }
    }

    public abstract TSerialized GetSerializedValue();
    public abstract void SetSerializedValue(TSerialized value);

    public abstract TValue GetValue();
    public abstract void SetValue(TValue? value);

    public virtual object? GetEntityValue() => GetValue();

    public virtual object? GetData(string dataField)
    {
        return null;
    }

    object? IDynamicFormFieldControl.GetValue() => GetValue();
    void IDynamicFormFieldControl.SetValue(object? value) => SetValue(value != null ? (TValue)value : default);
}
