// 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 System.Linq;
using System.Threading.Tasks;
using Hiperscan.AnalysisModule;
using Hiperscan.AnalysisModule.Identification;
using Hiperscan.AnalysisModule.Remoting;
using Hiperscan.ApoIdentCore.Enums;
using Hiperscan.ApoIdentCore.GuiControl;
using Hiperscan.ApoIdentCore.Remoting;
using Hiperscan.ApoIdentCore.SectionManager.Substance;
using Hiperscan.ApoIdentCore.SectionManager.Substance.SubstanceSelection;
using Hiperscan.ApoIdentCommon.ApoConfig.ThirdPartyDataExchange;
using Hiperscan.GuiExtensions.Dialog;
using Hiperscan.Unix;

namespace Hiperscan.ApoIdentCore
{
    public partial class AppControl
    {
        private void InitSubstanceManagement()
        {
            if (this.SubstanceManager == null)
            {
                RemoteManager remote_manager = new RemoteManager(this.IdentModuleSettings,
                                                                 this.QuantifierModuleSettings,
                                                                 this.FileSystem);

                remote_manager.StateChanged += this.OnRemoteStateChanged;
                remote_manager.LoadingStarted += this.OnLoadingServerDataProgressed;
                remote_manager.LoadingProgressed += this.OnLoadingServerDataProgressed;
                remote_manager.LoadingFinished += this.OnLoadingServerDataFinished;

                this.SubstanceManager = new SubstanceManager(remote_manager,
                                                             this.FileSystem,
                                                              this.GlobalStates);
   
                this.SubstanceManager.SubstancesCatalog.BeginSubstanceSelection += this.OnBeginSubstanceSelection;
                this.SubstanceManager.SubstancesCatalog.SubstanceSelectionAborted += this.OnSubstanceSelectionAborted;
                this.SubstanceManager.SubstancesCatalog.SubstanceSelectionChanged += this.OnSubstanceSelectionChanged;

                this.SubstanceManager.SubstancesCatalog.ExchangeDataErrorDetected += this.OnExchangeDataErrorDetected;
                this.SubstanceManager.SubstancesCatalog.NoExchangeSubstansteIdentified += this.OnNoExchangeSubstanceIdentified;
                this.SubstanceManager.SubstancesCatalog.ExchangeFuzzySubstanceNameIdentified += this.OnExchangeSubstanceDetected;
                this.SubstanceManager.SubstancesCatalog.SeveralModelClassesDetected += this.OnSeveralModelClassesDetected;

                this.SubstanceManager.SubstancesCatalog.SubstanceSelectionStateChanged += this.OnSubstanceSelectionStateChanged;
                this.SubstanceManager.SubstancesCatalog.SubstanceMeasurableStateChanged += this.OnNirMeasurableStateChanged;
                this.SubstanceManager.BakMarking.ValueChanged += this.OnProtocolValueChanged;
            }   
        }

        public void SelectNewSubstance(string substance_name, bool from_match)
        {
            if (this.GuiSyncControl.IsSubstanceSectionUpdating)
                return;

            if (this.SubstanceManager.SubstancesCatalog.SelectionFromMatch == true)
                return;

            this.SubstanceManager.SubstancesCatalog.SelectionFromMatch = from_match;

            Task.Run(() =>
            {
                this.ProgramSync.EnterCriticialZone();
                this.SubstanceManager.SubstancesCatalog.SelectSubstance(substance_name);
                this.SubstanceManager.SubstancesCatalog.SelectionFromMatch = false;
                this.ProgramSync.LeaveCriticalZone();
            });
        }

        public void ImportExchangeData()
        {
            try
            {
                this.ProgramSync.EnterCriticialZone();

                if (this.ProtocolManager.CheckProtocolSaveState(this) == false)
                    return;

                this.ProfileManager.CurrentProfile.ExchangeData.ImportFiles();
                if (this.ProfileManager.CurrentProfile.ExchangeData.Count == 0)
                {
                    UpdateArgs arg = new UpdateArgs();
                    arg.DialogArgs.StandardDialogType = StandardDialogType.Info;
                    arg.DialogArgs.Message = String.Format(Catalog.GetString("There is no exchange data available."));
                    this.UpdateGuiNeeded?.Invoke(arg);
                    return;
                }

                if (this.ProfileManager.CurrentProfile.ExchangeData.Count > 1)
                {
                    UpdateArgs arg = new UpdateArgs();
                    arg.DialogArgs.ApoDialogType = ApoDialogType.DataExchangeImport;
                    this.UpdateGuiNeeded?.Invoke(arg);

                    if (arg.DialogArgs.Response != DialogRespnseType.Ok)
                        return;
                }
                else
                {
                    this.ProfileManager.CurrentProfile.ExchangeData.CurrentDataSetKey = this.ProfileManager.CurrentProfile.ExchangeData.First().Key;
                }

                string exchange_key = this.ProfileManager.CurrentProfile.ExchangeData.CurrentDataSetKey;
                ExchangeDataSet exchange_data_set = this.ProfileManager.CurrentProfile.ExchangeData[exchange_key];

                this.SubstanceManager.SubstancesCatalog.SelectSubstance(exchange_data_set);
            }
            catch (Exception ex)
            {
                UpdateArgs arg = new UpdateArgs();
                arg.DialogArgs.StandardDialogType = StandardDialogType.ErrorWithServiceMessage;
                arg.DialogArgs.Message = this.ProfileManager.CurrentProfile.ExchangeData.GetReadingErrorMessage(ex); ;
                this.UpdateGuiNeeded?.Invoke(arg);
            }
            finally
            {
                this.ProgramSync.LeaveCriticalZone();
            }
        }

