// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2018 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.Collections.Generic;
using System.IO;
using System.Threading;
using Hiperscan.Common.IO;
using Hiperscan.Pdf.SubstanceOverview;
using Hiperscan.Unix;
using Hiperscan.ApoIdentCore.SectionManager.Substance.SubstanceSelection;
using Hiperscan.ApoIdentCommon.ApoConfig;
using Hiperscan.ApoIdentCommon.ApoConfig.IO;
using Hiperscan.AnalysisModule;
using Hiperscan.AnalysisModule.Identification;
using System.Linq;

namespace Hiperscan.ApoIdentCore.SectionManager.Substance
{
#pragma warning disable RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist

    public partial class SubstancePdfs
    {
        public enum PdfListType
        {
            Available,
            Insert,
            Added,
        }

        public SubstancePdfs(SubstanceManager substance_mamanager,
                             ApoIdentFileSystem file_system)
        {
            this.SubstanceManager = substance_mamanager;
            this.FileSystem = file_system;
        }

        public bool CheckForPdfUpdates(AnalysisType analysis_type, bool is_es20, bool module_is_available)
        {
            RemoteSettings settings = this.SubstanceManager.GetRemoteSettings(analysis_type);

            bool reset = module_is_available == false;
            if (analysis_type == AnalysisType.Quantification && is_es20 == false)
                reset = true;

            if (reset)
            {
                settings.Reset();
                return false;
            }

            AnalysisServerInfo info = this.SubstanceManager.GetServerInfo(analysis_type);

            bool needs_update = false;
            bool needs_update_substances = false;

            DateTime install_date = new DateTime(info.InstallDate.Year,
                                         info.InstallDate.Month,
                                         info.InstallDate.Day,
                                         info.InstallDate.Hour,
                                         info.InstallDate.Minute,
                                         info.InstallDate.Second);

            if (settings.InstallDate != install_date)
                needs_update = true;

            if (analysis_type == AnalysisType.Identification)
            {
                if (settings.IsApoIdent_2_1 != is_es20)
                    needs_update_substances = true;
            }

            settings.InstallDate = install_date;
            settings.ModuleVersion = info.Version.ModuleVersion;
            settings.Locale = info.Locale;
            settings.TestVersion = (info.IsRelease == false);
            settings.IsApoIdent_2_1 = is_es20;
            settings.ServerSuffix = info.Version.ModuleSubVersion;
            settings.ReleaseExpirationTime = info.CreationTimestamp.AddMonths(6);

            settings.ManagedModels.Clear();
            settings.ValidatedModels.Clear();
            settings.AdditionalTestedModels.Clear();

            List<IdentModelClass> model_classes = this.SubstanceManager.SubstancesCatalog.AllModelClasses;
            model_classes = model_classes.Where(m => m.DatabaseInfo.AnyalysisType == analysis_type).ToList();

            foreach (IdentModelClass model_class in model_classes)
            {
                if (model_class.HasValidationDocument == true)
                    settings.ValidatedModels.Add(model_class.Name);

                if (model_class.HasAdditionalTestDocument == true)
                    settings.AdditionalTestedModels.Add(model_class.Name);

                settings.ManagedModels.Add(model_class.Name);
            }

            settings.ManagedModels.Sort();
            settings.ValidatedModels.Sort();
            settings.AdditionalTestedModels.Sort();

            if (needs_update)
            {
                settings.UpdateUpdateCertificate = true;
                settings.UpdateLicenceText = true;
                settings.UpdateValidationPdfs = true;
                settings.UpdateAdditionalTestsPdfs = true;
            }

            if (needs_update_substances)
                settings.UpdateSubstancePdfs = true;

            return true;
        }

