// 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.Linq;
using Hiperscan.Extensions.ApoIdent.SubstanceManagementExtension;
using Hiperscan.Extensions.ApoIdentCommon.Scanner;
using Hiperscan.Spectroscopy.IdentModule;
using Hiperscan.Spectroscopy.Method;

namespace Hiperscan.Extensions.ApoIdent
{
    public partial class SubstanceManagement
    {
        private const string SEPARATOR = "<<>>";

        public event EventHandler BeginLoadingData = null;
        public event EventHandler LoadingDataProgressed = null;
        public event EventHandler LoadingDataFinshed = null;

        public event EventHandler BeforeSubstanceSelectionChanged = null;
        public event EventHandler AfterSubstanceSelectionChanged = null;
        public event EventHandler SubstanceSelectionNotChanged = null;
        public event EventHandler NoSubstanceSelected = null;

        public event EventHandler ModelClassHandlingRequested = null;

        public SubstanceManagement(Configurations config, IdentServerClient client)
        {
            this.Config = config;
            this.Server = client;
            this.Pdfs = new SubstancePdfs(this);
        }

        public void StartLoading()
        {
            this.BeginLoadingData?.Invoke(this, EventArgs.Empty);
        }

        public void LoadingProgress(double progress)
        {
            if (this.LoadingDataProgressed != null)
            {
                DataEventArgs args = new DataEventArgs();
                args.Data.Add(DataEventArgs.PROGRESS, progress);

                this.LoadingDataProgressed(this, args);
            }
        }

        public void LoadingFinished()
        {
            if (this.LoadingDataFinshed != null)
                this.LoadingDataFinshed(this, EventArgs.Empty);
        }

        public bool ContainsEntryForScan(ScannerInfo scanner_info)
        {
            return this.SubstanceIdExists(scanner_info.SubstanceId);
        }

        public void SelectNewSubstance(ScannerInfo scanner_info)
        {
            List <FuzzyNameSet> sets = this.FindBestNamesBySubstanceId(scanner_info.SubstanceId, scanner_info.RegisteredName);
            this.SelectNewSubstanceInternal(sets.First().SubstanceName, ProtocolAutoCompletionType.Scanner);
        }

        public void SelectNewSubstance(FuzzyNameSet fuzzy_set)
        {
            this.SelectNewSubstanceInternal(fuzzy_set.SubstanceName, ProtocolAutoCompletionType.ThirdParty);
        }

        public void SelectNewSubstance(string substance_name, ProtocolAutoCompletionType completion_state)
        {
            this.SelectNewSubstanceInternal(substance_name, completion_state);
        }

        private void SelectNewSubstanceInternal(string substance_name, ProtocolAutoCompletionType protocol_completion_type)
        {
            if (protocol_completion_type == ProtocolAutoCompletionType.None)
            {
                this.SubstanceSelectionNotChanged?.Invoke(this, EventArgs.Empty);
                return;
            }

            if (this.SubstanceNameExists(substance_name) == false)
            {
                this.SubstanceSelectionNotChanged?.Invoke(this, EventArgs.Empty);
                return;
            }

            if (substance_name == this.CurrentSelectedName && protocol_completion_type == ProtocolAutoCompletionType.ApoIdent)
            {
                this.SubstanceSelectionNotChanged?.Invoke(this, EventArgs.Empty);
                return;
            }

            this.ContinueAction = true;
            this.BeforeSubstanceSelectionChanged?.Invoke(this, EventArgs.Empty);
            if (this.ContinueAction == false)
            {
                this.SubstanceSelectionNotChanged?.Invoke(this, EventArgs.Empty);
                return;
            }

            this.ProtocolCompletion = protocol_completion_type;
            HashSet<string> model_class_names = this.GetModelClassesBySubstanceName(substance_name);

            if (model_class_names.Count > 1)
            {
                this.CreateSubstanceRequest(substance_name);
                this.ModelClassHandlingRequested?.Invoke(this, EventArgs.Empty);
                return;
            }

            this.SelectSubstanceByName(model_class_names.First(), substance_name);
            this.AfterSubstanceSelectionChanged?.Invoke(this, EventArgs.Empty);
        }

        public List<FuzzyNameSet> FindSubstanceForDataExchange(ExchangeDataSet data_set)
        {
            List<FuzzyNameSet> values = this.FindBestNamesBySubstanceId(data_set.HiperScanSubstanceId, data_set.PrimarySubstanceName);

            if  (values.Count == 0)
                values.AddRange(this.FindSubstanceByName(data_set));

            return values;
        }

        private List<FuzzyNameSet> FindBestNamesBySubstanceId(string substance_id, string substance_name)
        {
            List<FuzzyNameSet> fuzzy_sets = new List<FuzzyNameSet>();

            if (String.IsNullOrEmpty(substance_id) == true)
                return fuzzy_sets;

            if (this.SubstanceIdExists(substance_id) == false)
                return fuzzy_sets;

            SubstanceInfo substance_info = this.GetSusbtanceInfo(substance_id);
            if (String.IsNullOrEmpty(substance_name))
                substance_name = substance_info.PrimaryDefaultName;

            foreach (string model_class_name in this.GetModelClassesBySubstanceId(substance_info.Id))
            {
                fuzzy_sets.Add(new FuzzyNameSet(model_class_name,
                                                substance_name,
                                                true,
                                                substance_info));
            }

            return  fuzzy_sets;
        }

