// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2022 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.Linq;
using Hiperscan.AnalysisModule;
using Hiperscan.AnalysisModule.Identification;
using Hiperscan.ApoIdentCommon.ApoConfig.ThirdPartyDataExchange;
using Hiperscan.Unix;

namespace Hiperscan.ApoIdentCore.SectionManager.Substance.SubstanceSelection
{
    public partial class SubstancesCatalog
    {
        public event ErrorHandler ExchangeDataErrorDetected = null;
        public event ErrorHandler NoExchangeSubstansteIdentified = null;
        public event EventHandler ExchangeFuzzySubstanceNameIdentified = null;

        public void SelectSubstance(string substance_name)
        {
            if (this.SelectedSubstanceName == substance_name)
                return;

            if (this.Resolver.GetSubstanceInfosByName(substance_name).Count == 0)
            {
                this.SubstanceSelectionAborted?.Invoke(this, EventArgs.Empty);
                return;
            }

            if (this.IsProtocolSaved() == false)
            {
                this.SubstanceSelectionAborted?.Invoke(this, EventArgs.Empty);
                return;
            }

            this.Reset();
            this.PreselectedSubstances = this.Resolver.GetSubstanceInfosByName(substance_name);
            this.SetBestReferenceSubstanceName(substance_name);
            this.SelectionType = SubstanceSelectionType.FromAutoCompletion;
            this.WriteKindOfSubstanceSelection();
            this.SelectModelClass();
        }

        public void SelectSubstance(ExchangeDataSet exchange_data_set)
        {
            this.Reset();

            string message = exchange_data_set.Check();
            if (String.IsNullOrEmpty(message) == false)
            {
                message = String.Format(Catalog.GetString("Cannot import data, due to missing data: {0}"), message);
                this.ExchangeDataErrorDetected?.Invoke(message);
                return;
            }

            if (String.IsNullOrEmpty(exchange_data_set.HiperScanSubstanceId) == false)
                this.PreselectedSubstances = this.Resolver.GetSubstanceInfosById(exchange_data_set.HiperScanSubstanceId);

            if (this.PreselectedSubstances.Count == 0)
            {
                this.PreselectedSubstances = this.FindFuzzyDialogParameter(exchange_data_set.PrimarySubstanceName);

                if (this.PreselectedSubstances.Count == 0)
                {
                    this.NoExchangeSubstansteIdentified?.Invoke(this.NoDataExchangeDataFoundMessage(exchange_data_set));
                    return;
                }

                this.FuzzyName = exchange_data_set.PrimarySubstanceName;
                this.ExchangeFuzzySubstanceNameIdentified?.Invoke(this, EventArgs.Empty);

                this.PreselectedSubstances = this.PreselectedSubstances.
                    Where(item => item.FromUserSelected == true).
                    ToList();

                if (this.PreselectedSubstances.Count == 0)
                    return;
            }

            this.SetBestReferenceSubstanceName(exchange_data_set.PrimarySubstanceName);
            this.SubstanceTradeName = exchange_data_set.PrimarySubstanceName;
            this.SelectionType = SubstanceSelectionType.ThirdParty;
            this.ExchangeData = exchange_data_set;
            this.WriteKindOfSubstanceSelection();
            this.SelectModelClass();
        }

        private void SelectModelClass()
        {
            IdentSubstance substance = null;

            switch (this.PreselectedSubstances.Count)
            {
                case 1:
                    substance = this.PreselectedSubstances.First();
                    break;

                default:
                    this.FuzzyName = this.PreselectedSubstances.First().BestReferenceName;
                    this.SeveralModelClassesDetected?.Invoke(this, EventArgs.Empty);
                    substance = this.PreselectedSubstances.First(item => item.FromUserSelected);
                    break;
            }

            this.SetNewSubstance(substance);
        }

