// 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.Threading;
using Gtk;

namespace Hiperscan.Extensions.ApoIdent
{
    public partial class ApoIdentApplication
    {
        public void Update(ApoIdentApplicationState state)
        {
            this.__Update(state);
        }

        public void Update(ApoIdentApplicationState state, string message)
        {
            this.__Update(state, message, null, null);
        }

        public void Update(ApoIdentApplicationState state, string message, Exception ex)
        {
            this.__Update(state, message, ex, null);
        }

        public void Update(ApoIdentApplicationState state, string message, Exception ex, string title, EventHandler handler = null)
        {
            this.__Update(state, message, ex, title, handler);
        }

        public void Update(ApoIdentApplicationState state, double fraction)
        {
            this.__Update(state, fraction);
        }

        public void Update(ApoIdentApplicationState state, double fraction, double temperature, double nominal_temperature)
        {
            this.__Update(state, fraction, temperature, nominal_temperature);
        }

        private void __Update(ApoIdentApplicationState state, params object[] data)
        {
            Guid guid = Guid.NewGuid();

            switch(state)
            {
                case ApoIdentApplicationState.MeasuringTimeLeft:
                case ApoIdentApplicationState.MeasuringEndInternalCaibration:
                case ApoIdentApplicationState.DeviceStateChanged:
                    guid = Guid.Empty;
                    break;
                default:
                    break;
            }

            this.Control.EnterGui(guid.ToString());

            List<object> parameters = new List<object>();
            parameters.Add(state);
            parameters.Add(guid);

            if (data != null && data.Length > 0)
                parameters.AddRange(data);

            if (this.Control.MainThreadId == Thread.CurrentThread.ManagedThreadId)
                this.__UpdateThreadSave(parameters, EventArgs.Empty);
            else
                Application.Invoke(parameters, EventArgs.Empty, this.__UpdateThreadSave);

            this.Control.WaitForGuiUpdateFinished(guid.ToString());
        }

        private void __UpdateThreadSave(object sender, EventArgs e)
        {
            List<object> parameters = (List<object>)sender;

            ApoIdentApplicationState state = (ApoIdentApplicationState) parameters[0];
            Guid guid = (Guid)parameters[1];

            switch (state)
            {
                case ApoIdentApplicationState.ShowErrorDialogWithServiceDialog:
                case ApoIdentApplicationState.ShowErrorDialogWithoutSerivceDialog:
                    this.__UpdateMessageDialog(state, (string)parameters[2], (Exception)parameters[3]);
                    break;

                case ApoIdentApplicationState.ConfigBeginProfileChanged:
                    this.UpdateCursor();
                    break;

                case ApoIdentApplicationState.ConfigProfileChanged:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(true);
                    this.UpdateSubstanceSection(true, true);
                    this.UpdateSpecimenSection(true);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    break;

                case ApoIdentApplicationState.ConfigProfileSettingsChanged:
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(false, true);
                    this.UpdateSpecimenSection(true);
                    this.UpdateResultSection();
                    break;

                case ApoIdentApplicationState.ProgramStart:
                    this.UpdateHelpSection();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(true);
                    this.UpdateSubstanceSection(true, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, String.Empty, false, 0);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.ConnectionStart:
                    this.UpdateHelpSection();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(true);
                    this.UpdateSubstanceSection(true, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, String.Empty, false, 0);
                    this.__UpdateAnimationSection(state);
                    this.SplashScreen.StartApoIdentPhase();
                    break;

                case ApoIdentApplicationState.ConnectionStartIdentServerFailed:
                case ApoIdentApplicationState.ConnectionIdentServerVersionRefused:
                case ApoIdentApplicationState.ConnectionIdentServerRefused:
                case ApoIdentApplicationState.ConnectionCheckIdentModuleFailed:
                case ApoIdentApplicationState.ConnectionInitDeviceFailed:
                case ApoIdentApplicationState.ConnnectionDeviceRegistrationFailed:
                case ApoIdentApplicationState.ConnectionInitFinderFailed:
                case ApoIdentApplicationState.ConnectionLoadDataFailed:
                case ApoIdentApplicationState.CoonectDeviceWarmupFailed:
                case ApoIdentApplicationState.ConnectionUnknownError:
                    this.UpdateCursor();
                    this.SplashScreen.StopInitPhase(false);
                    this.UpdateHelpSection();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(true);
                    this.UpdateSubstanceSection(true, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, String.Empty, false, 0);
                    this.__UpdateAnimationSection(state, (string)parameters[2]);
                    this.__UpdateMessageDialog(state, (string)parameters[2], (Exception)parameters[3]);
                    break;

                case ApoIdentApplicationState.ConnectionLost:
                    this.UpdateCursor();
                    this.UpdateHelpSection();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false); 
                    this.UpdateSubstanceSection(true, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, String.Empty, false, 0);
                    this.__UpdateAnimationSection(state, (string)parameters[2]);
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.ConnectionWarmupEnd:
                    this.UpdateCursor();
                    this.SplashScreen.StopWarmingUp();
                    this.UpdateHelpSection();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(true);
                    this.UpdateSubstanceSection(true, true);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, null, false, 0);
                    this.__UpdateAnimationSection(state);
                    break;


