// 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.Text;
using System.Threading;

using Hiperscan.Common.I18n;
using Hiperscan.Common.IO;
using Hiperscan.Pdf.SubstanceOverview;
using Hiperscan.Spectroscopy.IdentModule;
using Hiperscan.Spectroscopy.IdentServer;
using Hiperscan.Unix;


namespace Hiperscan.Extensions.ApoIdent
{
    public class SubstancePdfs
    {
        public enum PdfListType
        {
            Available,
            Insert,
            Added,
        }

        public event EventHandler LoadingValidationDocuStarted = null;
        public event EventHandler LoadingValidationDocuFinished = null;
        public event EventHandler LoadingSubstancuDocuStarted = null;
        public event EventHandler LoadingSubstanceDocuFinished = null;
        public event EventHandler LoadingAdditionalTestsStarted = null;
        public event EventHandler LoadingAdditionalTestsFinished = null;

        public SubstancePdfs(SubstanceManagement substance_db)
        {
            this.SubstanceManagement = substance_db;
        }

        public void CheckForPdfUpdates()
        {
            bool needs_update = false;
            bool needs_update_additional_tests = false;
            bool needs_update_managed_classifieres = false;

            if (this.SubstanceManagement.Config.IdentModuleSettings.IdentServerVersion != IdentServerClient.Info.Version.IdentModuleVersion)
                needs_update = true;

            if (this.SubstanceManagement.Config.IdentModuleSettings.IdentServerSuffix != IdentServerClient.Info.Version.IdentModuleSubVersion)
                needs_update_additional_tests = true;

            if (this.SubstanceManagement.Config.IdentModuleSettings.Locale != IdentServerClient.Info.Locale)
                needs_update = true;

            if (this.SubstanceManagement.Config.IdentModuleSettings.TestVersion != (IdentServerClient.Info.IsRelease == false))
                needs_update = true;

            string old_maanged_classifieres = String.Join(String.Empty, this.SubstanceManagement.Config.IdentModuleSettings.ManagedClassifiers.ToArray());

            this.SubstanceManagement.Config.IdentModuleSettings.ValidatedClassifiers.Clear();
            this.SubstanceManagement.Config.IdentModuleSettings.ManagedClassifiers.Clear();
            foreach (ModelClassInfo classifier_info in this.SubstanceManagement.MasterModelClassInfos)
            {
                if (classifier_info.ValidationDocument == true)
                    this.SubstanceManagement.Config.IdentModuleSettings.ValidatedClassifiers.Add(classifier_info.ModelClassName);

                this.SubstanceManagement.Config.IdentModuleSettings.ManagedClassifiers.Add(classifier_info.ModelClassName);
            }

            this.SubstanceManagement.Config.IdentModuleSettings.ManagedClassifiers.Sort(StringComparer.InvariantCulture);
            string new_maanged_classifieres = String.Join(String.Empty, this.SubstanceManagement.Config.IdentModuleSettings.ManagedClassifiers.ToArray());
            if (old_maanged_classifieres != new_maanged_classifieres)
                needs_update_managed_classifieres = true;


            if (needs_update == true)
            {
                this.SubstanceManagement.Config.IdentModuleSettings.ReleaseExpirationTime = IdentServerClient.Info.CreationTimestamp.AddMonths(6);
                this.SubstanceManagement.Config.IdentModuleSettings.Locale = IdentServerClient.Info.Locale;
                this.SubstanceManagement.Config.IdentModuleSettings.IdentServerVersion = IdentServerClient.Info.Version.IdentModuleVersion;
                 this.SubstanceManagement.Config.IdentModuleSettings.TestVersion = IdentServerClient.Info.IsRelease == false;

                this.SubstanceManagement.Config.IdentModuleSettings.UpdateValidationDocu = true;
                this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs = true;        
            }

            if (needs_update || needs_update_additional_tests == true)
            {
                this.SubstanceManagement.Config.IdentModuleSettings.IdentServerSuffix = IdentServerClient.Info.Version.IdentModuleSubVersion;
                this.SubstanceManagement.Config.IdentModuleSettings.UpdateAdditionalTestsPdfs = true;
                this.SubstanceManagement.Config.IdentModuleSettings.UpdateUpdateCertificate = true;
                this.SubstanceManagement.Config.IdentModuleSettings.UpdateIMLicenceText = true;
            }

            if (needs_update_managed_classifieres)
            {
                this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs = true;
            }
        }

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

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

                case PdfListType.Insert:
                    file = FilesAndDirs.SubstanceOverviewInsertFile;
                    break;

