// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2021 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 Gtk;
using Hiperscan.Extensions.ApoIdent.Widgets;
using Hiperscan.Spectroscopy.Method;
using Hiperscan.Unix;

//TODO setting flags for protocol section
//TODO remind save

namespace Hiperscan.Extensions.ApoIdent
{
    public partial class ApoIdentApplication
    {
        private void InitSubstanceManagement()
        {
            this.SubstanceManagement = new SubstanceManagement(this.Config, this.IdentServerClient);

            this.SubstanceManagement.BeforeSubstanceSelectionChanged += this.OnBeforeSubstanceSelectionChanged;
            this.SubstanceManagement.AfterSubstanceSelectionChanged += this.OnAfterSubstanceSelectionChanged;
            this.SubstanceManagement.SubstanceSelectionNotChanged += this.OnSubstanceSelectionNotChanged;
            this.SubstanceManagement.NoSubstanceSelected += this.OnNoSubstanceSelected;

            this.SubstanceManagement.BeginLoadingData += this.OnBeginLoadingData;
            this.SubstanceManagement.LoadingDataProgressed += this.OnLoadingDatatProgressed;

            this.SubstanceManagement.ModelClassHandlingRequested += this.OnModelClassHandlingRequested;

            this.SubstanceManagement.Pdfs.LoadingValidationDocuStarted += this.OnPdfDataChanged;
            this.SubstanceManagement.Pdfs.LoadingValidationDocuFinished += this.OnPdfDataChanged;
            this.SubstanceManagement.Pdfs.LoadingSubstancuDocuStarted += this.OnPdfDataChanged;
            this.SubstanceManagement.Pdfs.LoadingSubstanceDocuFinished += this.OnPdfDataChanged;
            this.SubstanceManagement.Pdfs.LoadingAdditionalTestsStarted += this.OnPdfDataChanged;
            this.SubstanceManagement.Pdfs.LoadingAdditionalTestsFinished += this.OnPdfDataChanged;
        }

        private bool MatchSubstance(EntryCompletion completion, string key, TreeIter iter)
        {
            if (key == " ")
                return true;

            key = key.ToLower().Trim();
            key = key.Replace(",", String.Empty);
            key = key.Replace(";", String.Empty);
            key = key.Replace("-", String.Empty);
            key = key.Replace(".", String.Empty);
            key = key.Replace("\u0061\u0308", "\u00e4");
            key = key.Replace("\u006f\u0308", "\u00f6");
            key = key.Replace("\u0075\u0308", "\u00fc");

            TreeModel store = completion.Model;
            string line = (((string)store.GetValue(iter, 1)) ?? String.Empty).Trim().ToLower();
            line = line.Replace(",", String.Empty);
            line = line.Replace(";", String.Empty);
            line = line.Replace("-", String.Empty);
            line = line.Replace(".", String.Empty);
            line = line.Replace("ß", "ss");

            if (key.Length < 4)
            {
                if (line.StartsWith(key, StringComparison.InvariantCulture) == true)
                    return true;

                string[] line_data = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                int count = 0;
                foreach (string sub_line in line_data)
                {
                    if (sub_line.StartsWith(key, StringComparison.InvariantCulture) == true)
                        count++;
                }
                if (count == line_data.Length)
                    return true;
            }
            else
            {
                if (line.Contains(key) == true)
                    return true;
            }
            return false;
        }

        private void OnBeginLoadingData(object sender, EventArgs e)
        {
            this.Update(ApoIdentApplicationState.ConnectionBeginLoadingData);
        }

        private void OnLoadingDatatProgressed(object sender, EventArgs e)
        {
            double fraction = (double)(e as DataEventArgs).Data[DataEventArgs.PROGRESS];
            this.Update(ApoIdentApplicationState.ConnectionLoadingDatatProgressed, fraction);
        }


        [GLib.ConnectBefore]
        private void OnSubstanceMatchSelected(object sender, MatchSelectedArgs e)
        {
            if (this.Control.IsGuiUpdating)
                return;

            lock (this.Control.Transaction)
            {
                string text = (string)e.Model.GetValue(e.Iter, 1);
                this.substance_search_entry.Text = this.SubstanceManagement.CurrentSelectedName ?? String.Empty;
                this.SubstanceManagement.SelectNewSubstance((text ?? String.Empty).Trim(),
                                                            ProtocolAutoCompletionType.ApoIdent);

                e.RetVal = this.SubstanceManagement.ContinueAction == false;
                this.SubstanceManagement.ContinueAction = true;
            }
        }

        private void OnSubstanceSearchEntryActivated(object sender, EventArgs e)   
        {
            if (this.Control.IsGuiUpdating)
                return;

            lock (this.Control.Transaction)
            {
                this.SubstanceManagement.SelectNewSubstance((sender as Gtk.Entry).Text.Trim(),
                                                            ProtocolAutoCompletionType.ApoIdent);
            }
        }

