// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2022, 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 Hiperscan.Common.I18n;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.Method;
using Hiperscan.Unix;
using Hiperscan.ApoIdentCore.GuiControl;
using Hiperscan.ApoIdentCore.SectionManager.Substance.SubstanceSelection;
using Hiperscan.ApoIdentCore.SectionManager.Result.Fields;
using Hiperscan.ApoIdentCore.Enums;
using Hiperscan.ApoIdentCommon.ApoConfig;
using Hiperscan.ApoIdentCommon.ApoConfig.Cache;
using Hiperscan.GuiExtensions.Dialog;
using Hiperscan.AnalysisModule.Identification.Result;
using Hiperscan.AnalysisModule.Identification;
using Hiperscan.ApoIdentCore.SectionManager.Substance;
using Hiperscan.ApoIdentCore.Misc;
using Hiperscan.AnalysisModule;

namespace Hiperscan.ApoIdentCore.SectionManager.Result
{
    public class ResultManager : ISectionManager
    {
        public const string SUGGESTION_INFO = @"
Hinweis: Diese Prüfvorschriften wurden vom Institut für Pharmazie der Universität Leipzig und der Apothekerin Elke Parchmann erstellt und verifiziert. Ziel ist es, eine eindeutige Unterscheidung innerhalb einer Gruppe/Klassifizierung zu gewährleisten. Um dies sicherzustellen, müssen für die betreffende Substanz alle ergänzenden Prüfungen durchgeführt werden. Bei den angegebenen Prüfungen handelt es sich um Abwandlungen und ggf. Ergänzungen bereits etablierter Prüfvorschriften einschlägiger Monographiesammlungen (Ph. Eur., DAC/NRF, apothekengerechte Prüfvorschriften).

<i>Es obliegt dem zuständigen Apotheker zu entscheiden, ob der Vorschlag für diese Substanz angewendet werden sollen. Er muss diese Entscheidung dokumentieren und ggf. auch gegenüber der Aufsichtsbehörde verteidigen, falls diese die Validität der Kombination aus NIR-Prüfung und ergänzender Prüfung anzweifelt.</i>
        ";

        private const string HTML_BOLD_START_TAG = "<b>";
        private const string HTML_BOLD_END_TAG = "</b>";

        private const string HTML_SMALL_START_TAG = "<span size=\"small\">";
        private const string HTML_SMALL_END_TAG = "</span>";



        public event ApoUpdateHandler MisIdentificationDetected = null;
        public event EventHandler CompleteStateChanged = null;

        public ResultManager(CatalogLocale.LocaleType locale,
                             ProfileManager profile_manager,
                            GlobalStates states)
        {
            this.Locale = locale;
            this.ProfileManager = profile_manager;
            this.GlobalStates = states;

            this.AdditopalTestField.ValueChanged += (sender, e) => this.SetCompletedState();
        }

        public void SetValidStateChangedEvents(EventHandler handler)
        {
            this.AdditopalTestField.ValidStateChanged += handler;
        }

        public void SetValueChangedEvents(EventHandler handler)
        {
            this.AdditopalTestField.ValueChanged += handler;
            this.Comment.ValueChanged += handler;
            this.CustomerEvaluationField.ValueChanged += handler;
        }

        public void ResetAdditionalTests()
        {
            if (this.AnalysisType != AnalysisType.Quantification)
                return;

            this.InitializeAdditionalTest();
        }

        private void InitializeAdditionalTest()
        {
            this.CurrentAdditionalTest = this.ProfileManager.CurrentProfile.AdditionalTest;
            this.CurrentAdditionalTest.IsGreenSubstance = this.SubstancesCatalog.ClassificationType == ClassificationType.Single;
            this.CurrentAdditionalTest.InsertIntoProtocol = false;
            this.CurrentAdditionalTest.TruePositive = false;
            this.CurrentAdditionalTest.CustomerEvalution = false;
            this.CurrentAdditionalTest.AnalysisType = this.AnalysisType;
            this.CurrentAdditionalTest.QuantificationConfirmed = false;

            this.AdditopalTestField.IsMandatory = this.IsAdditionalTestMandatory();
        }

