using System.Reflection;

namespace Pilz.Collections.SimpleHistory;

public class ObjectState : ObjectBase
{
    /// <summary>
    /// The Object including the members to patch.
    /// </summary>
    /// <returns></returns>
    public object? Object { get; set; } = null;

    /// <summary>
    /// The name of the Member to patch.
    /// </summary>
    /// <returns></returns>
    public string MemberName { get; set; } = "";

    /// <summary>
    /// The Value that should be patched.
    /// </summary>
    /// <returns></returns>
    public object? ValueToPatch { get; set; } = null;

    /// <summary>
    /// The member types to include at searching for the member.
    /// </summary>
    /// <returns></returns>
    public ObjectValueType MemberType { get; set; } = ObjectValueType.Field;

    /// <summary>
    /// The Binding Flags that are used at searching for the member.
    /// </summary>
    /// <returns></returns>
    public BindingFlags MemberFlags { get; set; } = BindingFlags.Default;

    /// <summary>
    /// Creates a new Instance of ObjectState from input.
    /// </summary>
    /// <param name="obj">The Object including the members to patch.</param>
    /// <param name="valname">The name of the Member to patch.</param>
    /// <param name="valToPatch">The member types to include at searching for the member.</param>
    /// <param name="valtype">The Binding Flags that are used at searching for the member.</param>
    public ObjectState(object obj, string valname, object valToPatch, ObjectValueType valtype)
    {
        Object = obj;
        MemberName = valname;
        ValueToPatch = valToPatch;
        MemberType = valtype;
    }

    /// <summary>
    /// Creates a new Instance of ObjectState.
    /// </summary>
    public ObjectState()
    {
    }

    internal void Patch()
    {
        if (Object is null)
            return;

        var t = Object.GetType();

        switch (MemberType)
        {
            case ObjectValueType.Field:
                if (t.GetField(MemberName, MemberFlags) is FieldInfo f)
                {
                    var temp = f.GetValue(Object);
                    f.SetValue(Object, ValueToPatch);
                    ValueToPatch = temp;
                }
                break;
            case ObjectValueType.Property:
                if (t.GetProperty(MemberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) is PropertyInfo p)
                {
                    var temp = p.GetValue(Object);
                    p.SetValue(Object, ValueToPatch);
                    ValueToPatch = temp;
                }
                break;
            default:
                throw new Exception("ValueType is invalid!");
        }
    }
}