using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using UnityEngine;

namespace RebootKit.Engine.Foundation {
    [AttributeUsage(AttributeTargets.Field)]
    public class ConfigVarAttribute : Attribute {
        public ConfigVarAttribute(string name, float defaultValue, string description = "", CVarFlags flags = CVarFlags.Client) {
            Name = name;
            Description = description;
            Value = new CVarValue(defaultValue);
            Flags = flags;
        }

        public ConfigVarAttribute(string name, double defaultValue, string description = "", CVarFlags flags = CVarFlags.Client) {
            Name = name;
            Description = description;
            Value = new CVarValue(defaultValue);
            Flags = flags;
        }

        public ConfigVarAttribute(string name, string defaultValue, string description = "",  CVarFlags flags = CVarFlags.Client) {
            Name = name;
            Description = description;
            Value = new CVarValue(defaultValue);
            Flags = flags;
        }

        public ConfigVarAttribute(string name, int defaultValue, string description = "", CVarFlags flags = CVarFlags.Client) {
            Name = name;
            Description = description;
            Value = new CVarValue(defaultValue);
            Flags = flags;
        }

        internal string Name { get; }
        internal string Description { get; }
        internal CVarValue Value { get; }
        internal CVarFlags Flags { get; }
    }

    public static class ConfigVarsContainer {
        static readonly Logger s_logger = new Logger(nameof(ConfigVarsContainer));

        static readonly List<ConfigVar> s_configVars = new List<ConfigVar>();

        public static void Register(ConfigVar cvar) {
            if (Get(cvar.name) != null) {
                throw new Exception($"CVar with name '{cvar.name}' already exists");
            }

            s_configVars.Add(cvar);
            s_logger.Info($"Registered CVar: {cvar.name}");
        }

        public static void Clear() {
            s_configVars.Clear();
            s_logger.Info("Cleared all CVars");
        }

        public static ConfigVar Get(string name) {
            foreach (ConfigVar configVar in s_configVars) {
                if (configVar.name.Equals(name, StringComparison.Ordinal)) {
                    return configVar;
                }
            }
            
            return null;
        }

        public static IEnumerable<ConfigVar> All() => s_configVars;

        public static void Init() {
            Clear();

            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

            foreach (Assembly assembly in assemblies) {
                Type[] types = assembly.GetTypes();

                foreach (Type type in types) {
                    FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);

                    foreach (FieldInfo field in fields) {
                        if (Attribute.IsDefined(field, typeof(ConfigVarAttribute))) {
                            ConfigVarAttribute cvarAttribute = field.GetCustomAttribute<ConfigVarAttribute>();

                            ConfigVar cvar = Get(cvarAttribute.Name);
                            if (cvar == null) {
                                cvar = new ConfigVar(cvarAttribute.Name, cvarAttribute.Value, cvarAttribute.Description) {
                                    flags = cvarAttribute.Flags
                                };
                                Register(cvar);
                            }
                            
                            field.SetValue(null, cvar);
                        }
                    }
                }
            }

            Load();
        }

        public static void Save() {
            string path = Application.persistentDataPath + "/" + RConsts.k_CVarsFilename;
            
            s_logger.Info("Saving cvars to file: " + path);

            StringBuilder sb = new StringBuilder();

            foreach (ConfigVar cvar in All()) {
                if ((cvar.flags & CVarFlags.ReadOnly) == 0) {
                    sb.AppendFormat("{0} {1}\n", cvar.name, cvar);
                }
            }

            File.WriteAllText(path, sb.ToString());
        }

        public static void Load() {
            string path = Application.persistentDataPath + "/" + RConsts.k_CVarsFilename;
            if (!File.Exists(path)) {
                s_logger.Info("CVar file not found, skipping load");
                return;
            }
            
            s_logger.Info("Loading cvars from file: " + path);
            string[] lines = File.ReadAllLines(path);
            foreach (string line in lines) {
                string[] parts = line.Split(' ');
                if (parts.Length < 2) {
                    continue;
                }

                string name = parts[0];
                string value = parts[1];

                ConfigVar cvar = Get(name);
                if (cvar != null) {
                    cvar.Value.CopyFrom(new CVarValue(value));
                    s_logger.Info($"Loaded CVar: {name} = {value}");
                } else {
                    cvar = new ConfigVar(name, value) {
                        flags = CVarFlags.User
                    };
                    cvar.ParseFromString(value);

                    Register(cvar);
                }
            }
        }
    }
}