        public void UpdateAfterSubstanceSelection(SubstanceManager substance_manager)
        {
            this.SubstanceManager = substance_manager;
            this.InitializeAdditionalTest();
            this.SetCompletedState();
        }

        private bool IsAdditionalTestMandatory()
        {
            if (this.ProfileManager.CurrentProfile == null)
                return false;

            if (this.ProfileManager.CurrentProfile.AdditionalTest.IsMandatoryForComplementaryTest == false)
                return false;

            if (this.SubstancesCatalog == null)
                return false;

            if (this.AnalysisType == AnalysisType.Quantification)
                return true;

            if (this.ProfileManager.CurrentProfile.ExchangeData.IsActivated)
            {
                if (this.Locale == CatalogLocale.LocaleType.de_DE)
                    return false;
            }

            if (this.IsNirMeasurable == false)
                return true;

            if (this.SubstancesCatalog.ClassificationType == ClassificationType.Multiple)
                return true;

            return this.IdentificationIsTruePositive == false;
        }

        public string GetTypeOfAdditionalTest()
        {
            return this.GetAdditionalTestLabelName().Replace("-\n", String.Empty).
                       Replace("\n", " ").
                       Replace("*", String.Empty);
        }

        public string GetAdditionalTestLabelName()
        {
            this.GetAdditionalTestLabelInfos(out string label, out _);
            return label;
        }

        public string GetAdditionalTestLabelTooltip()
        {
            this.GetAdditionalTestLabelInfos(out _, out string tooltip);
            return tooltip;
        }

