// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2024. 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.Text.RegularExpressions;

using Hiperscan.Unix;
using Hiperscan.Spectroscopy;
using Hiperscan.GuiExtensions;
using Hiperscan.Extensions.ReferenceCheck.Control.Enums;
using Hiperscan.Extensions.ReferenceCheck.Control.GuiUpdate;

namespace Hiperscan.Extensions.ReferenceCheck.Control.Acquisition
{
    public abstract class AcquisitionSet
    {
        public event UpdateGuiHandler UpdateGuiNeeded = null;

        public AcquisitionSet(FinderButtonState finder_button_state, AcquisitionSessions sessions)
        {
            this.FinderButtonState = finder_button_state;
            this.Sessions = sessions;
            this.Init();
        }

        private void Init()
        {
            this.SetProperties();
        }

        protected abstract void SetProperties();

        public void InitNewSession()
        {
            this.NoUpdateFinderButton = true;
            this.Sessions.GetNextSession();
            this.SetReadyForAcquisition();
            this.NoUpdateFinderButton = false;
        }

        public void SetReadyForAcquisition()
        {
            if (this.Sessions.IsCurrentSessionComplete)
                return;

            if (this.AcquisitionState.HasFlag(AcquisitionState.IsAcquiring))
                return;

            this.AcquisitionState = AcquisitionState.ReadyForSerialInput;

            this.UpdateGui(UpdateCategory.All);
            this.SetSerial(this.Serial);
        }

        public virtual void SetSerial(string serial)
        {
            this.Serial = serial ?? String.Empty;

            if (this.IsSerialValid)
                this.AcquisitionState |= AcquisitionState.SerialInputIsComplete;
            else
                this.AcquisitionState &= ~AcquisitionState.SerialInputIsComplete;

            if (this.NoUpdateFinderButton == false)
                this.FinderButtonState.CanAcuireSpectrum.IsActive = this.IsSerialValid;

            this.UpdateGui(UpdateCategory.Sensitives | UpdateCategory.QuickDataChange);
        }

        public void SetAcquisitionStart()
        {
            this.FinderButtonState.CanAcuireSpectrum.IsActive = false;
            this.AcquisitionState &= ~AcquisitionState.AcquiringFailed;
            this.AcquisitionState |= AcquisitionState.IsAcquiring;
            this.UpdateGui(UpdateCategory.All);
        }

        public void SetAcquisitionFinished(Spectrum spectrum)
        {
            this.Sessions.AddSpectrum(this.Serial, spectrum);
            this.AcquisitionState = AcquisitionState.AcuiringIsFinished;
            this.UpdateGui(UpdateCategory.All);
            this.Serial = String.Empty;

            if (this.Sessions.IsCurrentSessionComplete == false)
                this.SetReadyForAcquisition();
        }

        public void SetAcquisitionFailed()
        {
            this.FinderButtonState.CanAcuireSpectrum.IsActive = true;
            this.AcquisitionState = AcquisitionState.AcquiringFailed;
            this.UpdateGui(UpdateCategory.All);

            this.SetReadyForAcquisition();
        }

        private void UpdateGui(UpdateCategory category)
        {
            UpdateArgs arg = new UpdateArgs();
            arg.ViewType = ViewTypes.Acquisition;
            arg.UpdateCatecory = category;

            if (this.IsAcquiring)
                arg.CursorState = CursorState.Busy;
            
            if (this.AcquisitionIsFinished || this.AcquisitionFailed)
                arg.CursorState = CursorState.Normal;

            this.UpdateGuiNeeded?.Invoke(arg);
        }

        public static string GetTitle(AcquisitionType type)
        {
            string title = String.Empty;

            switch (type)
            {
                case AcquisitionType.Black:
                    title = Catalog.GetString("Acquisition Black Reference");
                    break;

                case AcquisitionType.White:
                    title = Catalog.GetString("Acquisition White Reference");
                    break;

                case AcquisitionType.Transflectance:
                    title = Catalog.GetString("Acquisition Transflectance Insert Reference");
                    break;

                case AcquisitionType.WhiteInsert:
                    title = Catalog.GetString("Acquisition White Reference for measuring small Quantities");
                    break;

                case AcquisitionType.Empty:
                    title = Catalog.GetString("Acquisition Empty Reference");
                    break;

                default:
                    break;
            }

            return title;
        }

