// 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;
namespace Hiperscan.Extensions.ApoIdent
{
    public class ScanRecognizer
    {
        public event EventHandler TextScanDetected = null;

        public ScanRecognizer(Gtk.Window parent, Configurations.ScannerPreferences settings)
        {
            this.Settings = settings;
            this.NumberOfStartRecognization = this.Settings.StartKeys.Length;
            this.NumberOfEndRecognizations = this.Settings.EndKeys.Length;

            this.RegisterEvents(parent);
            this.IsActive = true;
        }

        private void RegisterEvents(Gtk.Widget parent)
        {
            if (parent is Gtk.Window)
            {
                (parent as Gtk.Window).FocusInEvent += this.OnActivatedWidgetChanged;
            }
            else if (parent is Gtk.CheckButton)
            {
                (parent as Gtk.CheckButton).FocusInEvent += this.OnActivatedWidgetChanged;
                return;
            }
            else if (parent is Gtk.ToolButton)
            {
                (parent as Gtk.ToolButton).FocusChildSet += this.OnActivatedWidgetChanged;
                return;
            }
            else if (parent is Gtk.Entry)
            {
                (parent as Gtk.Entry).FocusInEvent += this.OnActivatedWidgetChanged;
                return;
            }
            else if (parent is Gtk.ComboBox)
            {
                (parent as Gtk.ComboBox).FocusChildSet += this.OnActivatedWidgetChanged;
                return;
            }
            else if (parent is Gtk.Button)
            {
                (parent as Gtk.Button).FocusInEvent += this.OnActivatedWidgetChanged;
                return;
            }


            if(parent is Gtk.Container)
            {
                foreach (Gtk.Widget child_widget in (parent as Gtk.Container).Children)
                    this.RegisterEvents(child_widget);
            }
        }

        [GLib.ConnectBefore]
        private void OnActivatedWidgetChanged(object sender, EventArgs e)
        {
            Gtk.Widget widget = sender as Gtk.Widget;

            if (this.CurrentWidget == widget)
                return;

            if (this.CurrentWidget != null)
                this.CurrentWidget.KeyPressEvent -= this.OnKeyPressed;

            this.CurrentWidget = widget;

            if (this.IsActive == false)
                return;

            this.CurrentWidget.KeyPressEvent += this.OnKeyPressed;
            this.ScannedValue = String.Empty;
            this.ReadScan = false;
            Console.WriteLine("Text scanner active element: {0}", widget.Name);
        }


        [GLib.ConnectBefore]
        private void OnKeyPressed(object sender, Gtk.KeyPressEventArgs e)
        {
            bool no_emit = false;
            this.CapsLockActive = e.Event.State.HasFlag(Gdk.ModifierType.LockMask);

            Gdk.Key key = this.CheckForCapsLock(e.Event);

            if (this.ReadScan == false)
            {
                if (this.Settings.StartKeys[this.Recognition] == key)
                {
                    this.Recognition++;
                    no_emit = true;
                }
                else
                {
                    this.Recognition = 0;
                }

                if (this.Recognition == this.NumberOfStartRecognization)
                {
                    this.Recognition = 0;
                    this.ReadScan = true;

                    GLib.Timeout.Add(2000, () =>
                    {
                        this.Recognition = 0;
                        this.ReadScan = false;
                        return false;
                    });
                }
            }
            else
            {
                no_emit = true;

                if (this.Settings.EndKeys[this.Recognition] == key)
                {
                    this.Recognition++;
                    if (this.Recognition == this.NumberOfEndRecognizations)
                    {
                        this.HasEndReached = true;
                        this.ReadScan = false;
                        this.Recognition = 0;
                    }
                }
                else
                {
                    this.ScannedValue += (char)e.Event.Key;
                    this.Recognition = 0;
                }
            }

            e.RetVal = no_emit;
        }

        private Gdk.Key CheckForCapsLock(Gdk.EventKey event_key)
        {
            Gdk.Key key = event_key.Key;

            if (event_key.State.HasFlag(Gdk.ModifierType.LockMask))
            {
                if (key >= Gdk.Key.A && key <= Gdk.Key.Z)
                {
                    key = key + 32;
                }
                else
                {
                    if (key >= Gdk.Key.a && key <= Gdk.Key.z)
                    {
                        key = key - 32;
                    }
                }
            }

            return key;
        }

        private bool read_scan = true;
        private bool ReadScan
        {
            get
            {
                return this.read_scan;
            }
            set
            {
                if (this.read_scan == value)
                    return;

                this.read_scan = value;

                if (value == false && String.IsNullOrEmpty(this.ScannedValue) == false)
                {
                    if (this.HasEndReached == true)
                        this.TextScanDetected?.Invoke(this, EventArgs.Empty);
                    this.HasEndReached = false;
                }

                this.ScannedValue = String.Empty;
            }
        }

        private bool is_active = false;
        public bool IsActive
        {
            private get
            {
                return this.is_active;
            }
            set
            {
                if (this.is_active == value)
                    return;

                if (this.CurrentWidget != null)
                    this.CurrentWidget.KeyPressEvent -= this.OnKeyPressed;

                if (value == true)
                {
                    if (this.CurrentWidget != null)
                        this.CurrentWidget.KeyPressEvent += this.OnKeyPressed;
                }

                this.is_active = value;
            }
        }

        private bool HasEndReached { get; set; } = false;
        private int NumberOfStartRecognization { get; set; }
        private int NumberOfEndRecognizations { get; set; }

        private Gtk.Widget CurrentWidget { get; set; }
        private int Recognition { get; set; } = 0;

        public string ScannedValue { get; set; } = String.Empty;

        private Configurations.ScannerPreferences Settings { get; set; }
        public bool CapsLockActive { get; private set; } = false;
    }
}
