// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2017 Thomas Klose & Torsten Schache, 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.IO;
using System.Xml;

using Hiperscan.ApoIdentCommon.ApoConfig.IO;
using Hiperscan.Unix;

namespace Hiperscan.ApoIdentCommon.ApoConfig
{
    public class Configurations : SecureFileWriter
    {
        public event EventHandler ConnectionSettingsChanged = null;
        public event EventHandler ProfilesChanged = null;
        public event EventHandler GuiSettingsChanged = null;
        public event EventHandler LabelPrinterSettingsChanged = null;
        public event EventHandler ProfileSettingsChanged = null;

        private const string APO_IDENT_TAG = "ApoIdentConfig";

        public Configurations(string file_name, int repeats = 5) : base(file_name, repeats)
        {
            this.OldHashCode = this.GetXmlDoc().InnerXml.GetHashCode();
        }

        protected override void Read(string file_name)
        {
            XmlDocument doc = new XmlDocument();
            using (StreamReader sr = new StreamReader(file_name, System.Text.Encoding.UTF8))
            {
                doc.Load(sr);
            }

            XmlNode root_node = doc.DocumentElement;

            try
            {
                XmlNode profiles_node = root_node.SelectNodes(ProfileManager.PROFILES_TAG)[0];
                this.Profiles = new ProfileManager(profiles_node);

            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while reading profiles: {0}", ex);
                this.Profiles = new ProfileManager();
            }

            try
            {
                XmlNode ident_module_node = root_node.SelectNodes(IdentModuleSettings.ACQUISITION_SETTINGS_TAG)[0];
                this.IdentModuleSettings = new IdentModuleSettings(ident_module_node);
            }
            catch (Exception)
            {
                this.IdentModuleSettings = new IdentModuleSettings();
            }

            try
            {
                XmlNode quantifier_module_node = root_node.SelectNodes(QuantifierModuleSettings.QUANTIFIER_ACQUISITION_SETTINGS_TAG)[0];
                this.QuantifierModuleSettings = new QuantifierModuleSettings(quantifier_module_node);
            }
            catch (Exception)
            {
                this.IdentModuleSettings = new IdentModuleSettings();
            }

            try
            {
                XmlNode device_search_node = root_node.SelectNodes(DeviceSettings.DEVICE_SETTINGS_TAG)[0];
                this.DeviceSettings = new DeviceSettings(device_search_node);
            }
            catch (Exception)
            {
                this.DeviceSettings = new DeviceSettings();
            }

            try
            {
                XmlNode printer_node = root_node.SelectNodes(LabelPrinterSettings.PRINTER_SETTINGS_TAG)[0];
                this.LabelPrinterSettings = new LabelPrinterSettings(printer_node);
            }
            catch (Exception)
            {
                this.LabelPrinterSettings = new LabelPrinterSettings();
            }

            try
            {
                XmlNode gui_node = root_node.SelectNodes(GuiSettings.GUI_SETTINGS_TAG)[0];
                this.GuiSettings = new GuiSettings(gui_node);
            }
            catch (Exception)
            {
                this.GuiSettings = new GuiSettings();
            }

            try
            {
                XmlNode software_node = root_node.SelectNodes(SoftwareUpgradeSettings.SOFTWARE_UPGRADE_SETTINGS_TAG)[0];
                this.SoftwareUpgradeSettings = new SoftwareUpgradeSettings(software_node);
            }
            catch (Exception)
            {
                this.SoftwareUpgradeSettings = new SoftwareUpgradeSettings();
            }
        }

        protected override void Write(string file_name)
        {
            XmlDocument doc = this.GetXmlDoc();

            FileIO.Save(doc, file_name);

            this.OldHashCode = this.GetXmlDoc().InnerXml.GetHashCode();
            this.ForceWriting = false;
        }

        private XmlDocument GetXmlDoc()
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(String.Format("<{0}></{0}>", Configurations.APO_IDENT_TAG));
            XmlNode root = doc.DocumentElement;

            this.Profiles.WriteToXml(doc, root);
            this.DeviceSettings.WriteToXml(doc, root);
            this.IdentModuleSettings.WriteToXml(doc, root);
            this.QuantifierModuleSettings.WriteToXml(doc, root);
            this.LabelPrinterSettings.WriteToXml(doc, root);
            this.GuiSettings.WriteToXml(doc, root);
            this.SoftwareUpgradeSettings.WriteToXml(doc, root);