        public void ShowSubstanceOverviewPdf(PdfListType type)
        {
            string file = String.Empty;

            switch (type)
            {
                case PdfListType.Available:
                    file = this.FileSystem.SubstanceOverviewAvailableFile;
                    break;

                case PdfListType.Insert:
                    file = this.FileSystem.SubstanceOverviewInsertFile;
                    break;

                case PdfListType.Added:
                    file = this.FileSystem.SubstanceOverviewAddedFile;
                    break;

                default:
                    break;
            }

            RemoteSettings settings = this.SubstanceManager.GetRemoteSettings(AnalysisType.Identification);


            if (settings.UpdateSubstancePdfs)
            {
                string message = String.Format(Catalog.GetString("An error occurred while getting the document from the {0}."),
                                               AnalysisType.Identification.GetProductName());

                throw new Exception(message);
            }

            if (type == PdfListType.Added)
            {
                if (false == FileIO.ExistsFile(this.FileSystem.SubstanceOverviewAddedFile))
                    throw new Exception(Catalog.GetString("For this update 'Substance and Batch Maintenance' is not available."));
            }

            if (false == FileIO.ExistsFile(file))
                throw new Exception(Catalog.GetString("Cannot find file"));

            Process.OpenUri("file://" + file.Replace("\\", "/"));
        }

        public void UpdateSubstancePdfs(AnalysisType analysis_type)
        {
            if (analysis_type != AnalysisType.Identification)
                return;

            try
            {
                this.IsUpdatingSubstancePdfs = true;
                Thread background_thread = new Thread(this.DoUpdateSubstancePdfs);
                background_thread.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("UpdateSubstancePdfs() failed: {0}", ex.ToString());
            }
            finally
            {
                this.IsUpdatingSubstancePdfs = false;
            }
        }

        private void DoUpdateSubstancePdfs()
        {
            try
            {
                string dir = this.FileSystem.SubstanceOverviewDir;
                FileIO.RecreateDirectory(dir, this.NeedUpdateSubstances());

                this.UpdateAvailableSubstances();
                this.UpdateInsertSubstances();
                this.UpdateChangedSubstances();

                this.SubstanceManager.GetRemoteSettings(AnalysisType.Identification).UpdateSubstancePdfs = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoUpdateAllSubstanceDocument() failed: {0}", ex.ToString());
            }
            finally
            {
                this.IsUpdatingSubstancePdfs = false;
            }
        }

        private void UpdateAvailableSubstances()
        {
            if (this.NeedUpdateSubstances(this.FileSystem.SubstanceOverviewAvailableFile) == false)
                return;

            FileIO.DeleteFile(this.FileSystem.SubstanceOverviewAvailableFile, true);

            PdfModelStructure list = this.GetSubstanceOverviewPdfData(false, true);
            list.ListOfContentsHeader = Catalog.GetString("List of Contents - Subscribed Substances");
            list.Version = this.SubstanceManager.GetServerInfo(AnalysisType.Identification)?.Version.ModuleVersion;
            list.ShowClassificationColumn = true;
            OverviewPdfCreator.Create(list, this.FileSystem.SubstanceOverviewAvailableFile, OverviewErrorType.NoAvailablePdf);
        }

        private void UpdateInsertSubstances()
        {
            if (this.NeedUpdateSubstances(this.FileSystem.SubstanceOverviewInsertFile) == false)
                return;

            FileIO.DeleteFile(this.FileSystem.SubstanceOverviewInsertFile, true);

            PdfModelStructure list = this.GetSubstanceOverviewPdfData(true, true);
            if (list != null && list.IsEmpty == false)
            {
                list.ListOfContentsHeader = Catalog.GetString("List of Contents - Subscribed Substances with Sample Insert");
                list.Version = this.SubstanceManager.GetServerInfo(AnalysisType.Identification)?.Version.ModuleVersion;
                list.ShowClassificationColumn = false;
                OverviewPdfCreator.Create(list, this.FileSystem.SubstanceOverviewInsertFile, OverviewErrorType.NoAvailablePdf);
            }
        }