        public void ClearSubstanceSelection()
        {
            this.ProgramSync.EnterCriticialZone();

            if (this.ProtocolManager.CheckProtocolSaveState(this))
            {
                this.ResetSubstanceSelection();
            }

            this.ProgramSync.LeaveCriticalZone();  
        }

        public void ChangeModelClass()
        {
            this.ProgramSync.EnterCriticialZone();

            this.SubstanceManager.SubstancesCatalog.ChangeModelClass();

            this.ProgramSync.LeaveCriticalZone();
        }

        public void ShowSubstanceClassificationInfo()
        {
            this.ProgramSync.EnterCriticialZone();

            UpdateArgs arg = new UpdateArgs();
            arg.DialogArgs.ApoDialogType = ApoDialogType.SubstanceClassificationInfo;
            this.UpdateGuiNeeded?.Invoke(arg);

            this.ProgramSync.LeaveCriticalZone();
        }

        public void ShowSubstanceValidation()
        {
            if (this.SubstanceManager.IsComplete == false)
                return;
           
            this.ProgramSync.EnterCriticialZone();

            try
            {
                AnalysisType analysis_type = this.SubstanceManager.SubstancesCatalog.AnalysisType;
                string anchor = String.Empty;
                if (analysis_type == AnalysisType.Identification)
                    anchor = "sect:classification" + this.SubstanceManager.SubstancesCatalog.Classification.Id;

                string model_class = this.SubstanceManager.SubstancesCatalog.ModelClass.Name;
                string pdf_file = this.FileSystem.GetValidationFile(analysis_type, model_class);

                Hiperscan.PdfLib.Pdf.Open(pdf_file,  anchor);
            }
            catch (Exception ex)
            {
                UpdateArgs arg = new UpdateArgs();
                arg.DialogArgs.StandardDialogType = StandardDialogType.ErrorWithServiceMessage;
                arg.DialogArgs.Exception = ex;
                arg.DialogArgs.Message = String.Format(Catalog.GetString("Cannot open validation report '{0}': {1}"),
                                               this.SubstanceManager.SubstancesCatalog.SelectedSubstanceName,
                                               ex.Message);

                this.UpdateGuiNeeded?.Invoke(arg);
            }

            this.ProgramSync.LeaveCriticalZone();
        }

        public void SetBakMarking(BAKMarkingState state)
        {
            if (this.GuiSyncControl.IsSubstanceSectionUpdating)
                return;

            this.ProfileManager.CurrentProfile.BakMarking.CurrentValue = state;
            this.SubstanceManager.BakMarking.Entry = state;
        }

        private void OnRemoteStateChanged(object sender, EventArgs e)
        {
            RemoteConnector connector = sender as RemoteConnector;
            Console.WriteLine(connector.State.Message);
        }