            return doc;
        }

        public void StashAll()
        {
            this.Profiles.Stash();
            this.DeviceSettings.Stash();
            this.IdentModuleSettings.Stash();
            this.QuantifierModuleSettings.Stash();
            this.LabelPrinterSettings.Stash();
            this.GuiSettings.Stash();
            this.SoftwareUpgradeSettings.Stash();
        }

        public void UnstashAll()
        {
            this.Profiles.Unstash();
            this.DeviceSettings.Unstash();
            this.IdentModuleSettings.Unstash();
            this.QuantifierModuleSettings.Unstash();
            this.LabelPrinterSettings.Unstash();
            this.GuiSettings.Unstash();
            this.SoftwareUpgradeSettings.Unstash();

            this.RaiseEvents();
        }

        private void RaiseEvents()
        {
            foreach (ProfileSettings item in this.Profiles.AllProfiles.Values)
            {
                if (item.HasChanges)
                {
                    this.ProfileSettingsChanged?.Invoke(item, EventArgs.Empty);
                }
            }

            if (this.Profiles.HasChanges)
            {
                this.ProfilesChanged?.Invoke(this.Profiles, EventArgs.Empty);
            }
            else
            {
                if (this.Profiles.CurrentProfile != null && this.Profiles.CurrentProfile.HasChanges)
                    this.ProfilesChanged?.Invoke(this.Profiles, EventArgs.Empty);
            }

            if (this.LabelPrinterSettings.HasChanges)
                this.LabelPrinterSettingsChanged?.Invoke(this.LabelPrinterSettings, EventArgs.Empty);

            if (GuiSettings.HasChanges)
                this.GuiSettingsChanged?.Invoke(this.GuiSettings, EventArgs.Empty);

            if (this.IdentModuleSettings.HasChanges || 
                this.DeviceSettings.HasChanges ||
                this.QuantifierModuleSettings.HasChanges)
            {
                this.ConnectionSettingsChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        public override void Clear()
        {
            this.Profiles = new ProfileManager();
            this.IdentModuleSettings = new IdentModuleSettings();
            this.QuantifierModuleSettings = new QuantifierModuleSettings();
            this.DeviceSettings = new DeviceSettings();
            this.LabelPrinterSettings = new LabelPrinterSettings();
            this.GuiSettings = new GuiSettings();
            this.SoftwareUpgradeSettings = new SoftwareUpgradeSettings();
        }

        public bool ForceWriting
        {
            get
            {
                if (this.Profiles.ForceWriting)
                    return true;

                if (this.IdentModuleSettings.ForceWriting)
                    return true;

                if (this.QuantifierModuleSettings.ForceWriting)
                    return true;

                if (this.DeviceSettings.ForceWriting)
                    return true;

                if (this.LabelPrinterSettings.ForceWriting)
                    return true;

                if (GuiSettings.ForceWriting)
                    return true;

                if (this.SoftwareUpgradeSettings.ForceWriting)
                    return true;

                return false;
            }

            set
            {
                this.Profiles.ForceWriting = value;
                this.IdentModuleSettings.ForceWriting = value;
                this.QuantifierModuleSettings.ForceWriting = value;
                this.DeviceSettings.ForceWriting = value;
                this.LabelPrinterSettings.ForceWriting = value;
                this.GuiSettings.ForceWriting = value;
                this.SoftwareUpgradeSettings.ForceWriting = value;
            }
        }

        public override bool HasWritingData
        {
            get
            {
                return this.ForceWriting ||
                           this.OldHashCode != this.GetXmlDoc().InnerXml.GetHashCode();
            }
        }

        public ProfileManager Profiles { get; private set; }
        public IdentModuleSettings IdentModuleSettings { get; private set; }
        public QuantifierModuleSettings QuantifierModuleSettings { get; private set; }
        public DeviceSettings DeviceSettings { get; private set; }
        public LabelPrinterSettings LabelPrinterSettings { get; private set; }
        public GuiSettings GuiSettings { get; private set; }
        public SoftwareUpgradeSettings SoftwareUpgradeSettings { get; set; }

        private int OldHashCode { get; set; } = 0;
    }
}