        private void GetAdditionalTestLabelInfos(out string label, out string tooltip)
        {
            string complementary_tooltip = Catalog.GetString("The NIR result is ambiguous. You may disambiguate by means of a completive test. Click here to document it.");
            string additional_tooltip = Catalog.GetString("The NIR-result is unambiguous. If you wish to further affirm the result by additional tests, you may click here to document them.");
            string identity_tooltip = Catalog.GetString("NIR cannot or could not identify this substance. You may use an alternative method and click here to document the result.");


            string quantify_tool_tip = Catalog.GetString("Documentation of methods and results was already made. You cannot edit the content anymore.");
            if (this.QuantificationResultExists == false)
                quantify_tool_tip = Catalog.GetString("Documentation of methods and results");

            string complementary_label = Catalog.GetString(@"Additional
Tests"); ;
            string additional_label = Catalog.GetString(@"Additional
Tests");
            string identity_label = Catalog.GetString(@"Identity
tests");
            string quantify_label = Catalog.GetString(@"Documen-
tation");

            tooltip = additional_tooltip;
            label = additional_label;

            if (this.ProfileManager.CurrentProfile == null)
                return;

            if (this.SubstancesCatalog == null)
                return;

            if (this.SubstancesCatalog.AnalysisType == AnalysisType.Quantification)
            {
                label = quantify_label;
                tooltip = quantify_tool_tip;
            }
            else if (this.SubstancesCatalog.ClassificationType == ClassificationType.NotMeasurable)
            {
                label = identity_label;
                tooltip = identity_tooltip;
            }
            else if (this.SubstancesCatalog.ClassificationType == ClassificationType.UserDefined)
            {
                label = identity_label;
                tooltip = identity_tooltip;
            }
            else if (this.SubstancesCatalog.ClassificationType == ClassificationType.Multiple)
            {
                label = complementary_label;
                tooltip = complementary_tooltip;
            }

            if (this.IdentificationResultExists && this.IsNirMeasurable)
            {
                if (this.IdentificationIsTruePositive == false)
                {
                    label = identity_label;
                    tooltip = identity_tooltip;
                }
            }

            if (this.IsAdditionalTestMandatory())
                label += "*";
        }

        public void Analyse(Spectrum spectrum)
        {
            if (this.AnalysisType == AnalysisType.Quantification)
                this.Quantify(spectrum);
            else
                this.Classify(spectrum);
        }

        private void Classify(Spectrum spectrum)
        {
            this.IdentificationResult = (ApoIdentResultSet)this.SubstanceManager.Analyse(this.SubstancesCatalog.AnalysisType,
                                                                                          spectrum);
            this.SetCompletedState();
        }

        private void Quantify(Spectrum spectrum)
        {
            this.QuantificationResult = (CannabisResult)this.SubstanceManager.Analyse(this.SubstancesCatalog.AnalysisType, spectrum);
            this.SetCompletedState();
        }

        public void CheckIdentificationResult()
        {
            if (this.SubstancesCatalog.ClassificationType == ClassificationType.Single)
                this.CustomerEvaluationField.Entry = this.IdentificationIsTruePositive;
            else
                this.CustomerEvaluationField.Entry = false;

            this.AdditopalTestField.IsMandatory = this.IsAdditionalTestMandatory();
            this.CurrentAdditionalTest.TruePositive = this.IdentificationIsTruePositive;
            this.CurrentAdditionalTest.CustomerEvalution = this.CustomerEvaluationField.GetEntryValue();
            this.AdditopalTestField.SetDefaultValue();
            this.CurrentAdditionalTest.InsertIntoProtocol = false;
            this.SetCompletedState();

            if (this.IdentificationIsTruePositive)
            {
                this.WasLastIdentificationPositive = true;
                return;
            }

            UpdateArgs arg = UpdateArgs.GetResetArg();

            if (this.SubstancesCatalog.ModelClass.SubstanceType == SubstanceType.Fluid)
                arg.AnimationType = ProgramState.IdentificationFluidFailed;
            else
                arg.AnimationType = ProgramState.IdentificationSolidFailed;

            if (this.WasLastIdentificationPositive == true && this.ProfileManager.CurrentProfile.DontShowHintMessageAgain == false)
            {
                arg.DialogArgs.StandardDialogType = StandardDialogType.DontShowAgainQuestion;
                arg.DialogArgs.Title = Catalog.GetString("Identification failed");
                arg.DialogArgs.Message = Catalog.GetString("The identification failed. This may result from incorrect operation.");
                arg.DialogArgs.Message += "\n\n" + Catalog.GetString("Further information?");
                arg.DialogArgs.DontShowAgainMesssage = Catalog.GetString("Do not show this dialogue again during the current session");

                this.MisIdentificationDetected?.Invoke(arg);
                this.ProfileManager.CurrentProfile.DontShowHintMessageAgain = arg.DialogArgs.DontShowAgain;

                this.WasLastIdentificationPositive = false;
            }
            else
            {
                this.MisIdentificationDetected?.Invoke(arg);
            }
        }

        public void CheckQuantificationResult()
        {
            if (this.QuantificationResult.IsTrustable)
                return;

            UpdateArgs arg = UpdateArgs.GetResetArg();
            arg.AnimationType = ProgramState.QuantificationFailed;
            arg.DialogArgs.StandardDialogType = StandardDialogType.Error;
            arg.DialogArgs.Title = Catalog.GetString("Quantification failed");
            arg.DialogArgs.Message = Catalog.GetString("The result is not plausible. Please repeat the measurement."); 
            arg.DialogArgs.Message += Catalog.GetString(@"

If the problem persists, please consult your distributor or Hiperscan customer support:
E-mail: info@hiperscan.com
Phone: +49 351 212 496 0");
            this.MisIdentificationDetected?.Invoke(arg);
        }

        public bool IsYellowSubstance()
        {
            if (this.AnalysisResultExists == false)
                return false;

            return this.SubstancesCatalog.IsYellowSubstance;
        }

        public string GetTHCValue(bool use_html)
        {
            string value = String.Empty;

            if (this.QuantificationResultExists)
            {
                if (this.AnalysisType == AnalysisType.Quantification)
                {
                    value = this.GetCannabisOutput(use_html, this.QuantificationResult.IsTrustable);
                    value = String.Format(value,
                                          this.QuantificationResult.GetTHCResultInPercent(),
                                          this.QuantificationResult.GetTHCTolerance(),
                                          this.QuantificationResult.GetTHCSigma());
                }
            }

            return value;
        }

        public string GetCBDValue(bool use_html)
        {
            string value = String.Empty;

            if (this.QuantificationResultExists)
            {
                if (this.AnalysisType == AnalysisType.Quantification)
                {
                    value = this.GetCannabisOutput(use_html, this.QuantificationResult.IsTrustable);
                    value = String.Format(value,
                                          this.QuantificationResult.GetCBDResultInPercent(),
                                          this.QuantificationResult.GetCBDTolerance(),
                                          this.QuantificationResult.GetCBDSigma());
                }
            }

            return value;
        }

        private string GetCannabisOutput(bool use_html, bool is_trustable)
        {
            string output = String.Empty;

            if (is_trustable == false)
            {
                output = Catalog.GetString("n/a");
                if (use_html)
                    output = ResultManager.HTML_BOLD_START_TAG + output + ResultManager.HTML_BOLD_END_TAG;

                return output;
            }

            output = String.Format("{0}{{0}}{1} {{1}} {2}{{2}}{3}",
                                   use_html ? ResultManager.HTML_BOLD_START_TAG : String.Empty,
                                   use_html ? ResultManager.HTML_BOLD_END_TAG : String.Empty,
                                   use_html ? ResultManager.HTML_SMALL_START_TAG : String.Empty,
                                   use_html ? ResultManager.HTML_SMALL_END_TAG : String.Empty);

            return output;
        }

        public string GetClassificationName(bool use_html_thick)
        {
            string value = String.Empty;

            if (this.AnalysisResultExists == true)
            {

                if (this.IsYellowSubstance())
                    value = this.SubstancesCatalog.Classification.Name;
                else
                    value = this.SubstancesCatalog.SelectedSubstanceName;

                if (this.AnalysisType == AnalysisType.Quantification)
                {
                    if (this.QuantificationResult.IsTrustable)
                        value += " (" + this.QuantificationResult.GetCannabisClassification() + ")";
                    else
                        value = this.QuantificationResult.GetCannabisClassification();
                }
            }

            if (use_html_thick)
                return ResultManager.HTML_BOLD_START_TAG + value + ResultManager.HTML_BOLD_END_TAG;

            return value;
        }

        public string GetNirResultText(bool use_html_thick)
        {
            string value = String.Empty;

            if (this.AnalysisType == AnalysisType.Quantification)
            {
                if (use_html_thick)
                    value = ResultManager.HTML_BOLD_START_TAG + value + ResultManager.HTML_BOLD_END_TAG;

                return value;
            }


            if (this.IdentificationResultExists == true)
            {

                if (this.SubstancesCatalog.IsSelectedSubstanceNirMeasurable == false)
                {
                    value = Catalog.GetString("Not NIR-active");
                }
                else
                {
                    if (this.IdentificationIsTruePositive == true)
                        value = Catalog.GetString("Match");
                    else
                        value = Catalog.GetString("No match");
                }
            }

            if (use_html_thick)
                return ResultManager.HTML_BOLD_START_TAG + value + ResultManager.HTML_BOLD_END_TAG;

            return value;
        }

        public string GetNirRatingText(bool use_html_thick)
        {
            string value = String.Empty;
            if (use_html_thick)
                value = ResultManager.HTML_BOLD_START_TAG + value + ResultManager.HTML_BOLD_END_TAG;

            if (this.IdentificationResultExists == true)
            {
                string min_rating = (this.IdentificationResult.MinRating * 100).ToString("f0");

                string rating = (this.IdentificationResult.Rating * 100).ToString("f1");

                if (this.IdentificationIsTruePositive == false)
                {
                    if (rating == (this.IdentificationResult.MinRating * 100).ToString("f1"))
                        rating = (this.IdentificationResult.MinRating * 100 - 0.1).ToString("f1");
                }

                value = String.Format(Catalog.GetString("{0}{1}%{2} (Limits {3}% to 100%)"),
                                      use_html_thick ? ResultManager.HTML_BOLD_START_TAG : String.Empty,
                                      rating,
                                      use_html_thick ? ResultManager.HTML_BOLD_END_TAG : String.Empty,
                                      min_rating);
            }

            return value;
        }

        public string GetCommentText()
        {
            return this.Comment.GetEntryValue();
        }

        public string GetAdditionalTestText()
        {
            if (this.ProfileManager.CurrentProfile == null)
                return String.Empty;

            if (this.ProfileManager.CurrentProfile.AdditionalTest == null)
                return String.Empty;

            return this.AdditopalTestField.GetEntryValue().Replace("\n", " ");
        }

        public bool HasSuggestion()
        {
            if (this.SubstancesCatalog == null)
                return false;

            if (this.Locale != CatalogLocale.LocaleType.de_DE)
                return false;

            if (this.AnalysisType == AnalysisType.Quantification &&
                this.SubstancesCatalog.ModelClass.HasAdditionalTestDocument)
                return true;

            if (this.SubstancesCatalog.ClassificationType != ClassificationType.Multiple)
                return false;

            if (String.IsNullOrEmpty(this.SubstancesCatalog.Substance?.PdfLinkAdditionalTest))
                return false;

            return true;
        }

        public bool? GetNirEvaluation()
        {
            if (this.IdentificationResultExists == false ||
                this.IsNirMeasurable == false)
                return null;

            if (this.IdentificationIsTruePositive == true)
                return true;
            else
                return false;
        }

        private void SetCompletedState()
        {
            bool value = true;

            if (this.AnalysisResultExists == false)
                value = false;
            else if (this.CurrentAdditionalTest.IsUserInputValid == false)
                value = false;

            this.IsComplete = value;
        }

        public void Clear()
        {
            this.Reset();
        }

        public void Reset()
        {
            this.SubstanceManager = null;
            this.CurrentAdditionalTest = null;
            this.DiscardResult();
            this.WasLastIdentificationPositive = true;
            this.AdditopalTestField.SetDefaultValue();
            this.Comment.SetDefaultValue();
            this.CustomerEvaluationField.SetDefaultValue();

            this.AdditopalTestField.IsMandatory = this.IsAdditionalTestMandatory();
            this.SetCompletedState();
        }

        public void DiscardResult()
        {
            this.SubstanceManager?.DiscardResults();
            this.IdentificationResult = null;
            this.QuantificationResult = null;
            this.Comment.SetDefaultValue();

            if (this.AnalysisType != AnalysisType.Quantification)
            {
                this.AdditopalTestField.SetDefaultValue();
                this.AdditopalTestField.IsMandatory = this.IsAdditionalTestMandatory();
            }

            this.SetCompletedState();
        }

        public bool IsNirMeasurable
        {
            get
            {
                if (this.SubstancesCatalog == null)
                    return false;

                return this.SubstancesCatalog.IsSelectedSubstanceNirMeasurable;
            }
        }

        public bool AnalysisResultExists
        {
            get
            {
                if (this.SubstancesCatalog == null)
                    return false;

                if (this.CurrentAdditionalTest == null)
                    return false;

                if (this.IsNirMeasurable == false)
                    return true;

                if (this.IdentificationResult != null)
                    return true;

                return this.QuantificationResult != null;
            }
        }

        private bool IdentificationResultExists
        {
            get
            {
                return this.AnalysisType != AnalysisType.Quantification && this.IdentificationResult != null;
            }
        }

        private bool QuantificationResultExists
        {
            get
            {
                return this.AnalysisType == AnalysisType.Quantification && this.QuantificationResult != null;
            }
        }

        private bool is_complete = false;
        public bool IsComplete
        {
            get
            {
                return is_complete;
            }

            private set
            {
                if (this.is_complete == value)
                    return;

                this.is_complete = value;
                this.CompleteStateChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        public bool IdentificationIsTruePositive
        {
            get
            {
                if (this.AnalysisResultExists == false)
                    return false;

                return this.IdentificationResult.TruePositive;
            }
        }

        public AnalysisType AnalysisType
        {
            get
            {
                return this.SubstancesCatalog?.AnalysisType ?? AnalysisType.None;
            }
        }

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


        public ApoIdentResultSet IdentificationResult { get; private set; }
        public CannabisResult QuantificationResult { get; private set; }

        public CommentField Comment { get; private set; } = new CommentField();
        public AdditionalTestField AdditopalTestField { get; private set; } = new AdditionalTestField();
        public CustomerEvaluationField CustomerEvaluationField { get; private set; } = new CustomerEvaluationField();

        public AdditionalTest CurrentAdditionalTest { get; private set; }
        public bool WasLastIdentificationPositive { get; set; } = true;

        private ProfileManager ProfileManager { get; set; }
        private SubstanceManager SubstanceManager { get; set; }
        private CatalogLocale.LocaleType Locale { get; set; }
        private GlobalStates GlobalStates { get; set; }
    }
}