                case PdfListType.Added:
                    file = FilesAndDirs.SubstanceOverviewAddedFile;
                    break;

                default:
                    break;
            }

            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs == true)
                throw new Exception(Catalog.GetString("An error occurred while getting the document from the IdentModule."));

            if (type == PdfListType.Added && this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs == false)
                if (File.Exists(FilesAndDirs.SubstanceOverviewAddedFile) == false)
                    throw new Exception(Catalog.GetString("For this IdentModule the 'Substance and Batch Maintenance' is not available."));

            if (File.Exists(file) == false)
                throw new Exception(Catalog.GetString("Cannot find the file"));

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

        public void UpdateSubstancePdfs()
        {
            try
            {
                this.IsUpdatingSubstancePdfs = true;

                if (this.LoadingSubstancuDocuStarted != null)
                    this.LoadingSubstancuDocuStarted(this, EventArgs.Empty);

                Thread background_thread = new Thread(this.DoUpdateSubstancePdfs);
                background_thread.Start();
            }
            catch (Exception ex)
            {
                this.IsUpdatingSubstancePdfs = false;
                Console.WriteLine("UpdateSubstancePdfs() failed: {0}", ex.ToString());
            }
            finally
            {
                if (this.LoadingSubstanceDocuFinished != null)
                    this.LoadingSubstanceDocuFinished(this, EventArgs.Empty);
            }
        }


        private void DoUpdateSubstancePdfs()
        {
            try
            {
                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs == true)
                {
                    try
                    {
                        Directory.Delete(FilesAndDirs.SubstanceOverviewDir, true);
                    }
                    catch (Exception) { }
                }

                if (Directory.Exists(FilesAndDirs.SubstanceOverviewDir) == false)
                    Directory.CreateDirectory(FilesAndDirs.SubstanceOverviewDir);

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

                this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoUpdateAllSubstanceDocument() failed: {0}", ex.ToString());
            }
            finally
            {
                this.IsUpdatingSubstancePdfs = false;
                if (this.LoadingSubstanceDocuFinished != null)
                    this.LoadingSubstanceDocuFinished(this, EventArgs.Empty);
            }
        }

        private void UpdateAvailableSubstances()
        {
            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs == false &&
                    File.Exists(FilesAndDirs.SubstanceOverviewAvailableFile) == true)
                return;

            try
            {
                File.Delete(FilesAndDirs.SubstanceOverviewAvailableFile);
            }
            catch (Exception) { }

            PdfModelStructure list = this.GetSubstanceOverviewPdfData(false, true);
            list.ListOfContentsHeader = Catalog.GetString("List of Contents - Subscribed Substances");
            list.Version = IdentServerClient.Info.Version.IdentModuleVersion;
            list.ShowClassificationColumn = true;
            OverviewPdfCreator.Create(list, FilesAndDirs.SubstanceOverviewAvailableFile, OverviewErrorType.NoAvailablePdf);
        }

        private void UpdateInsertSubstances()
        {
            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs == false &&
                    File.Exists(FilesAndDirs.SubstanceOverviewInsertFile) == true)
                return;

            try
            {
                File.Delete(FilesAndDirs.SubstanceOverviewInsertFile);
            }
            catch (Exception) { }

            PdfModelStructure list = this.GetSubstanceOverviewPdfData(true, true);
            if (list != null && list.IsEmpty == false)
            {
                list.ListOfContentsHeader = Catalog.GetString("List of Conntents - Subscribed Substances with Sample Inset");
                list.Version = IdentServerClient.Info.Version.IdentModuleVersion;
                list.ShowClassificationColumn = false;
                OverviewPdfCreator.Create(list, FilesAndDirs.SubstanceOverviewInsertFile, OverviewErrorType.NoAvailablePdf);
            }
        }

        private void UpdateChangedSubstances()
        {
            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateSubstancePdfs == false &&
                    File.Exists(FilesAndDirs.SubstanceOverviewAddedFile) == true)
                return;

            try
            {
                File.Delete(FilesAndDirs.SubstanceOverviewAddedFile);
            }
            catch (Exception) { }

            byte[] stream = this.SubstanceManagement.Server.GetSubstanceChangeLog();
            if (stream != null)
                stream.ToFile(FilesAndDirs.SubstanceOverviewAddedFile);
        }

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

            foreach (ModelClassInfo classifier_info in this.SubstanceManagement.MasterModelClassInfos)
            {
                foreach (ClassificationInfo classification_info in classifier_info.Classifications)
                {
                    foreach (SubstanceInfo substance_info in classification_info.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 == classifier_info.ModelClassName);

                        if (model_class_entry == null)
                        {
                            model_class_entry = new PdfModelClassEntry();
                            model_class_entry.ModelClassName = classifier_info.ModelClassName;
                            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_info.ClassificationName;
                                entry.NumberOfClassificationMembers = classification_info.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_info.ClassificationName;
                            entry.NumberOfClassificationMembers = classification_info.Substances.Count;
                            model_class_entry.Substances.Add(entry);
                        }
                    }
                }
            }

            list.Sort();
            return list;
        }





        public void ShowValidationPdf(string class_name)
        {
            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateValidationDocu == true)
                throw new Exception(Catalog.GetString("An error occurred while getting the document from the IdentModule."));

            string file = FilesAndDirs.ValidationFile(class_name);
            if (File.Exists(file) == false)
                throw new Exception(Catalog.GetString("Cannot find the file."));

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

        public void UpdateValidationDocument()
        {
            try
            {
                this.IsUpdatingValdationPdfs = true;

                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateValidationDocu == true)
                {
                    try
                    {
                        Directory.Delete(FilesAndDirs.ValidationDir, true);
                    }
                    catch (Exception) { }
                }

                if (Directory.Exists(FilesAndDirs.ValidationDir) == false)
                    Directory.CreateDirectory(FilesAndDirs.ValidationDir);

                if (this.LoadingValidationDocuStarted != null)
                    this.LoadingValidationDocuStarted(this, EventArgs.Empty);

                Thread background_thread = new Thread(this.DoUpdateAllValidationDocument);
                background_thread.Start();
            }
            catch (Exception ex)
            {
                this.IsUpdatingValdationPdfs = false;
                Console.WriteLine("UpdateValidationDocument() failed: {0}", ex.ToString());
            }
            finally
            {
                if (this.LoadingValidationDocuFinished != null)
                    this.LoadingValidationDocuFinished(this, EventArgs.Empty);
            }
        }

        private void DoUpdateAllValidationDocument()
        {
            try
            {
                Dictionary<ModelClassInfo, Thread> threads = new Dictionary<ModelClassInfo, Thread>();

                foreach (ModelClassInfo classifier in this.SubstanceManagement.MasterModelClassInfos)
                {
                    if (classifier.ValidationDocument == false)
                        continue;

                    string file = FilesAndDirs.ValidationFile(classifier.ModelClassName);

                    if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateValidationDocu == false && File.Exists(file) == true)
                        continue;

                    try
                    {
                        File.Delete(file);
                    }
                    catch (Exception) { }

                    byte[] stream = this.SubstanceManagement.Server.GetValidationDocument(classifier);

                    using (FileStream fs = new FileStream(file, FileMode.CreateNew))
                    {
                        fs.Write(stream, 0, stream.Length);
                    }
                }

                this.SubstanceManagement.Config.IdentModuleSettings.UpdateValidationDocu = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoUpdateAllValidationDocument() failed: {0}", ex.ToString());
            }
            finally
            {
                this.IsUpdatingValdationPdfs = false;
                if (this.LoadingValidationDocuFinished != null)
                    this.LoadingValidationDocuFinished(this, EventArgs.Empty);
            }
        }


        private void DoUpdateSingleValidationDocument(object classifier_object)
        {
            ModelClassInfo classifier = (ModelClassInfo)classifier_object;

            try
            {
                string file = FilesAndDirs.ValidationFile(classifier.ModelClassName);

                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateValidationDocu == false && File.Exists(file) == true)
                    return;

                try
                {
                    File.Delete(file);
                }
                catch (Exception) { }

                byte[] stream = this.SubstanceManagement.Server.GetValidationDocument(classifier);

                using (FileStream fs = new FileStream(file, FileMode.CreateNew))
                {
                    fs.Write(stream, 0, stream.Length);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoUpdateSingleValidationDocument({1}) failed: {0}", ex.ToString(), classifier.ModelClassName);
            }
        }





        public void ShowAdditionalTestPdf()
        {
            string file = FilesAndDirs.AdditionalDocuFile;

            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateAdditionalTestsPdfs == true)
                throw new Exception(Catalog.GetString("An error occurred while getting the document from the IdentModule."));

            if (File.Exists(file) == false)
                throw new Exception(Catalog.GetString("Cannot find the file."));

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

        public void UpdateAdditionalTestsPdfs()
        {
            if (IdentServerClient.Info.Locale != Locale.LocaleType.de_DE)
                return;

            try
            {
                this.IsUpdatingAdditionalPdfs = true;

                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateAdditionalTestsPdfs == true)
                {
                    try
                    {
                        Directory.Delete(FilesAndDirs.AdditionalDocuDir, true);
                    }
                    catch { }
                }

                if (Directory.Exists(FilesAndDirs.AdditionalDocuDir) == false)
                    Directory.CreateDirectory(FilesAndDirs.AdditionalDocuDir);

                if (this.LoadingAdditionalTestsStarted != null)
                    this.LoadingAdditionalTestsStarted(this, EventArgs.Empty);

                Thread background_thread = new Thread(this.DoUpdateAdditionalTestsPdfs);
                background_thread.Start();
            }
            catch (Exception ex)
            {
                this.IsUpdatingAdditionalPdfs = false;
                Console.WriteLine("UpdateAdditionalTestsPdfs() failed: {0}", ex.ToString());
            }
            finally
            {
                if (this.LoadingAdditionalTestsFinished != null)
                    this.LoadingAdditionalTestsFinished(this, EventArgs.Empty);
            }
        }


        private void DoUpdateAdditionalTestsPdfs()
        {
            try
            {
                byte[] data = this.SubstanceManagement.Server.GetAddtionalTestsPdf();
                string file = FilesAndDirs.AdditionalDocuFile;

                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateAdditionalTestsPdfs == true || File.Exists(file) == false)
                {
                    try
                    {
                        File.Delete(file);
                    }
                    catch (Exception) { }

                    using (FileStream fs = new FileStream(file, FileMode.CreateNew))
                    {
                        fs.Write(data, 0, data.Length);
                    }
                }

                this.SubstanceManagement.Config.IdentModuleSettings.UpdateAdditionalTestsPdfs = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoUpdateAdditionalTestsPdfs() failed: {0}", ex.ToString());
            }
            finally
            {
                this.IsUpdatingAdditionalPdfs = false;
                if (this.LoadingAdditionalTestsFinished != null)
                    this.LoadingAdditionalTestsFinished(this, EventArgs.Empty);
            }
        }



        public void ShowUpdateCertificatePdf()
        {
            string file = FilesAndDirs.UpdateCertificateFile;

            if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateUpdateCertificate == true)
                throw new Exception(Catalog.GetString("An error occurred while getting the document from the IdentModule."));

            if (File.Exists(file) == false)
                throw new Exception(Catalog.GetString("Cannot find the file."));

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

        public void UpdateCertificate()
        {
            try
            {
                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateUpdateCertificate == false &&
                    File.Exists(FilesAndDirs.UpdateCertificateFile) == true)
                    return;

                if (Directory.Exists(FilesAndDirs.UpdateCertificateDir) == false)
                    Directory.CreateDirectory(FilesAndDirs.UpdateCertificateDir);

                try
                {
                    File.Delete(FilesAndDirs.UpdateCertificateFile);
                }
                catch { }

                UpdateCertificateInfo info = this.SubstanceManagement.Server.GetUpdateCertificateData();
                Hiperscan.UpdateCertificate.UpdateCertificate.CreateCertificate(info, FilesAndDirs.UpdateCertificateDir);

                this.SubstanceManagement.Config.IdentModuleSettings.UpdateUpdateCertificate = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("UpdateCertificate() failed: {0}", ex.ToString());
            }
        }

        public void UpdateIMLicenseText()
        {
            try
            {
                if (this.SubstanceManagement.Config.IdentModuleSettings.UpdateIMLicenceText == false &&
                    File.Exists(FilesAndDirs.IMLicenseFile) == true)
                    return;

                try
                {
                    File.Delete(FilesAndDirs.IMLicenseFile);
                    string text = this.SubstanceManagement.Server.GetLicense();

                    using (StreamWriter sw = new StreamWriter(FilesAndDirs.IMLicenseFile, false, Encoding.UTF8))
                    {
                        sw.Write(text);
                    }
                }
                catch { }


                this.SubstanceManagement.Config.IdentModuleSettings.UpdateIMLicenceText = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("UpdateIMLicenseText() failed: {0}", ex.ToString());
            }
        }

        private SubstanceManagement SubstanceManagement { get; set; }

        public bool IsUpdatingSubstancePdfs  { get; private set; }
        public bool IsUpdatingValdationPdfs  { get; private set; }
        public bool IsUpdatingAdditionalPdfs { get; private set; }
    }
}