        private void SetNewSubstance(IdentSubstance substance)
        {
            this.Substance = substance;
            this.ModelClass = substance.ModelClass;
            this.Classification = substance.Classification;

            this.SelectedSubstanceName = substance.BestReferenceName;
            this.SelectedDefaultName = this.Substance.PrimaryDefaultName;
            this.SelectedLatinName = this.Substance.PrimaryLatinName;

            this.ClassificationType = this.Classification.ClassificationType;
            this.AnalysisType = this.ModelClass.DatabaseInfo.AnyalysisType;

            HashSet<string> exclude_names = new HashSet<string>();
            exclude_names.Add(this.SelectedSubstanceName);
            if (String.IsNullOrEmpty(this.SelectedLatinName) == false)
                exclude_names.Add(this.SelectedLatinName);

            this.Synonnyms = substance.GetSynonyms(exclude_names);
            this.PreselectedSubstances = this.Resolver.GetSubstanceInfosByName(substance.BestReferenceName);

            this.IsSubstanceSelected = true;
            this.IsSelectedSubstanceNirMeasurable = this.Classification.IsNirMeasurable;
  
            this.SubstanceSelectionChanged?.Invoke(this, EventArgs.Empty);
        }

        private void SetBestReferenceSubstanceName(string reference_substance_name)
        {
            foreach (IdentSubstance substance in this.PreselectedSubstances)
                substance.FindFuzzyParameters(reference_substance_name);
        }

        private List<IdentSubstance> FindFuzzyDialogParameter(string reference_substance_name)
        {
            foreach (IdentModelClass model_class in this.Resolver.AllModelClasses)
                model_class.CalcFuzzy(reference_substance_name);

            return this.Resolver.AllModelClasses.
                       SelectMany(m => m.GetAllSubstances()).
                       Where(s => s.FuzzyScore >= IdentSubstance.FUZZY_SCORE_THRESHOLD).
                ToList();
        }

        private bool IsProtocolSaved()
        {
            this.ContinueAction = false;
            this.BeginSubstanceSelection?.Invoke(this, EventArgs.Empty);
            return this.ContinueAction;
        }

        private void WriteKindOfSubstanceSelection()
        {
            string message = String.Format("Substance was selected by {0}\n", this.SelectionType);
            foreach (IdentSubstance substance in this.PreselectedSubstances)
            {
                message += String.Format(" => SubstanceId = {0}; SubstanceName = {1}; ModelClass = {2}; ",
                                         substance.Id,
                                         substance.BestReferenceName,
                                         substance.ModelClass.Name);
            }

            Console.WriteLine(message);
        }

        private string NoDataExchangeDataFoundMessage(ExchangeDataSet exchange_data_set)
        {
            return String.Format(Catalog.GetString("Cannot find or resolve substance name '{0}'. If you want to delete this import data, please use the '{1}'-application."),
                                                            exchange_data_set.PrimarySubstanceName,
                                                            exchange_data_set.Application);
        }

        private bool is_substance_selected = false;
        public bool IsSubstanceSelected
        {
            get
            {
                return this.is_substance_selected;
            }

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

                this.is_substance_selected = value;
                this.SubstanceSelectionStateChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private bool is_nir_measurable = false;
        public bool IsSelectedSubstanceNirMeasurable
        {
            get
            {
                return this.is_nir_measurable;
            }

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

                this.is_nir_measurable = value;
                this.SubstanceMeasurableStateChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        public SubstanceSelectionType SelectionType { get; set; }
        public IdentSubstance Substance { get; set; }
        public IdentModelClass ModelClass { get; private set; }
        public IdentClassification Classification { get; private set; }
        public string SelectedSubstanceName { get; private set; }
        public string SelectedDefaultName { get; private set; }
        public string SelectedLatinName { get; private set; }
        public ClassificationType ClassificationType { get; private set; }
        public HashSet<string> Synonnyms { get; private set; }
        public string SubstanceTradeName { get; private set; }
        public AnalysisType AnalysisType { get; private set; }

        public string FuzzyName { get; set; } = String.Empty;
        public List<IdentSubstance> PreselectedSubstances { get; private set; }
        public ExchangeDataSet ExchangeData { get; private set; }
        public bool SelectionFromMatch { get; set; } = false;
    }
}