        public static string GetAdvice(AcquisitionType type, bool next)
        {
            string advice = String.Empty;

            switch (type)
            {
                case AcquisitionType.Black:
                    advice = String.Format(Catalog.GetString("Please apply the {0} reference."),
                                           next ? Catalog.GetString("next black") : Catalog.GetString("black"));
                    break;

                case AcquisitionType.White:
                    advice = String.Format(Catalog.GetString("Please apply the {0} reference."),
                                           next ? Catalog.GetString("next white") : Catalog.GetString("white"));
                    break;

                case AcquisitionType.Transflectance:
                    advice = advice = String.Format(Catalog.GetString("Please apply the {0} reference."),
                                           next ? Catalog.GetString("next Transflectance Insert") : Catalog.GetString("Transflectance Insert"));
                    break;

                case AcquisitionType.WhiteInsert:
                    advice = advice = String.Format(Catalog.GetString("Please apply the {0} reference."),
                                           next ? Catalog.GetString("next White reference for measuring small quantities") : Catalog.GetString("White reference for measuring small quantities"));
                    break;

                case AcquisitionType.Empty:
                    advice = advice = String.Format(Catalog.GetString("Please apply the {0} reference."),
                                           next ? Catalog.GetString("next empty") : Catalog.GetString("empty"));
                    break;

                default:
                    break;
            }

            return advice;
        }

        public bool SerialExists
        {
            get
            {
                return this.Sessions.SerialExists(this.Serial);
            }
        }

        public bool IsSerialValid
        {
            get
            {
                return this.Regex.Match(this.Serial).Success;
            }
        }

        public string SessionOutput
        {
            get
            {
                int count = this.Sessions.CurrentSpectraCount + 1;
                if (count > this.Sessions.CurrentMaxAcquisitionCount)
                    count = this.Sessions.CurrentMaxAcquisitionCount;

                return String.Format(Catalog.GetString("{0:d2} ({1} of {2} measurements)"),
                                     this.Sessions.CurrentSessionId,
                                     count,
                                     this.Sessions.CurrentMaxAcquisitionCount);
            }
        }

        public bool AllowSerialInput
        {
            get
            {
                if (this.AcquisitionType == AcquisitionType.Empty)
                    return false;

                if (this.AcquisitionType == AcquisitionType.Transflectance)
                    return false;

                return true;
            }
        }

        public bool IsWaitingForInput
        {
            get
            {
                bool is_waiting = this.AcquisitionState.HasFlag(AcquisitionState.ReadyForSerialInput);
                if (this.IsAcquiring)
                    is_waiting = false;

                return is_waiting;
            }
        }

        public bool CanAcquire
        {
            get
            {
                bool can_acquire = this.AcquisitionState.HasFlag(AcquisitionState.SerialInputIsComplete);
                if (this.IsAcquiring)
                    can_acquire = false;

                return can_acquire;
            }
        }


        public bool AcquisitionIsFinished
        {
            get
            {
                bool can_acquire = this.AcquisitionState.HasFlag(AcquisitionState.AcuiringIsFinished);
                if (this.IsAcquiring)
                    can_acquire = false;

                return can_acquire;
            }
        }

        private bool AcquisitionFailed
        {
            get
            {
                bool can_acquire = this.AcquisitionState.HasFlag(AcquisitionState.AcquiringFailed);
                if (this.IsAcquiring)
                    can_acquire = false;

                return can_acquire;
            }
        }

        public bool IsAcquiring
        {
            get
            {
                return this.AcquisitionState.HasFlag(AcquisitionState.IsAcquiring);
            }
        }

        public bool AllSessionsComplete
        {
            get
            {
                return this.Sessions.AllSessionsComplete;
            }
        }

        public string TitleMessage
        {
            get
            {
                return AcquisitionSet.GetTitle(this.AcquisitionType);
            }
        }

        public string AdviceMessage
        {
            get
            {
                return AcquisitionSet.GetAdvice(this.AcquisitionType, this.Sessions.CurrentSpectraCount > 1);
            }
        }


        public virtual string SerialExistsMessage => String.Empty;


        private string serial = String.Empty;
        public virtual string Serial
        {
            get
            {
                return this.serial;
            }
            set
            {
                this.serial = value ?? String.Empty;
            }
        }

        public AcquisitionType AcquisitionType { get; protected set; }
        protected Regex Regex { get; set; }

        protected FinderButtonState FinderButtonState { get; set; }
        protected AcquisitionState AcquisitionState { get; set; } = AcquisitionState.None;

        public AcquisitionSessions Sessions { get; private set; }
        private bool NoUpdateFinderButton { get; set; } = false;
    }
}