        private void OnSubstanceSearchEntryFocusOut(object sender, Gtk.FocusOutEventArgs e)
        {
            if (this.Control.IsGuiUpdating)
                return;

            if (this.Control.IsDisconnecting)
                return;

            lock (this.Control.Transaction)
            {
                this.SubstanceManagement.SelectNewSubstance((sender as Gtk.Entry).Text.Trim(),
                                                            ProtocolAutoCompletionType.None);
            }
        }

        private void OnSubstanceSelectionNotChanged(object sender, EventArgs e)
        {
            this.Update(ApoIdentApplicationState.SubstanceManagementSubstanceSelectionNotChanged);
        }

        private void OnNoSubstanceSelected(object sender, EventArgs e)
        {
            this.Control.SetBeforeMeasurement();
            this.Control.SubstanceSelected = false;
            if (this.Config.Profiles.Current.AdditionalCompletionEntries !=null)
                this.Config.Profiles.Current.AdditionalCompletionEntries.Reset();

            this.Finder.IsInsertActive = false;
            this.Finder.ResetRecalibrationTimestamp();
            this.Finder.MeasuringState = this.Finder.GetMeasuringState(this.SubstanceManagement);

            this.Update(ApoIdentApplicationState.SubstanceManagementSubstanceNoSubstanceSelected);
            this.PrepareProtocolSection();
        }

        private void OnModelClassButtonClicked(object sender, EventArgs e)
        {
            lock(this.Control.Transaction)
            {
                this.SubstanceManagement.CreateSubstanceRequest();
                this.Update(ApoIdentApplicationState.SubstanceManagementModelClassDialog);
                this.SubstanceManagement.UpdateAfterSubstanceRequest();
            }
        }

        private void OnBeforeSubstanceSelectionChanged(object sender, EventArgs e)
        {
            if (this.CheckForNotSavedProtocolDetails() == false) 
            {
                this.SubstanceManagement.ContinueAction = false;
                this.Update(ApoIdentApplicationState.SubstanceManagementSubstanceSelectionNotChanged);
            }
        }

        private void OnModelClassHandlingRequested(object sender, EventArgs e)
        {
            this.Update(ApoIdentApplicationState.SubstanceManagementModelClassDialog);
            this.SubstanceManagement.UpdateAfterSubstanceRequest();
        }

        private void OnAfterSubstanceSelectionChanged(object sender, EventArgs e)
        {
            this.Control.SetBeforeMeasurement();
            this.Control.SubstanceSelected = true;
            this.Config.Profiles.Current.AdditionalCompletionEntries.Reset();

            this.IdentServerClient.UpdateAuthentificationSet(this.SubstanceManagement);
            this.Finder.IsInsertActive = false;

            if (this.SubstanceManagement.CurrentModelClass.SubstanceType == SubstanceType.Fluid)
                this.Finder.ResetRecalibrationTimestamp();

            this.Finder.MeasuringState = this.Finder.GetMeasuringState(this.SubstanceManagement);
            this.Update(ApoIdentApplicationState.SubstanceManagementSubstanceAfterSelectionChanged);
            this.PrepareProtocolSection();

            if (this.SubstanceManagement.CurrentClassification.IsNirMeasurable == false)
            {
                this.Control.SetNotMeasurable();
                this.CheckAdditionalTestAsMandatory();
                string message = String.Format(Catalog.GetString("You have chosen a substance of the classifier '{0}'. In this mode you can create only a protocol without NIRS-measuring. Please complete the mandatory fields for this substance."),
                                           this.SubstanceManagement.CurrentModelClass.ModelClassName);
                    
                this.Update(ApoIdentApplicationState.SubstanceManagementNotMeasurableDialog, message);

                this.Update(ApoIdentApplicationState.MeasuringSuccess);
                return;
            }

            if (String.IsNullOrEmpty(this.SubstanceManagement.CurrentSubstance.AdditionalSubstanceInfo) == false)
            {
                string message = this.SubstanceManagement.CurrentSubstance.AdditionalSubstanceInfo;
                this.Update(ApoIdentApplicationState.SubstanceManagementAdditionalTextDialog, message);
            }
        }

        private void OnSubstanceInfoButtonClicked(object sender, EventArgs e)
        {
            lock (this.Control.Transaction)
            {
                this.Update(ApoIdentApplicationState.SubstanceManagementInfoButtonClassificationDialog);
            }
        }