        private List<FuzzyNameSet> FindSubstanceByName(ExchangeDataSet data_set)
        {
            List<FuzzyNameSet> name_sets = new List<FuzzyNameSet>();

            foreach (ModelClassInfo class_info in this.MasterModelClassInfos)
            {
                foreach (ClassificationInfo classification_info in class_info.Classifications)
                {
                    foreach (SubstanceInfo substance_info in classification_info.Substances)
                    {
                        if (substance_info.IsVisible == false)
                            continue;
                        
                        FuzzyNameSet name_set = new FuzzyNameSet(class_info.ModelClassName,
                                                                 data_set.PrimarySubstanceName,
                                                                 false,
                                                                 substance_info);

                        if (name_set.Score >= FuzzyNameSet.SCORE_THRESHOLD)
                            name_sets.Add(name_set);
                    }
                }
            }

            return name_sets;
        }



        public Dictionary<string, string> CheckUniqueSubstanceName(string cleaned_substance_name)
        {
            Dictionary<string, string> values = new Dictionary<string, string>();

            foreach (string substance_name in this.AllSubstanceNames)
            {
                if (substance_name.xCleanForSubstanceSearch() == cleaned_substance_name)
                {
                    foreach (string classifier_name in  this.GetModelClassesBySubstanceName(substance_name))
                    {
                        string primary_name = this.GetModelClassificationInfo(classifier_name).GetSubstanceInfo(substance_name).PrimaryDefaultName;
                        values.Add(classifier_name, primary_name);
                    }

                    return values;
                }
            }

            return values;
        }


        private void SelectSubstanceByName(string model_clase_name, string substance_name)
        {
            this.CurrentModelClass = this.GetModelClassificationInfo(model_clase_name);

            string substance_name_without_batch = BatchExtension.RemoveExtension(substance_name);

            this.CurrentClassification  = this.CurrentModelClass.GetClassificationInfo(substance_name_without_batch);
            this.CurrentSubstance       = this.CurrentModelClass.GetSubstanceInfo(substance_name_without_batch);

            this.CurrentSelectedName = substance_name;
            this.CurrentBatchNumber = BatchExtension.GetBatchNumber(substance_name);
            this.CurrentSubstanceCategory = SubstanceListAutoCompletionEntry.ConvertToSubstanceCategory(this.CurrentClassification);

            this.CurrentSelectedDefaultName = this.CurrentSubstance.GetSelectedDefaultName(substance_name_without_batch);
            this.CurrentSelectedLatinName = this.CurrentSubstance.GetSelectedLatinName(substance_name_without_batch); ;

            if (this.CurrentClassification.ClassificationType == ClassificationType.NonMultiple)
            {
                this.CurrentSelectedDefaultName = BatchExtension.AddExtension(this.CurrentSelectedDefaultName, this.CurrentBatchNumber);
                if (String.IsNullOrEmpty(this.CurrentSelectedLatinName) == false)
                    this.CurrentSelectedLatinName = BatchExtension.AddExtension(this.CurrentSelectedLatinName, this.CurrentBatchNumber);
            }
        }

        public SubstanceCategory GetSubstanceCategory(string model_class_name, string substance_name)
        {
            ModelClassInfo model_class_info = this.GetModelClassificationInfo(model_class_name);
            ClassificationInfo classification_info = model_class_info.GetClassificationInfo(substance_name);
            return SubstanceListAutoCompletionEntry.ConvertToSubstanceCategory(classification_info);
        }

        public void InitModelExtension()
        {     
             this.Extension = this.Config.Profiles.Current?.SubstanceExtension;
            if (this.Extension != null && this.Extension.GetModelClassExtension().Classifications.Count != 0)
                this.RefreshExtension();
        }

        public void RefreshExtension()
        {
            this.RemoveUserdefinedModelClassFromResolver();

            this.Extension.GetModelClassExtension().InitResolver();
            this.Extension.GetModelClassExtension().DatabaseInfo = AccessDatabaseInfo.UserDefined;
            this.AddModelClassToResolver(this.Extension.GetModelClassExtension());
        }

        public void ResetSelection()
        {
            this.ProtocolCompletion = ProtocolAutoCompletionType.None;
            this.CurrentSubstance = null;
            this.CurrentClassification = null;
            this.CurrentModelClass = null;
            this.CurrentSelectedName = null;
            this.CurrentSelectedDefaultName = null;
            this.CurrentSelectedLatinName = null;
            this.CurrentBatchNumber = null;
            this.CurrentSubstanceCategory = SubstanceCategory.None;
            this.NoSubstanceSelected?.Invoke(this, EventArgs.Empty);
        }

        public List<SubstanceInfo> GetAdditionalTestPdfCheckSubstances()
        {
            return this.SubstanceResolver.GetAdditionalTestPdfCheckSubstances();
        }

        public bool ContinueAction { get; set; } = true;

        public ModelClassInfo CurrentModelClass          {get; private set;}
        public ClassificationInfo CurrentClassification  {get; private set;}
        public SubstanceInfo CurrentSubstance            {get; private set;}
        public string CurrentSelectedName { get; private set; }
        public string CurrentSelectedDefaultName { get; private set; }
        public string CurrentSelectedLatinName { get; private set; }
        public string CurrentBatchNumber { get; private set; }
        public SubstanceCategory CurrentSubstanceCategory { get; private set; }

        public SubstanceExtension Extension {get; private set;}
        public SubstancePdfs Pdfs           {get; private set;}
        public IdentServerClient Server     {get; private set;}
        public Configurations Config        {get; private set;}
        public ProtocolAutoCompletionType ProtocolCompletion { get; private set; }
        public bool IsSubstanceReselected   { get; set; }



    }
}