        private void OnLoadingServerDataProgressed(object sender, EventArgs e)
        {
            RemoteConnector connector = sender as RemoteConnector;

            UpdateArgs arg = new UpdateArgs();
            arg.StateBarValues = new StateBarValues();
            arg.StateBarValues.Progress = connector.LoadingProgressFraction;
            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnLoadingServerDataFinished(object sender, EventArgs e)
        {
            RemoteConnector connector = sender as RemoteConnector;

            UpdateArgs arg = new UpdateArgs();
            arg.StateBarValues = new StateBarValues();
            arg.StateBarValues.Progress = connector.LoadingProgressFraction;
            this.UpdateGuiNeeded?.Invoke(arg);

            System.Threading.Thread.Sleep(500);

            arg = new UpdateArgs();
            arg.StateBarValues = new StateBarValues();;
            arg.StateBarValues.ProgressVisible = false;
            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnBeginSubstanceSelection(object sender, EventArgs e)
        {
            SubstancesCatalog substance_catalog = sender as SubstancesCatalog;
            substance_catalog.ContinueAction = this.ProtocolManager.CheckProtocolSaveState(this);
        }

        private void OnSubstanceSelectionAborted(object sender, EventArgs e)
        {
            UpdateArgs arg = new UpdateArgs();
            arg.Substance = UpdateCategory.Reset;

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnSubstanceSelectionChanged(object sender, EventArgs e)
        {
            this.SpecimenManager.Reset();
            this.MeasuringManager.Reset();
            this.ResultManager.Reset();
            this.ProtocolManager.Reset();

            this.PharmacyManager.UpdateAfterSubstanceSelection(this.SubstanceManager.SubstancesCatalog);
            this.SubstanceManager.UpdateAfterSubstanceSelection(this.ProfileManager);
            this.SpecimenManager.UpdateAfterSubstanceSelection(this.SubstanceManager.SubstancesCatalog);
            this.MeasuringManager.UpdateAfterSubstanceSelection(this.SubstanceManager);
            this.ResultManager.UpdateAfterSubstanceSelection(this.SubstanceManager);
            this.ProtocolManager.UpdateAfterSubstanceSelection(this.SubstanceManager.SubstancesCatalog);

            if (this.SubstanceManager.SubstancesCatalog.IsSelectedSubstanceNirMeasurable == false)
                this.HandleNotMeasurable();
            else
                this.HandleMeasurable();
        }

        private void HandleNotMeasurable()
        {
            if (this.SubstanceManager.SubstancesCatalog.IsSelectedSubstanceNirMeasurable == true)
                return;

            UpdateArgs arg = UpdateArgs.GetResetArg();
            arg.DialogArgs.StandardDialogType = StandardDialogType.DontShowAgainInfo;
            arg.DialogArgs.Message = String.Format(Catalog.GetString("You have chosen a substance of the Classifier '{0}'. In this mode you can only create a report without measurement. To do so, please fill in the mandatory input fields for this substance."),
                                                   this.SubstanceManager.SubstancesCatalog.ModelClass.Name);
            arg.DialogArgs.DontShowAgain = this.ProfileManager.CurrentProfile.DontShowNotMeasurableDialogAgain;
            arg.AnimationType = ProgramState.NotMeasurable;

            this.UpdateGuiNeeded?.Invoke(arg);

            this.ProfileManager.CurrentProfile.DontShowNotMeasurableDialogAgain = arg.DialogArgs.DontShowAgain;

            arg = new UpdateArgs();
            arg.AnimationType = ProgramState.ReadyForProtocolInput;
            if (ProtocolManager.IsComplete)
                arg.AnimationType = ProgramState.ReadyForProtocolCreation;
            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void HandleMeasurable()
        {
            if (this.SubstanceManager.SubstancesCatalog.IsSelectedSubstanceNirMeasurable == false)
                return;

            UpdateArgs arg = UpdateArgs.GetResetArg();
            arg.AnimationType = this.MeasuringManager.GetMeasuringState();

            if (String.IsNullOrEmpty(this.SubstanceManager.SubstancesCatalog.Substance.AdditionalSubstanceInfo) == false)
            {
                arg.DialogArgs.StandardDialogType = StandardDialogType.Warning;
                arg.DialogArgs.Message = this.SubstanceManager.SubstancesCatalog.Substance.AdditionalSubstanceInfo;
            }

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnExchangeDataErrorDetected(string message)
        {
            UpdateArgs arg = new UpdateArgs();
            arg.DialogArgs.StandardDialogType = StandardDialogType.ErrorWithServiceMessage;
            arg.DialogArgs.Message = message;

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnNoExchangeSubstanceIdentified(string message)
        {
            UpdateArgs arg = new UpdateArgs();
            arg.DialogArgs.StandardDialogType = StandardDialogType.ErrorWithServiceMessage;
            arg.DialogArgs.Message = message;

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnExchangeSubstanceDetected(object sender, EventArgs e)
        {
            UpdateArgs arg = new UpdateArgs();
            arg.DialogArgs.ApoDialogType = ApoDialogType.DataExchangeSelectSubstance;

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void OnSeveralModelClassesDetected(object sender, EventArgs e)
        {
            UpdateArgs arg = new UpdateArgs();
            arg.DialogArgs.ApoDialogType = ApoDialogType.SelectModelClass;

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        private void ResetSubstanceSelection()
        {
            this.PharmacyManager.Reset();
            this.SpecimenManager.Reset();
            this.MeasuringManager.Reset();
            this.ResultManager.Reset();
            this.ProtocolManager.Reset();
            this.SubstanceManager.Reset();

            this.SetReadyForSubstanceInput();
        }

        private void SetReadyForSubstanceInput()
        {


            if (this.GlobalStates.ConnectionSuccess)
            {
                UpdateArgs arg = UpdateArgs.GetResetArg();
                arg.AnimationType = ProgramState.ReadyForSubstanceInput;
                this.UpdateGuiNeeded?.Invoke(arg);
                return;
            }

            if (this.GlobalStates.IsOnlyQuantifierModuleAvailable)
                return;

       
            if (this.ProfileManager?.CurrentProfile?.HasVisibleSubstances ?? false)
            {
                UpdateArgs arg = UpdateArgs.GetResetArg();
                arg.AnimationType = ProgramState.ReadyForSubstanceInput;
                this.UpdateGuiNeeded?.Invoke(arg);
                return;
            }

            if (this.GlobalStates.HasNotMeasurableData)
            {
                UpdateArgs arg = UpdateArgs.GetResetArg();
                arg.AnimationType = ProgramState.NoSubstances;
                this.UpdateGuiNeeded?.Invoke(arg);
            }
        }

        public SubstanceManager SubstanceManager { get; private set; }
    }
}