        private void OnReadExchangeDataButtonClicked(object sender, EventArgs e)
        {
            lock (this.Control.Transaction)
            {
                if (this.CheckForNotSavedProtocolDetails() == false)
                    return;

                this.Config.Profiles.Current.ExchangeData.DeterminedFuzzySet = null;
                string message = null;

                try
                {
                    this.Config.Profiles.Current.ExchangeData.ImportFiles();
                    if (this.Config.Profiles.Current.ExchangeData.Count == 0)
                    {
                        message = String.Format(Catalog.GetString("There are no exchange data available."));
                        this.Update(ApoIdentApplicationState.SubstanceManagementExchangeNoDataDialog, message);
                        return;
                    }

                    if (this.Config.Profiles.Current.ExchangeData.Count > 1)
                    {
                        this.Update(ApoIdentApplicationState.SubstanceManagementExchangeSelectFileDialog);
                        if (this.DialogResponse != ResponseType.Ok)
                            return;
                    }
                    else
                    {
                        this.Config.Profiles.Current.ExchangeData.CurrentDataSetKey = this.Config.Profiles.Current.ExchangeData.First().Key;
                    }

                    string exchange_key = this.Config.Profiles.Current.ExchangeData.CurrentDataSetKey;
                    ExchangeDataSet exchange_data_set = this.Config.Profiles.Current.ExchangeData[exchange_key];

                    List<FuzzyNameSet> fuzzy_sets = this.SubstanceManagement.FindSubstanceForDataExchange(exchange_data_set);

                    if (fuzzy_sets.Count == 1 && fuzzy_sets[0].ResolvedOverId == true)
                    {
                        this.Config.Profiles.Current.ExchangeData.DeterminedFuzzySet = fuzzy_sets[0];
                    }
                    else if (fuzzy_sets.Count == 0)
                    {
                        message = String.Format(Catalog.GetString("Could not find or resolve substance name '{0}'. " +
                                                                   "If you want to delete this import data, you can do this over the '{1}'-application."),
                                                 exchange_data_set.PrimarySubstanceName,
                                                 this.Config.Profiles.Current.ExchangeData.ApplicationName);

                        this.Update(ApoIdentApplicationState.SubstanceManagementExchangeNoDataDialog, message);

                    }
                    else
                    {
                        this.Config.Profiles.Current.ExchangeData.FuzyList = fuzzy_sets;
                        this.Update(ApoIdentApplicationState.SubstanceManagementExchangeSelectSubstanceDialog);
                    }

                    if (this.Config.Profiles.Current.ExchangeData.DeterminedFuzzySet != null)
                        this.SubstanceManagement.SelectNewSubstance(this.Config.Profiles.Current.ExchangeData.DeterminedFuzzySet);
                }
                catch (Exception ex)
                {
                    message = this.Config.Profiles.Current.ExchangeData.GetReadingErrorMessage(ex);
                    this.Update(ApoIdentApplicationState.ShowErrorDialogWithServiceDialog, message, ex);
                }
            }
        }

        public void OnValidationReportButtonClicked(object sender, EventArgs e)
        {
            lock (this.Control.Transaction)
            {
                if (this.Control.SubstanceSelected == false)
                    return;

                if (this.Control.CreateNotMeasurableReport == true)
                {
                    string message = String.Format(Catalog.GetString("Validation is not available when a NIR inactive substance ist selected."));
                    this.Update(ApoIdentApplicationState.ShowErrorDialogWithoutSerivceDialog, message);
                    return;
                }

                if (this.SubstanceManagement.CurrentModelClass.ValidationDocument == false)
                {
                    string message = String.Format(Catalog.GetString("Validation is not available for this substance class."));
                    this.Update(ApoIdentApplicationState.ShowErrorDialogWithoutSerivceDialog, message);
                    return;
                }

                if (this.Config.IdentModuleSettings.TestVersion == true)
                {
                    string message = String.Format(Catalog.GetString("Validation is not available for Test-IdentModule."));
                    this.Update(ApoIdentApplicationState.ShowErrorDialogWithoutSerivceDialog, message);
                    return;
                }

                try
                {
                    string classification_id = this.SubstanceManagement.CurrentClassification.Id;
                    string class_pdf_file = FilesAndDirs.ValidationFile(this.SubstanceManagement.CurrentModelClass.ModelClassName);
                    Hiperscan.PdfLib.Pdf.Open(class_pdf_file, "sect:classification" + classification_id);
                }
                catch (Exception ex)
                {
                    string message = String.Format(Catalog.GetString("Cannot open validation document '{0}': {1}"),
                                                   this.SubstanceManagement.CurrentSelectedName,
                                                   ex.Message);

                    this.Update(ApoIdentApplicationState.ShowErrorDialogWithoutSerivceDialog, message, ex);
                }
            }
        }

        public SubstanceManagement SubstanceManagement {get; private set;}
    }
}
