using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Nocksoft.IO.ConfigFiles;




public static class ConfigUtils
{
    private static INIFile config;

    public static void InitConfig(string path = "config.ini")
    {
        config = new INIFile(path, true);
    }
    public static void SetValue<T>(this Enum _type, T _value)
    {
        if (!checkIfInitialized())
            return;
        config.SetValue(getStrType(_type), getStrName(_type), _value.ToString());
    }
    public static T GetValue<T>(this Enum _type)
    {
        if (!checkIfInitialized())
            return default(T);
        try
        {
            var value = config.GetValue(getStrType(_type), getStrName(_type));
            return (T)Convert.ChangeType(value, typeof(T));
        }
        catch (Exception ex)
        {
            Console.WriteLine("ConfigManager Exception on " + getStrType(_type) + "." + getStrName(_type) + ": " + ex.Message);
            return default(T);
        }
    }
    private static string getStrType(Enum _enum)
    {
        return _enum.GetType().Name;
    }
        
    private static string getStrName(Enum _enum)
    {
        return Enum.GetName(_enum.GetType(), _enum);
    }


    private static bool checkIfInitialized()
    {
        if (config == null)
        {
            Console.WriteLine("Config not initialized");
            return false;
        }
        else
        {
            return true;
        }
    }
}



/**
 * Copyright by Nocksoft
 * https://www.nocksoft.de
 * https://nocksoft.de/tutorials/visual-c-sharp-arbeiten-mit-ini-dateien/
 * https://github.com/Nocksoft/INIFile.cs
 * -----------------------------------
 * Author:  Rafael Nockmann @ Nocksoft
 * Updated: 2022-01-09
 * Version: 1.0.3
 *
 * Language: Visual C#
 *
 * License: MIT license
 * License URL: https://github.com/Nocksoft/INIFile.cs/blob/master/LICENSE
 *
 * Description:
 * Provides basic functions for working with INI files.
 *
 * ##############################################################################################
 */


namespace Nocksoft.IO.ConfigFiles
{
    public class INIFile
    {
        private string _File;

        /// <summary>
        /// Call the constructor creates a new object of the INIFile class to work with INI files.
        /// </summary>
        /// <param name="file">Name of INI file, which you want to access.</param>
        /// <param name="createFile">Specifies whether the INI file should be created if it does not exist.</param>
        public INIFile(string file, bool createFile = false)
        {
            if (createFile == true && File.Exists(file) == false)
            {
                FileInfo fileInfo = new FileInfo(file);
                FileStream fileStream = fileInfo.Create();
                fileStream.Close();
            }
            _File = file;
        }

        #region Public Methods

        /// <summary>
        /// Removes all comments and empty lines from a complete section and returns the sections.
        /// This method is not case-sensitive.
        /// The return value does not contain any spaces at the beginning or at the end of a line.
        /// </summary>
        /// <param name="section">Name of the requested section.</param>
        /// <param name="includeComments">Specifies whether comments should also be returned.</param>
        /// <returns>Returns the whole section.</returns>
        public List<string> GetSection(string section, bool includeComments = false)
        {
            section = CheckSection(section);

            List<string> completeSection = new List<string>();
            bool sectionStart = false;

            string[] fileArray = File.ReadAllLines(_File);

            foreach (var item in fileArray)
            {
                if (item.Length <= 0) continue;

                // Beginning of section.
                if (item.Replace(" ", "").ToLower() == section)
                {
                    sectionStart = true;
                }
                // Beginning of next section.
                if (sectionStart == true && item.Replace(" ", "").ToLower() != section && item.Replace(" ", "").Substring(0, 1) == "[" && item.Replace(" ", "").Substring(item.Length - 1, 1) == "]")
                {
                    break;
                }
                if (sectionStart == true)
                {
                    // Add the entry to the List<string> completeSection, if it is not a comment or an empty entry.
                    if (includeComments == false
                        && item.Replace(" ", "").Substring(0, 1) != ";" && !string.IsNullOrWhiteSpace(item))
                    {
                        completeSection.Add(ReplaceSpacesAtStartAndEnd(item));
                    }
                    if (includeComments == true && !string.IsNullOrWhiteSpace(item))
                    {
                        completeSection.Add(ReplaceSpacesAtStartAndEnd(item));
                    }
                }
            }
            return completeSection;
        }

        /// <summary>
        /// The method returns a value for the associated key.
        /// This method is not case-sensitive.
        /// </summary>
        /// <param name="section">Name of the requested section.</param>
        /// <param name="key">Name of the requested key.</param>
        /// <param name="convertValueToLower">If "true" is passed, the value will be returned in lowercase letters.</param>
        /// <returns>Returns the value for the specified key in the specified section, if available, otherwise null.</returns>
        public string GetValue(string section, string key, bool convertValueToLower = false)
        {
            section = CheckSection(section);
            key = key.ToLower();

            List<string> completeSection = GetSection(section);

            foreach (var item in completeSection)
            {
                // Continue if entry is no key.
                if (!item.Contains("=") && item.Contains("[") && item.Contains("]")) continue;

                string[] keyAndValue = item.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
                if (keyAndValue[0].ToLower() == key && keyAndValue.Count() > 1)
                {
                    if (convertValueToLower == true)
                    {
                        keyAndValue[1] = keyAndValue[1].ToLower();
                    }
                    return keyAndValue[1];
                }
            }
            return null;
        }

