// UI.Configuration.cs created with MonoDevelop
// User: klose at 19:06 08.01.2009
// CVS release: $Id: UI.Configuration.cs,v 1.47 2011-04-19 12:36:58 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  Thomas Klose, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Gtk;

using Hiperscan.Common.IO;
using Hiperscan.Extensions;
using Hiperscan.QuickStep.Dialog;
using Hiperscan.QuickStep.Figure;
using Hiperscan.QuickStep.Integration;
using Hiperscan.QuickStep.IO;
using Hiperscan.SGS;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.QuickStep
{

    public partial class UserInterface
    {
        public const string USER_CONFIG_FILE_NAME = FileIOHelper.QuickStepConfigPath.DEFAULT_CONFIG_FILE_NAME;
        public const int MAX_X_CONSTRAINTS_COUNT = 5;


        internal void SaveSettings()
        {
            this.MainWindow.GetSize(out int width, out int height);
            Settings.Current.MainWindowWidth = width;
            Settings.Current.MainWindowHeight = height;

            if (this.MainWindow.GdkWindow != null)
                Settings.Current.MainWindowMaximized = (this.MainWindow.GdkWindow.State == Gdk.WindowState.Maximized);

            Settings.Current.SpectrometerPanedPosition = this.spectrometer_paned.Position;
            Settings.Current.DataSourcePanedPosition = this.data_source_paned.Position;

            var reopen_paths = new List<string>();
            foreach (Spectrum spectrum in this.CurrentSpectra.Values)
            {
                if (spectrum.FileInfo != null)
                    reopen_paths.Add(spectrum.FileInfo.FullName);
            }
            Settings.Current.ReopenPaths = reopen_paths;

            Console.WriteLine("Saving settings...");
            Settings.Save();

            AccelMap.Save(Path.Combine(UserInterface.ConfigDirPath, "accelerators"));
        }

        internal void LoadSettings()
        {
            AccelMap.Load(Path.Combine(UserInterface.ConfigDirPath, "accelerators"));
        }

        internal static class Settings
        {
            private static Configuration config;
            private static bool is_valid = true;
            private static bool is_initialized = false;
            public static volatile bool saving = false;

            private const string DEFAULT_SECTION_NAME = "QuickStep2.0Settings";

            public static void Init()
            {
                // sync config from Hiperscan's WebDAV server
                try
                {
                    if (MainClass.IsRelease == false)
                        WebDavConfig.Read();
                }
#pragma warning disable RECS0022
                catch { }
#pragma warning restore RECS0022

                Settings.Init(Settings.DEFAULT_SECTION_NAME, new GuiSettings());
                Settings.is_initialized = true;
            }

            private static void MoveConfigDataToRoamingFolder()
            {
                try
                {
                    bool copy_files = true;

                    if (FileIOHelper.QuickStepConfigPath.FolderChanged(UserInterface.ConfigurationProfileDirNameExtension) == false)
                        copy_files = false;

                    string destination_dir = FileIOHelper.QuickStepConfigPath.GetDirPath(UserInterface.ConfigurationProfileDirNameExtension);
                    if (File.Exists(Path.Combine(destination_dir, FileIOHelper.QuickStepConfigPath.DEFAULT_CONFIG_FILE_NAME)) == true)
                        copy_files = false;

                    string source_dir = FileIOHelper.QuickStepConfigPath.OldFolder(UserInterface.ConfigurationProfileDirNameExtension);
                    if (File.Exists(Path.Combine(source_dir, FileIOHelper.QuickStepConfigPath.DEFAULT_CONFIG_FILE_NAME)) == false)
                        copy_files = false;

                    if (File.Exists(Path.Combine(source_dir, FileIOHelper.QuickStepConfigPath.CONVERSION_COOKIE)) == true)
                        copy_files = false;

                    if (copy_files)
                        Settings.CopyConfigData(source_dir, destination_dir);

                    if (Directory.Exists(source_dir) == true &&
                     File.Exists(Path.Combine(source_dir, FileIOHelper.QuickStepConfigPath.CONVERSION_COOKIE)) == false)
                        File.Create(Path.Combine(source_dir, FileIOHelper.QuickStepConfigPath.CONVERSION_COOKIE));
                }
                catch (Exception ex)
                {
                    Console.WriteLine("MoveConfigDataToRoamingFolder failed {0}", ex);
                }
            }

            private static void CopyConfigData(string source_folder, string destination_folder)
            {
                Regex regex = new Regex(@".*log.*_\d\.txt$");

                foreach (string source_file in Directory.GetFiles(source_folder))
                {
                    if (regex.Match(Path.GetFileName(source_file)).Success == true)
                        continue;

                    string destination_file = Path.Combine(destination_folder, Path.GetFileName(source_file));
                    File.Copy(source_file, destination_file, true);
                }

                foreach (string sub_dir in Directory.GetDirectories(source_folder))
                {
                    if (Path.GetFileName(sub_dir) == "de_DE" || Path.GetFileName(sub_dir) == "en_GB")
                        continue;

                    string sub_destination_dir = Path.Combine(destination_folder, Path.GetFileName(sub_dir));

                    if (Directory.Exists(sub_destination_dir) == false)
                        Directory.CreateDirectory(sub_destination_dir);

                    Settings.CopyConfigData(sub_dir, sub_destination_dir);
                }
            }

            private static void CreateEmptyConfig(string file)
            {
                using (StreamWriter sw = new StreamWriter(file, false, Encoding.UTF8))
                {
                    sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                    sw.WriteLine("<configuration />");
                }
            }

            public static void Init(string section_name, ConfigurationSection new_section)
            {
                if (Settings.config == null)
                {
                    string file = Path.Combine(UserInterface.ConfigDirPath, UserInterface.USER_CONFIG_FILE_NAME);

                    Settings.MoveConfigDataToRoamingFolder();
                    Settings.MigrateConfigData();

                    int max_tries = 5;
                    for (int i = 1; i <= max_tries; i++)
                    {
                        try
                        {
                            if (File.Exists(file) == false)
                                Settings.CreateEmptyConfig(file);
                            File.Copy(file, $"{file}.config", true);
                            Settings.config = ConfigurationManager.OpenExeConfiguration(file);
                            break;
                        }
                        catch (Exception ex)
                        {
                            if (i == max_tries)
                                throw ex;

                            System.Threading.Thread.Sleep(150);
                        }
                    }
                }

                // If the section does not exist in the configuration
                if (Settings.config.Sections[section_name] == null)
                {
                    Settings.config.Sections.Add(section_name, new_section);
                    new_section = Settings.config.GetSection(section_name);
                }
            }

            private static void MigrateConfigData()
            {
                string config_dir = FileIOHelper.QuickStepConfigPath.GetDirPath(UserInterface.ConfigurationProfileDirNameExtension);
                                                
                foreach (var config_file in Directory.GetFiles(config_dir, "*.config"))
                {
                    string contents = File.ReadAllText(config_file);
                    contents = Regex.Replace(contents,
                                             @"name=""QuickStep1.0Settings"" type=""[^,]*",
                                             @"name=""QuickStep2.0Settings"" type=""Hiperscan.QuickStep.UserInterface+GuiSettings");
                    contents = Regex.Replace(contents,
                                             @"QuickStep1.0Settings SectionExists=""True""",
                                             @"QuickStep2.0Settings SectionExists=""True""");
                    contents = Regex.Replace(contents,
                                             @"name=""c3721fc0-8f56-11df-a4ee-0800200c9a66"" type=""[^,]*",
                                             @"name=""c3721fc0-8f56-11df-a4ee-0800200c9a66"" type=""Hiperscan.Extensions.DeviceCalibration.PluginSettings");

                    File.WriteAllText(config_file, contents);
                }
            }

            public static void RequestConfigOverride(Exception ex)
            {
                Settings.is_valid = false;
                string file = Path.Combine(UserInterface.ConfigDirPath, UserInterface.USER_CONFIG_FILE_NAME);

                Console.WriteLine("Cannot read configuration:");
                Console.WriteLine(ex);
                OverwriteConfigDialog dialog = new OverwriteConfigDialog(null, file);
                ResponseType response = (ResponseType)dialog.Run();
                dialog.Destroy();

                if (response == ResponseType.No)
                    throw new EmergencyStopException();

                try
                {
                    File.Delete(file);
                }
                catch (Exception _ex)
                {
                    InfoMessageDialog.Show(null,
                                           Catalog.GetString("Cannot overwrite configuration file:") +
                                           " " +
                                           _ex.Message);
                    throw new EmergencyStopException();
                }
                InfoMessageDialog.Show(null, Catalog.GetString("Please restart the application."));
                throw new EmergencyStopException();
            }

            public static void Save()
            {
                saving = true;

                if (Settings.IsValid == false)
                {
                    saving = false;
                    return;
                }

                Settings.Current.SectionExists = true;
                string file = Path.Combine(UserInterface.ConfigDirPath, UserInterface.USER_CONFIG_FILE_NAME);

#pragma warning disable RECS0022
                try
                {
                    Settings.__WriteConfigFile();

                    try
                    {
                        if (MainClass.IsRelease == false)
                            WebDavConfig.Write();
                    }
                    catch { }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error while saving settings: " + ex.ToString());
                    Console.WriteLine("Trying again in 1 second...");
                    System.Threading.Thread.Sleep(1000);
                    try
                    {
                        Settings.__WriteConfigFile();

                        try
                        {
                            if (MainClass.IsRelease == false)
                                WebDavConfig.Write();
                        }
                        catch { }
                    }
                    catch (Exception exx)
                    {
                        Console.WriteLine("Failed again: " + exx.Message);
                        Console.WriteLine("Giving up.");
                        UserInterface.Instance.ShowInfoMessage(null,
                                                               Catalog.GetString("An error occured while writing configuration file: {0}\n\nSetting will not be saved."),
                                                               exx.Message);
                    }
#pragma warning restore RECS0022
                }
                saving = false;
            }

            private static void __WriteConfigFile()
            {
                string file = Path.Combine(UserInterface.ConfigDirPath, UserInterface.USER_CONFIG_FILE_NAME);

                Regex regex = new Regex(@"([a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12})|(" + DEFAULT_SECTION_NAME +")");

                XmlDocument document = new XmlDocument();
                document.LoadXml("<configuration><configSections></configSections></configuration>");
                XmlNode config_section = document.GetElementsByTagName("configSections")[0];
                XmlNode configuration = document.GetElementsByTagName("configuration")[0];

                foreach (string section_name in Settings.config.Sections.Keys)
                {
                    try
                    {
                        if (regex.Match(section_name).Success == true)
                        {
                            ConfigurationSection section = Settings.config.GetSection(section_name);

                            XmlAttribute name = document.CreateAttribute("name");
                            name.InnerText = section_name;
                            XmlAttribute type = document.CreateAttribute("type");
                            type.InnerText = section.GetType().AssemblyQualifiedName;
                            XmlAttribute require_permission = document.CreateAttribute("requirePermission");
                            require_permission.InnerText = "false";

                            XmlElement element = document.CreateElement("section");
                            element.Attributes.Append(name);
                            element.Attributes.Append(type);
                            element.Attributes.Append(require_permission);

                            config_section.AppendChild(element);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                    }
                }

                foreach (string section_name in Settings.config.Sections.Keys)
                {
#pragma warning disable RECS0022
                    try
                    {
                        if (regex.Match(section_name).Success)
                        {
                            try
                            {
                                ConfigurationSection section = Settings.config.GetSection(section_name);

                                XmlElement element = document.CreateElement(section_name);
                                configuration.AppendChild(element);

                                UserInterface.Settings.__WriteConfigProperties(element, document, section);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine("Configuration error: " + ex);
                            }
                        }
                    }
                    catch { }
#pragma warning restore RECS0022
                }

                using (StreamWriter sw = new StreamWriter(file, false, Encoding.UTF8))
                {
                    document.Save(sw);
                }
            }

            private static void __WriteConfigProperties(XmlElement element, XmlDocument doc, object section)
            {
                foreach (PropertyInfo property in section.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    if (property.GetCustomAttributes(typeof(ConfigurationPropertyAttribute), false).Length == 0)
                        continue;

                    ConfigurationPropertyAttribute config_attribute =
                        (ConfigurationPropertyAttribute)property.GetCustomAttributes(typeof(ConfigurationPropertyAttribute), false)[0];

                    if ((property.GetValue(section, null) as ConfigurationElement) != null)
                    {
                        XmlElement sub_element = doc.CreateElement(property.Name);
                        element.AppendChild(sub_element);
                        __WriteConfigProperties(sub_element, doc, property.GetValue(section, null));
                    }
                    else
                    {
                        XmlAttribute attribute = doc.CreateAttribute(config_attribute.Name);
                        TypeConverter converter = TypeDescriptor.GetConverter(property.GetValue(section, null));

                        attribute.InnerText = converter.ConvertToInvariantString(property.GetValue(section, null));
                        element.Attributes.Append(attribute);
                    }
                }
            }

            public static ConfigurationSection Section(string section_name)
            {
                return Settings.config.GetSection(section_name);
            }

            public static GuiSettings Current => Settings.config.GetSection(Settings.DEFAULT_SECTION_NAME) as GuiSettings;
            public static bool IsValid => Settings.is_valid;
            public static bool IsInitialized => Settings.is_initialized;
        }

        public void InitConfigurationSection(string section_name, ConfigurationSection section)
        {
            try
            {
                Settings.Init(section_name, section);
            }
            catch (Exception ex)
            {
                Settings.RequestConfigOverride(ex);
            }
        }

        public ConfigurationSection GetConfigurationSection(string section_name)
        {
            return Settings.Section(section_name);
        }

        public void SaveConfiguration()
        {
            Settings.Save();
        }

        static private volatile bool __setting_plot_type = false;

        public PlotTypeInfo PlotType
        {
            get { return this.plot_info_list.Current.PlotType; }
            set
            {
                if (__setting_plot_type)
                    return;

                __setting_plot_type = true;
                bool call_again = false;

                try
                {
                    this.plot_info_list.Current.PlotType = value;
                    Console.WriteLine("Set plot type to: " + value.Name);
                    this.ScrolledPlot.Plot.XLabel = value.XLabel;
                    this.ScrolledPlot.Plot.YLabel = value.YLabel;
                    this.ScrolledPlot.Plot.PlotTypeId = value.GetPlotTypeId();
                    this.UpdateXConstraintsEntry();
                    call_again = !value.QueryInformation();

                    if (value is UndefinedPlotTypeInfo)
                    {
                        if (this.plot_type_combo.Active != -1)
                            this.plot_type_combo.Active = -1;
                    }
                    else
                    {
                        this.__SetPlotTypeCombo(value.Name, true);
                    }
                }
#pragma warning disable RECS0022
                catch { }
#pragma warning restore RECS0022
                finally
                {
                    __setting_plot_type = false;

                    if (call_again)
                        this.PlotType = this.plot_info_list.Current.PlotType;
                }
            }
        }

        public void __SetPlotTypeCombo(string name, bool update_plot)
        {
            bool sav_state = __update_on_plot_type_combo_change;
            __update_on_plot_type_combo_change = update_plot;

            this.plot_type_combo.Model.Foreach(delegate (TreeModel model, TreePath path, TreeIter iter)
            {
                if (((PlotTypeInfo)model.GetValue(iter, 1)).Name != name)
                    return false;
                this.plot_type_combo.SetActiveIter(iter);
                return true;
            });

            __update_on_plot_type_combo_change = sav_state;
        }

        public OutputCulture StandardOutputCulture
        {
            get
            {
                return Settings.Current.StandardOutputCulture;
            }
            set
            {
                switch (value)
                {

                case OutputCulture.English:
                    this.english_item.Active = true;
                    break;

                case OutputCulture.German:
                    this.german_item.Active = true;
                    break;

                case OutputCulture.System:
                    this.system_item.Active = true;
                    break;

                }

                if (this.DeviceList == null)
                    return;

                foreach (IDevice dev in this.DeviceList.Values)
                {
                    AutoSaveConfig config = (AutoSaveConfig)dev.CurrentConfig.UserData;

                    if (config == null)
                        continue;

                    config.SingleDir.OutputCulture = value;
                    config.StreamDir.OutputCulture = value;
                }

                Settings.Current.StandardOutputCulture = value;
            }
        }

        public CultureInfo OutputCultureInfo()
        {
            return this.OutputCultureInfo(this.StandardOutputCulture);
        }

        public CultureInfo OutputCultureInfo(OutputCulture output_culture)
        {
            switch (output_culture)
            {

            case OutputCulture.English:
                return new CultureInfo("en-US");

            case OutputCulture.German:
                return new CultureInfo("de-DE");

            case OutputCulture.System:
                return CultureInfo.CurrentCulture;

            default:
                return CultureInfo.CurrentCulture;
            }
        }

        public bool StandardToolbarVisible
        {
            get { return this.standard_toolbar_item.Active; }
            set
            {
                if (this.standard_toolbar_item.Active != value)
                    this.standard_toolbar_item.Active = value;
                else
                    this.standard_toolbar_item.EmitToggled();
            }
        }

        public bool FigureToolbarVisible
        {
            get { return this.figure_toolbar_item.Active; }
            set
            {
                if (this.figure_toolbar_item.Active != value)
                    this.figure_toolbar_item.Active = value;
                else
                    this.figure_toolbar_item.EmitToggled();
            }
        }

        public bool ExtensionsToolbarVisible
        {
            get { return this.extensions_toolbar_item.Active; }
            set
            {
                if (this.extensions_toolbar_item.Active != value)
                    this.extensions_toolbar_item.Active = value;
                else
                    this.extensions_toolbar_item.EmitToggled();
            }
        }

        public bool DataSourcesVisible
        {
            get { return this.data_sources_button.Active; }
            set
            {
                if (this.data_sources_button.Active != value)
                    this.data_sources_button.Active = value;
                else
                    this.OnDataSourcesButtonToggled(this.data_sources_button, null);
            }
        }

        public bool PropertyEditorVisible
        {
            get { return this.property_editor_button.Active; }
            set
            {
                if (this.property_editor_button.Active != value)
                    this.property_editor_button.Active = value;
                else
                    this.OnPropertyEditorButtonToggled(this.property_editor_button, null);
            }
        }

        public bool PlotBrowserVisible
        {
            get { return this.plot_browser_button.Active; }
            set
            {
                if (this.plot_browser_button.Active != value)
                    this.plot_browser_button.Active = value;
                else
                    this.OnPlotBrowserButtonToggled(this.plot_browser_button, null);
            }
        }

        public Config.PropertyType PropertyType
        {
            get { return (Config.PropertyType)this.property_notebook.CurrentPage; }
            set { this.property_notebook.CurrentPage = (int)value; }
        }

        public bool ExhibitionMode
        {
            get { return MainClass.ApplicationMode == UserInterface.Config.ApplicationMode.Exhibition; }
        }

        public bool TouchScreenMode
        {
            get { return MainClass.TouchScreen; }
        }

        public bool IsRelease
        {
            get { return MainClass.IsRelease; }
        }

        public LicenseTyoe CurrentLicense
        {
            get { return MainClass.CurrentLicense; }
        }

        public static string ConfigDirPath => FileIOHelper.QuickStepConfigPath.GetDirPath(UserInterface.ConfigurationProfileDirNameExtension);

        public static string ConfigurationProfileDirNameExtension { get; set; } = FileIOHelper.QuickStepConfigPath.DEFAULT_PROFILE_NAME;
    }
}