        private void UpdateChangedSubstances()
        {
            if (this.NeedUpdateSubstances(this.FileSystem.SubstanceOverviewAddedFile) == false)
                return;

            FileIO.DeleteFile(this.FileSystem.SubstanceOverviewAddedFile, true);

            byte[] stream = this.SubstanceManager.GetUpdateChanges();
            if (stream != null)
                stream.ToFile(this.FileSystem.SubstanceOverviewAddedFile);
        }

        private PdfModelStructure GetSubstanceOverviewPdfData(bool only_insert, bool list_all_default_names)
        {
            PdfModelStructure list = new PdfModelStructure();

            foreach (IdentModelClass model_class in this.SubstancesCatalog.MasterModelClasses)
            {
                foreach (IdentClassification classification in model_class.Classifications)
                {
                    foreach (IdentSubstance substance_info in classification.Substances)
                    {
                        if (substance_info.IsVisible == false)
                            continue;

                        if (only_insert == true && substance_info.CanInsert == false)
                            continue;

                        PdfModelClassEntry model_class_entry = list.ModelClasses.Find(x => x.ModelClassName == model_class.Name);

                        if (model_class_entry == null)
                        {
                            model_class_entry = new PdfModelClassEntry();
                            model_class_entry.ModelClassName = model_class.Name;
                            list.ModelClasses.Add(model_class_entry);
                        }

                        List<string> default_names = new List<string>();
                        default_names.Add(substance_info.PrimaryDefaultName);
                        default_names.AddRange(substance_info.OtherDefaultNames);

                        List<string> latin_names = new List<string>();
                        if (String.IsNullOrEmpty(substance_info.PrimaryLatinName) == false)
                        {
                            latin_names.Add(substance_info.PrimaryLatinName);
                            latin_names.AddRange(substance_info.OtherLatinNames);
                        }

                        if (list_all_default_names == true)
                        {
                            for (int i = 0; i < default_names.Count; i++)
                            {
                                PdfSubstanceEntry entry = new PdfSubstanceEntry();
                                entry.SubstanceId = substance_info.Id;
                                entry.DefaultNames.Add(default_names[i]);
                                entry.LatinNames = latin_names;
                                entry.ClassificationName = classification.Name;
                                entry.NumberOfClassificationMembers = classification.Substances.Count;
                                model_class_entry.Substances.Add(entry);
                            }
                        }
                        else
                        {
                            PdfSubstanceEntry entry = new PdfSubstanceEntry();
                            entry.SubstanceId = substance_info.Id;
                            entry.DefaultNames = default_names;
                            entry.LatinNames = latin_names;
                            entry.ClassificationName = classification.Name;
                            entry.NumberOfClassificationMembers = classification.Substances.Count;
                            model_class_entry.Substances.Add(entry);
                        }
                    }
                }
            }

            list.Sort();
            return list;
        }

        private bool NeedUpdateSubstances(string file = null)
        {
            if (String.IsNullOrEmpty(file) == false)
            {
                if (false == FileIO.ExistsFile(file))
                    return true;
            }

            return this.SubstanceManager.GetRemoteSettings(AnalysisType.Identification).UpdateSubstancePdfs;
        }

        public void Clean()
        {
            try
            {
                if (FileIO.ExistsFile(this.FileSystem.OldLicenseFile))
                    FileIO.DeleteFile(this.FileSystem.OldLicenseFile);
            }
            catch (Exception ex) 
            {
                Console.WriteLine("Could not delete old license file; {0}", ex.Message);
            }
        }

        private SubstancesCatalog SubstancesCatalog
        {
            get
            {
                return this.SubstanceManager.SubstancesCatalog;
            }
        }

        public bool IsUpdatingSubstancePdfs {get; private set;} = false;
        private SubstanceManager SubstanceManager { get; set; }
        private ApoIdentFileSystem FileSystem { get; set; }
    }

    #pragma warning restore RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist

}
