using System;

namespace RebootKit.Engine.Services.Console {
    public enum CVarValueKind {
        Number, String
    }

    [Serializable]
    public struct CVarValue {
        public CVarValueKind Kind;

        public double NumberValue;
        public string StringValue;

        public CVarValue(int value) {
            Kind = CVarValueKind.Number;
            NumberValue = value;
            StringValue = null;
        }

        public CVarValue(float value) {
            Kind = CVarValueKind.Number;
            NumberValue = value;
            StringValue = null;
        }

        public CVarValue(double value) {
            Kind = CVarValueKind.Number;
            NumberValue = value;
            StringValue = null;
        }

        public CVarValue(string value) {
            Kind = CVarValueKind.String;
            NumberValue = 0;
            StringValue = value;
        }

        public void CopyFrom(CVarValue value) {
            Kind = value.Kind;
            NumberValue = value.NumberValue;
            StringValue = value.StringValue;
        }

        public override string ToString() {
            return Kind switch {
                CVarValueKind.Number => NumberValue.ToString(),
                CVarValueKind.String => $"\"{StringValue}\"",
                _ => throw new ArgumentOutOfRangeException()
            };
        }
    }

    [Flags]
    public enum CVarFlags {
        None, Persistent, ReadOnly
    }

    [Serializable]
    public class CVar {
        public CVarFlags Flags;
        public string Name;
        public string Description;
        public CVarValue DefaultValue;
        public CVarValue Value { get; private set; }

        public int IndexValue => (int) Value.NumberValue;
        public float FloatValue => (float) Value.NumberValue;
        public double NumberValue => Value.NumberValue;
        public string StringValue => Value.StringValue;

        public event Action OnChanged = delegate { };

        public CVar(CVar other) {
            Flags = other.Flags;
            Name = other.Name;
            Description = other.Description;
            DefaultValue = other.DefaultValue;
            Value = other.Value;
        }

        public CVar(string name, CVarValue value, string description = "") {
            Name = name;
            Description = description;
            DefaultValue = value;
            Value = DefaultValue;
        }

        public CVar(string name, int value, string description = "") {
            Name = name;
            Description = description;
            DefaultValue = new CVarValue(value);
            Value = DefaultValue;
        }

        public CVar(string name, float value, string description = "") {
            Name = name;
            Description = description;
            DefaultValue = new CVarValue(value);
            Value = DefaultValue;
        }

        public CVar(string name, double value, string description = "") {
            Name = name;
            Description = description;
            DefaultValue = new CVarValue(value);
            Value = DefaultValue;
        }

        public CVar(string name, string value, string description = "") {
            Name = name;
            Description = description;
            DefaultValue = new CVarValue(value);
            Value = DefaultValue;
        }

        public void Set(int value) {
            if (Flags.HasFlag(CVarFlags.ReadOnly)) {
                return;
            }

            Value = new CVarValue(value);
            OnChanged?.Invoke();
        }

        public void Set(float value) {
            if (Flags.HasFlag(CVarFlags.ReadOnly)) {
                return;
            }

            Value = new CVarValue(value);
            OnChanged?.Invoke();
        }

        public void Set(string value) {
            if (Flags.HasFlag(CVarFlags.ReadOnly)) {
                return;
            }

            Value = new CVarValue(value);
            OnChanged?.Invoke();
        }

        public void ParseFromString(string str) {
            if (Flags.HasFlag(CVarFlags.ReadOnly)) {
                return;
            }

            if (float.TryParse(str, out float f)) {
                Set(f);
            } else {
                Set(str);
            }
        }

        public void Reset() {
            if (Flags.HasFlag(CVarFlags.ReadOnly)) {
                return;
            }

            Value = DefaultValue;
            OnChanged?.Invoke();
        }

        public override string ToString() {
            return Value.ToString();
        }
    }
}