        /// <summary>
        /// Set or add a value of the associated key in the specified section.
        /// This method is not case-sensitive.
        /// </summary>
        /// <param name="section">Name of the section.</param>
        /// <param name="key">Name of the key.</param>
        /// <param name="value">Value to save.</param>
        /// <param name="convertValueToLower">If "true" is passed, the value will be saved in lowercase letters.</param>
        public void SetValue(string section, string key, string value, bool convertValueToLower = false)
        {
            section = CheckSection(section, false);
            string sectionToLower = section.ToLower();

            bool sectionFound = false;

            List<string> iniFileContent = new List<string>();

            string[] fileLines = File.ReadAllLines(_File);

            // Creates a new INI file if none exists.
            if (fileLines.Length <= 0)
            {
                iniFileContent = AddSection(iniFileContent, section, key, value, convertValueToLower);
                WriteFile(iniFileContent);
                return;
            }

            for (int i = 0; i < fileLines.Length; i++)
            {
                // Possibility 1: The desired section has not (yet) been found.
                if (fileLines[i].Replace(" ", "").ToLower() != sectionToLower)
                {
                    iniFileContent.Add(fileLines[i]);
                    // If a section does not exist, the section will be created.
                    if (i == fileLines.Length - 1 && fileLines[i].Replace(" ", "").ToLower() != sectionToLower && sectionFound == false)
                    {
                        iniFileContent.Add(null);
                        iniFileContent = AddSection(iniFileContent, section, key, value, convertValueToLower);
                        break;
                    }
                    continue;
                }


                // Possibility 2 -> Desired section was found.
                sectionFound = true;

                // Get the complete section in which the target key may be.
                List<string> targetSection = GetSection(sectionToLower, true);

                for (int x = 0; x < targetSection.Count; x++)
                {
                    string[] targetKey = targetSection[x].Split(new string[] { "=" }, StringSplitOptions.None);
                    // When the target key is found.
                    if (targetKey[0].ToLower() == key.ToLower())
                    {
                        if (convertValueToLower == true)
                        {
                            iniFileContent.Add(key + "=" + value.ToLower());
                        }
                        else
                        {
                            iniFileContent.Add(key + "=" + value);
                        }
                        i = i + x;
                        break;
                    }
                    else
                    {
                        iniFileContent.Add(targetSection[x]);
                        // If the target key is not found, it will be created.
                        if (x == targetSection.Count - 1 && targetKey[0].ToLower() != key.ToLower())
                        {
                            if (convertValueToLower == true)
                            {
                                iniFileContent.Add(key + "=" + value.ToLower());
                            }
                            else
                            {
                                iniFileContent.Add(key + "=" + value);
                            }
                            i = i + x;
                            break;
                        }
                    }
                }
            }

            WriteFile(iniFileContent);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Ensures that a section is always in the following format: [section].
        /// </summary>
        /// <param name="section">Section to be checked for correct format.</param>
        /// <param name="convertToLower">Specifies whether the section should be vonverted in lower case letters.</param>
        /// <returns>Returns section in this form: [section].</returns>
        private string CheckSection(string section, bool convertToLower = true)
        {
            if (convertToLower == true)
            {
                section = section.ToLower();
            }
            if (!section.StartsWith("[") && !section.EndsWith("]"))
            {
                section = "[" + section + "]";
            }
            return section;
        }

        /// <summary>
        /// Removes leading and trailing spaces from sections, keys and values.
        /// </summary>
        /// <param name="item">String to be trimmed.</param>
        /// <returns>Returns the trimmed string.</returns>
        private string ReplaceSpacesAtStartAndEnd(string item)
        {
            // If the string has a key and a value.
            if (item.Contains("=") && !item.Contains("[") && !item.Contains("]"))
            {
                string[] keyAndValue = item.Split(new string[] { "=" }, StringSplitOptions.None);
                return keyAndValue[0].Trim() + "=" + keyAndValue[1].Trim();
            }

            return item.Trim();
        }

        /// <summary>
        /// Adds a new section with key value pair.
        /// </summary>
        /// <param name="iniFileContent">List iniFileContent from SetValue.</param>
        /// <param name="section">Section to be created.</param>
        /// <param name="key">Key to be added.</param>
        /// <param name="value">Value to be added.</param>
        /// <param name="convertValueToLower">Specifies whether the key and value should be saved in lower case letters.</param>
        /// <returns>Returns the new created section with key value pair.</returns>
        private List<string> AddSection(List<string> iniFileContent, string section, string key, string value, bool convertValueToLower)
        {
            if (convertValueToLower == true)
            {
                value = value.ToLower();
            }

            iniFileContent.Add(section);
            iniFileContent.Add($"{key}={value}");
            return iniFileContent;
        }

        private void WriteFile(List<string> content)
        {
            StreamWriter writer = new StreamWriter(_File);
            foreach (var item in content)
            {
                writer.WriteLine(item);
            }
            writer.Close();
        }

        #endregion
    }
}