using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using IniParser;
using IniParser.Model;

namespace MicronSync
{
    /// <summary>
    /// Settings to store.
    /// </summary>
    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    internal class UserConfigValues : INotifyPropertyChanged
    {
        /// <summary>
        /// Flags value to be included in configuration.
        /// </summary>
        internal class SaveToConfigAttribute : Attribute {}

        CommonIO _CIO = new CommonIO();

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged() { UserModifiedConfig = true; }

        public static readonly Dictionary<string, string> SysVars = new Dictionary<string, string>()
        {
            {"%appdata%\\", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\"},
            {"%localappdata%\\", Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\"},
            {"%userprofile%\\", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\"},
        };

        [SaveToConfig]
        public Version MSVersion
        {
            get { return Assembly.GetExecutingAssembly().GetName().Version; }
        }

        [SaveToConfig]
        public string MSIniType { get; set; }        = "Config";

        [SaveToConfig]
        public string Description
        {
            get { return _Description; }
            set
            {
                _Description = value;
                OnPropertyChanged();
            }
        }
        private string _Description                  = string.Empty;

        [SaveToConfig]
        public bool EnablePurge
        {
            get { return _EnablePurge; }
            set
            {
                _EnablePurge = value;
                OnPropertyChanged();
            }
        }
        private bool _EnablePurge;

        [SaveToConfig]
        public bool EnableBackup
        {
            get { return _EnableBackup; }
            set
            {
                _EnableBackup = value;
                OnPropertyChanged();
            }
        }
        private bool _EnableBackup;

        [SaveToConfig]
        public bool OverwriteBackup
        {
            get { return _OverwriteBackup; }
            set
            {
                _OverwriteBackup = value;
                OnPropertyChanged();
            }
        }
        private bool _OverwriteBackup;

        [SaveToConfig]
        public int CompressionLevel
        {
            get { return _CompressionLevel; }
            set
            {
                _CompressionLevel = value;
                OnPropertyChanged();
            }
        }
        private int _CompressionLevel               = 4;

        [SaveToConfig]
        public string RootBackupSource
        {
            get { return _RootBackupSource; }
            set
            {

                _RootBackupSource = value;

                BackupSource = _CIO.ConvertVariableToPath(_RootBackupSource) + _PathBackupSource;
                OnPropertyChanged();
            }
        }
        private string _RootBackupSource            = string.Empty;

        [SaveToConfig]
        public string PathBackupSource
        {
            get { return _PathBackupSource; }
            set
            {
                _PathBackupSource = value;
                BackupSource = _CIO.ConvertVariableToPath(_RootBackupSource) + value;
                OnPropertyChanged();
            }
        }
        private string _PathBackupSource            = string.Empty;

        [SaveToConfig]
        public string RootBackupDestination
        {
            get { return _RootBackupDestination; }
            set
            {
                _RootBackupDestination = value;

                BackupDestination = _CIO.ConvertVariableToPath(_RootBackupDestination) + _PathBackupDestination;
                OnPropertyChanged();
            }
        }
        private string _RootBackupDestination       = string.Empty;

        [SaveToConfig]
        public string PathBackupDestination
        {
            get { return _PathBackupDestination; }
            set
            {
                _PathBackupDestination = value;

                BackupDestination = _CIO.ConvertVariableToPath(_RootBackupDestination) + value;
                OnPropertyChanged();
            }
        }
        private string _PathBackupDestination       = string.Empty;

        [SaveToConfig]
        public string RootRestoreSource
        {
            get { return _RootRestoreSource; }
            set
            {
                _RootRestoreSource = value;

                RestoreSource = _CIO.ConvertVariableToPath(_RootRestoreSource) + _PathRestoreSource;
                OnPropertyChanged();
            }
        }
        private string _RootRestoreSource           = string.Empty;

        [SaveToConfig]
        public string PathRestoreSource
        {
            get { return _PathRestoreSource; }
            set
            {
                _PathRestoreSource = value;

                RestoreSource = _CIO.ConvertVariableToPath(_RootRestoreSource) + value;
                OnPropertyChanged();
            }
        }
        private string _PathRestoreSource           = string.Empty;

        [SaveToConfig]
        public string RootRestoreDestination
        {
            get { return _RootRestoreDestination; }
            set
            {
                _RootRestoreDestination = value;

                RestoreDestination = _CIO.ConvertVariableToPath(_RootRestoreDestination) + _PathRestoreDestination;
                OnPropertyChanged();
            }
        }
        private string _RootRestoreDestination      = string.Empty;

        [SaveToConfig]
        public string PathRestoreDestination
        {
            get { return _PathRestoreDestination; }
            set
            {
                _PathRestoreDestination = value;
                RestoreDestination = _CIO.ConvertVariableToPath(_RootRestoreDestination) + value;
                OnPropertyChanged();
            }
        }
        private string _PathRestoreDestination      = string.Empty;

        [SaveToConfig]
        public bool InBackupMode { get; set; }      = true;

        [SaveToConfig]
        public int WindowWidth { get; set; }

        #region Temporary UserConfigValues

        public string BackupSource
        {
            get { return _BackupSource; }
            set
            {
                _BackupSource = value;
                OnPropertyChanged();
            }
        }

        private string _BackupSource;

        public string BackupDestination
        {
            get { return _BackupDestination; }
            set
            {
                _BackupDestination = value;
                OnPropertyChanged();
            }
        }

        private string _BackupDestination;

        public string RestoreSource
        {
            get { return _RestoreSource; }
            set
            {
                _RestoreSource = value;
                OnPropertyChanged();
            }
        }

        private string _RestoreSource;

        public string RestoreDestination
        {
            get { return _RestoreDestintation; }
            set
            {
                _RestoreDestintation = value;
                OnPropertyChanged();
            }
        }

        private string _RestoreDestintation;

        public string OpenFile { get; set; }

        public bool UserModifiedConfig { get; set; }

        public bool OnSaveIni { get; set; }

        public Version LoadedVersion { get; set; }

        #endregion
    }

    internal class UserConfig : UserConfigValues
    {
        public bool IsValidConfig { get; set; } = true;

        public void Save()
        {
            var data = new IniData();

            foreach (var item in GetType().GetProperties())
                for (var i = 0; i < item.GetCustomAttributes(typeof(Attribute)).Count(); i++)
                    if (item.GetValue(this) != null && item.CanRead)
                        data.Global.AddKey(item.Name, Convert.ToString(item.GetValue(this)));

            var parser = new FileIniDataParser();
            parser.WriteFile(OpenFile, data);

            UserModifiedConfig = false;
        }

        public bool Load()
        {
            var parser = new FileIniDataParser();
            IniData data = parser.ReadFile(OpenFile);

            foreach (var item in GetType().GetProperties())
                for (var i = 0; i < item.GetCustomAttributes(typeof(Attribute)).Count(); i++)
                    foreach (var line in data.Global)
                        if (Convert.ToString(line.KeyName).StartsWith(item.Name) && item.CanWrite)
                            item.SetValue(this, Convert.ChangeType(line.Value, item.PropertyType));

            // Rules
            foreach (var line in data.Global)
            {
                if (IsValidConfig)
                {
                    if (line.KeyName == "MSIniType" && line.Value != "Config")
                    {
                        IsValidConfig = false;
                    }
                    if (line.KeyName == "MSVersion")
                    {
                        Version v;
                        if (Version.TryParse(line.Value, out v))
                            IsValidConfig = IsMinimumConfigVersion(v);
                    }
                }
                else { break; }
            }

            // Show errors
            if (!IsValidConfig)
            { MessageHandler.errorMessage(MessageHandler.errCodes.Config_BadFile, OpenFile); }

            return IsValidConfig;
        }

        private bool IsMinimumConfigVersion(Version readValue)
        {
            bool result = true;

            Version minValue = new Version(1, 2, 0, 0);
            if (readValue < minValue)
            {
                MessageHandler.stdMessage(MessageHandler.msgCodes.MainWindow_LoadIncompatible, readValue.ToString());
                result = false;
            }

            return result;
        }
    }

    [Obfuscation(Exclude = true, ApplyToMembers = true)]
    internal class SettingsValues
    {
        public class SaveToSettingsAttribute : Attribute { }

        [SaveToSettings]
        public Version MSVersion
        {
            get { return Assembly.GetExecutingAssembly().GetName().Version; }
        }

        [SaveToSettings]
        public string MSIniType { get; set; } = "Settings";

        [SaveToSettings]
        public bool UseDarkMode { get; set; }

        [SaveToSettings]
        public bool RememberWindowLoc { get; set; } = true;

        [SaveToSettings]
        public int WindowYPos { get; set; }

        [SaveToSettings]
        public int WindowXPos { get; set; }

        public static readonly string ExeDirectoryLocation = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "MicronSync");

        public static readonly string IniAbsoluteLocation = Path.Combine(
            ExeDirectoryLocation,
            "Settings.ini");
    }

    internal class Settings : SettingsValues
    {
        public void Save()
        {
            if (!Directory.Exists(ExeDirectoryLocation))
                Directory.CreateDirectory(ExeDirectoryLocation);

            var data = new IniData();

            foreach (var item in GetType().GetProperties())
                for (var i = 0; i < item.GetCustomAttributes(typeof(Attribute)).Count(); i++)
                    if (item.GetValue(this) != null && item.CanRead)
                        data.Global.AddKey(item.Name, Convert.ToString(item.GetValue(this)));

            var parser = new FileIniDataParser();
            parser.WriteFile(IniAbsoluteLocation, data);
        }

        public void Load()
        {
            if (!File.Exists(IniAbsoluteLocation))
                Save();

            var parser = new FileIniDataParser();
            IniData data = parser.ReadFile(IniAbsoluteLocation);

            foreach (var item in GetType().GetProperties())
                for (var i = 0; i < item.GetCustomAttributes(typeof(Attribute)).Count(); i++)
                    foreach (var line in data.Global)
                        if (Convert.ToString(line.KeyName).StartsWith(item.Name) && item.CanWrite)
                            item.SetValue(this, Convert.ChangeType(line.Value, item.PropertyType));
        }
    }
}