                case ApoIdentApplicationState.ConnectionBeginLoadingData:
                    this.UpdateStateSection(null, null, true, 0);
                    break;

                case ApoIdentApplicationState.ConnectionLoadingDatatProgressed:
                    this.UpdateStateSection(null, null, null, (double)parameters[2]);
                    break;

                case ApoIdentApplicationState.ConnectionWarmupStart:
                    this.SplashScreen.StopInitPhase(true);
                    this.UpdateStateSection(null, null, true, 0);
                    break;

                case ApoIdentApplicationState.ConnectionWarmupProgress:
                    this.SplashScreen.UpdateWarmup((double)parameters[3], (double)parameters[4]);
                    this.UpdateStateSection(null, null, true, (double)parameters[2]);
                    break;

                case ApoIdentApplicationState.ConnectDeviceWarmupInterrupted:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.ConnectionRemindOfUpdate:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.SubstanceManagementSubstanceSelectionNotChanged:
                    this.UpdateSubstanceSection(false, false);
                    break;

                case ApoIdentApplicationState.MeasuringFailedPostProcessing:
                    this.UpdateMenueSection();
                    this.UpdateSubstanceSection(false, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.SubstanceManagementSubstanceAfterSelectionChanged:
                case ApoIdentApplicationState.SubstanceManagementSubstanceNoSubstanceSelected:
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(false, false);
                    this.UpdateSpecimenSection(true);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.SubstanceManagementModelClassDialog:
                case ApoIdentApplicationState.SubstanceManagementInfoButtonClassificationDialog:
                case ApoIdentApplicationState.SubstanceManagementExchangeSelectFileDialog:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.SubstanceManagementNotMeasurableDialog:
                case ApoIdentApplicationState.SubstanceManagementAdditionalTextDialog:
                case ApoIdentApplicationState.SubstanceManagementExchangeNoDataDialog:
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.SubstanceManagementPdfLoading:
                    this.UpdateHelpSection();
                    break;

                case ApoIdentApplicationState.SubstanceManagementReinit:
                    this.UpdateSubstanceSection(true, false);
                    break;

                case ApoIdentApplicationState.SubstanceManagementExchangeSelectSubstanceDialog:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.MeasuringStart:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(true, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, null, false, 0);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.MeasuringStateChanged:
                    this.UpdateMenueSection();
                    this.UpdateMeasuringSection();
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.MeasuringWaitForNextCycle:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateMeasuringSection();
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.MeasuringRunCycle:
                    this.UpdateCursor();
                    this.UpdateMeasuringSection();
                    this.__UpdateAnimationSection(state);
                    break;



                case ApoIdentApplicationState.MeasuringSuccess:
                case ApoIdentApplicationState.MeasuringForFirstFluid:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(false, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, null, false, 0);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.MeasuringFailed:
                case ApoIdentApplicationState.MeasuringTransflectanceFailed:
                case ApoIdentApplicationState.MeasuringBlackFailed:
                case ApoIdentApplicationState.MeasuringWhiteFailed:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(false, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection(null, null, false, 0);
                    this.__UpdateAnimationSection(state, (string)parameters[2]);
                    this.__UpdateMessageDialog(state, (string)parameters[2], (Exception)parameters[3], (string)parameters[4], (EventHandler)parameters[5]);
                    break;

                case ApoIdentApplicationState.MeasuringInterruptedStart:
                    this.UpdateCursor();
                    this.UpdateMeasuringSection();
                    break;

                case ApoIdentApplicationState.MeasuringInterruptedEnd:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(false, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.__UpdateAnimationSection(state, (string)parameters[2]);
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;


                case ApoIdentApplicationState.MeasuringReferencesReset:
                    this.UpdateMenueSection();
                    this.UpdateMeasuringSection();
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.MeasuringEndInternalCaibration:
                    this.UpdateMenueSection();
                    this.UpdateMeasuringSection();
                    break;

                case ApoIdentApplicationState.MeasuringStartInternalCaibration:
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.MeasuringTimeLeft:
                    this.UpdateMeasuringSection((string)parameters[2]);
                    break;


                case ApoIdentApplicationState.MeasuringResultCheck:
                case ApoIdentApplicationState.MeasuringResultCheckRepeated:
                    this.__UpdateAnimationSection(state, (string)parameters[2]);
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.ResultAdditionalTestsWindowStart:
                case ApoIdentApplicationState.ResultShowPlots:
                case ApoIdentApplicationState.ResultShowValuationList:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.ResultPreSuggestion:
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.ResultAdditionalTestsWindowClosed:
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(true);
                    this.__UpdateAnimationSection(state);
                    break;


                case ApoIdentApplicationState.ProtocolDataChanged:
                    this.UpdateProtocolSection(true);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.ProtocolBatchDeadLineChanged:
                    this.UpdateSpecimenSection(false);
                    this.UpdateProtocolSection(true);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.ProtocolCheckSave:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.ProtocolSavingFailed:
                    this.__UpdateAnimationSection(state);
                    this.__UpdateMessageDialog(state, (string)parameters[2], (Exception) parameters[3]);
                    break;

                case ApoIdentApplicationState.ProtocolSavingSuccess:
                    this.UpdateSubstanceSection(false, true);
                    this.UpdateProtocolSection(true);
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.ProtocolShowingFailed:
                case ApoIdentApplicationState.ProtocolPrintingFailed:
                case ApoIdentApplicationState.ProtocolLabelPrintingFailed:
                    this.__UpdateAnimationSection(state, (string)parameters[2]);
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.ProtocolDataExchangeOk:
                    this.UpdateSubstanceSection(false,true);
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.ProtocolDataExchangeFailed:
                    this.UpdateSubstanceSection(false, false);
                    this.__UpdateMessageDialog(state, (string)parameters[2], (Exception)parameters[3]);
                    break;


                case ApoIdentApplicationState.MainWindowFocus:
                    this.UpdateMeasuringSection();
                    break;

                case ApoIdentApplicationState.CheckSettings:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.ImportantCustomerInfo:
                    this.__UpdateMessageDialog(state, (string)parameters[2]);
                    break;

                case ApoIdentApplicationState.DeviceStateChanged:
                    this.UpdateStateSection((string)parameters[2]);
                    break;

                case ApoIdentApplicationState.DeviceVersionDetected:
                    this.UpdateStateSection(null, (string)parameters[2], false, 0);
                    break;

                case ApoIdentApplicationState.DeviceAverageChanged:
                    this.Control.Host.UpdateProperties();
                    break;

                case ApoIdentApplicationState.Exit:
                    this.UpdateCursor();
                    this.UpdateMenueSection();
                    this.UpdateTestSection(false);
                    this.UpdateSubstanceSection(false, false);
                    this.UpdateSpecimenSection(false);
                    this.UpdateMeasuringSection();
                    this.UpdateResultSection();
                    this.UpdateProtocolSection(false);
                    this.UpdateStateSection();
                    this.__UpdateAnimationSection(state);
                    break;

                case ApoIdentApplicationState.PreferencesDialog:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.QueryDialog:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                case ApoIdentApplicationState.SubstancesDialog:
                    this.__UpdateMessageDialog(state, String.Empty);
                    break;

                default:
                    break;
            }

            this.Control.LeaveGui(guid.ToString());
        }


        private void UpdateCursor()
        {
            if (this.Control.Busy)
            {
                if (this.MainWindow.IsRealized == true && this.MainWindow.GdkWindow != null)
                    this.MainWindow.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);
            }
            else
            {
                if (this.MainWindow.IsRealized == true && this.MainWindow.GdkWindow != null)
                    this.MainWindow.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
            }
        }

        private object SyncGui { get; set; } = new object();

        public AppControl Control { get; private set; }
    }
}
