// 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 Gtk;
using Cairo;
using Hiperscan.Unix;
using Hiperscan.AnalysisModule.Identification;

namespace Hiperscan.Extensions.ApoIdent.Widgets
{
    public class BAKMarkingWidget
    {
        private const string ACTIVE = "ACTIVE";

        public event EventHandler ValueChanged = null;

        public BAKMarkingWidget(Gtk.Table table, int height)
        {
            this.Table = table;
            this.DrawingSize = height;

            this.InitializeDrawinArea(this.GetEventBox(0).Child as Gtk.DrawingArea, BAKMarkingState.Blue);
            this.InitializeDrawinArea(this.GetEventBox(1).Child as Gtk.DrawingArea, BAKMarkingState.Yellow);
            this.InitializeDrawinArea(this.GetEventBox(2).Child as Gtk.DrawingArea, BAKMarkingState.Orange);
            this.InitializeDrawinArea(this.GetEventBox(3).Child as Gtk.DrawingArea, BAKMarkingState.Red);

            this.BackGroundColor = new Gdk.Color(255, 255, 255); ;
            this.Refresh();
            this.State = BAKMarkingState.None;
        }

        private EventBox GetEventBox(uint column)
        {
            foreach (Gtk.Widget child in this.Table.Children)
            {
                Gtk.Table.TableChild table_child = (Gtk.Table.TableChild)this.Table[child];

                if (table_child.LeftAttach == column)
                    return child as Gtk.EventBox;
            }

            return null;                                                             
        }

        private void InitializeDrawinArea(Gtk.DrawingArea area, BAKMarkingState state)
        {
            switch(state)
            {
                case BAKMarkingState.Blue:
                    this.BlueArea = area;
                    this.BlueArea.TooltipText = Catalog.GetString("Protective goggles");
                    break;

                case BAKMarkingState.Orange:
                    this.OrangeArea = area;
                    this.OrangeArea.TooltipText = Catalog.GetString("Breathing protection");
                    break;

                case BAKMarkingState.Red:
                    this.RedArea = area;
                    this.RedArea.TooltipText = Catalog.GetString(@"Protective gloves
Protective goggles
Breathing protection");
                    break;

                case BAKMarkingState.Yellow:
                    this.YellowArea = area;
                    this.YellowArea.TooltipText = Catalog.GetString("Protective gloves");
                    break;
            }

            area.ExposeEvent += this.OnExpose;
            area.HeightRequest = this.DrawingSize;
            area.WidthRequest = this.DrawingSize;
            area.ModifyBg(StateType.Normal, new Gdk.Color(0, 0, 0));

            if (area.Data.ContainsKey(BAKMarkingWidget.ACTIVE) == false)
                area.Data.Add(BAKMarkingWidget.ACTIVE, false);

            (area.Parent as Gtk.EventBox).AddEvents((int)Gdk.EventType.ButtonRelease);
            (area.Parent as Gtk.EventBox).ButtonReleaseEvent += this.OnDrawingAreaClicked;

            (area.Parent as Gtk.EventBox).AddEvents((int)Gdk.EventType.EnterNotify);
            (area.Parent as Gtk.EventBox).EnterNotifyEvent += this.OnEnterArea;

            (area.Parent as Gtk.EventBox).AddEvents((int)Gdk.EventType.LeaveNotify);
            (area.Parent as Gtk.EventBox).LeaveNotifyEvent += this.OnleaveArea;
        }

        private void OnExpose(object sender, ExposeEventArgs args)
        {
            DrawingArea area = (DrawingArea)sender;
            if (area.GdkWindow == null)
                return;

            Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);

            cr.Save();

            Cairo.Color fill = this.GetColor(area);
            Cairo.Color line_color = new Cairo.Color(0.5, 0.5, 0.5);
            Cairo.Color white_transparent = new Color(1, 1, 1, 0.4);

            int size = this.DrawingSize;

            cr.Rectangle(0, 0, size, size);
            cr.SetSourceColor(this.BackGroundColorCairo);
            cr.Fill();

            double clip_size = size;
            if (this.IsActive(area) == false)
                clip_size *= 0.5;

            cr.Arc(size / 2, size / 2, clip_size / 2, 0, 2 * Math.PI);
            cr.ClosePath();
            cr.Clip();

            cr.Rectangle(0, 0, size, size);
            cr.SetSourceColor(fill);
            cr.Fill();

            cr.Rectangle(0, 0, size, size);
            cr.LineWidth = 1;
            cr.SetSourceColor(line_color);
            cr.Stroke();

            cr.MoveTo(new PointD(0, 0));
            cr.LineTo(new PointD(0, size));
            cr.LineTo(new PointD(size, 0));
            cr.LineTo(new PointD(0, 0));
            cr.ClosePath();
            cr.SetSourceColor(white_transparent);
            cr.Fill();

            cr.Arc(size / 2, size / 2, clip_size / 2, 0, 2 * Math.PI);
            cr.ClosePath();
            cr.SetSourceColor(new Color(0, 0, 0));
            cr.LineWidth = 0.5;
            cr.Stroke();

            cr.Restore();

            if (this.Table.Sensitive == false)
            {
                cr.Rectangle(0, 0, size, size);
                cr.SetSourceColor(this.InsensitiveColor);
                cr.Fill();
            }

            ((IDisposable)cr.GetTarget()).Dispose();
            ((IDisposable)cr).Dispose();
        }

        private Cairo.Color GetColor(Gtk.DrawingArea area)
        {
            if (area == this.RedArea)
                return this.Red;

            if (area == this.YellowArea)
                return this.Yellow;

            if (area == this.BlueArea)
                return this.Blue;

            return this.Orange;
        }

        private void Refresh()
        {
            this.RedArea.QueueDraw();
            this.BlueArea.QueueDraw();
            this.YellowArea.QueueDraw();
            this.OrangeArea.QueueDraw();
        }

        private void OnEnterArea(object sender, EnterNotifyEventArgs e)
        {
            this.SetCursor(sender, Gdk.CursorType.Hand2);
        }

        private void OnleaveArea(object sender, LeaveNotifyEventArgs e)
        {
            if (this.IsGuiUpdating == false)
                this.SetCursor(sender, Gdk.CursorType.Arrow);
        }

        private void OnDrawingAreaClicked(object sender, Gtk.ButtonReleaseEventArgs e)
        {
            if (this.Table.Sensitive == false)
                return;

            if (e.Event.Button == 1)
            {
                this.IsGuiUpdating = true;
                Gtk.DrawingArea area = (sender as Gtk.EventBox).Child as Gtk.DrawingArea;
                bool active = this.IsActive(area);
                this.SetIsActive(area, !active);
                this.SetBakMarkingState(area);
                this.IsGuiUpdating = true;
            }
        }

        private void SetCursor(object sender, Gdk.CursorType cursor)
        {
            Gtk.DrawingArea area = (sender as Gtk.EventBox).Child as Gtk.DrawingArea;

            if (area.GdkWindow == null)
                return;

            if (area.IsRealized == false)
                return;

            if (this.Table.Sensitive == false)
            {
                area.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
                return;
            }

            area.GdkWindow.Cursor = new Gdk.Cursor(cursor);
        }

        private bool IsActive(Gtk.DrawingArea area)
        {
            return (bool)area.Data[BAKMarkingWidget.ACTIVE];
        }

        private void SetIsActive(Gtk.DrawingArea area, bool value)
        {
            area.Data[BAKMarkingWidget.ACTIVE] = value;
        }

        private void SetBakMarkingState(Gtk.DrawingArea area)
        {
            BAKMarkingState tmp = BAKMarkingState.None;

                if (area == this.RedArea)
            {
                if (this.IsActive(area))
                    tmp = BAKMarkingState.Red;
            }
            else
            {
                if (IsActive(this.BlueArea))
                    tmp |= BAKMarkingState.Blue;

                if (IsActive(this.YellowArea))
                    tmp |= BAKMarkingState.Yellow;

                if (IsActive(this.OrangeArea))
                    tmp |= BAKMarkingState.Orange;
            }

            this.State = tmp;
        }

        private BAKMarkingState state = BAKMarkingState.None;
        public BAKMarkingState State
        {
            get
            {
                return this.state;
            }
            set
            {
                if (this.state == value)
                    return;

                if (value.HasFlag(BAKMarkingState.Red))
                    this.state = BAKMarkingState.Red;
                else
                    this.state = value;

                this.SetIsActive(this.RedArea, state.HasFlag(BAKMarkingState.Red));
                this.SetIsActive(this.BlueArea, state.HasFlag(BAKMarkingState.Blue));
                this.SetIsActive(this.YellowArea, state.HasFlag(BAKMarkingState.Yellow));
                this.SetIsActive(this.OrangeArea, state.HasFlag(BAKMarkingState.Orange));

                this.Refresh();
                this.ValueChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        public bool Visible
        {
            set
            {
                this.Table.Visible = value;
            }
        }

        public bool Sensitive
        {
            set
            {
                this.Table.Sensitive = value;
                this.Refresh();
            }
        }



        private Gdk.Color background_color;
        public Gdk.Color BackGroundColor
        {
            private get
            {
                return this.background_color;
            }

            set
            {
                this.background_color = value;
                this.BlueArea.ModifyBg(StateType.Normal, value);
                this.YellowArea.ModifyBg(StateType.Normal, value);
                this.OrangeArea.ModifyBg(StateType.Normal, value);
                this.RedArea.ModifyBg(StateType.Normal, value);
                this.Refresh();
            }
        }

        private Cairo.Color BackGroundColorCairo
        {
            get
            {
                return new Color((double)this.BackGroundColor.Red / ushort.MaxValue,
                                 (double)this.BackGroundColor.Green / ushort.MaxValue,
                                 (double)this.BackGroundColor.Blue / ushort.MaxValue);
            }
        }

        private int DrawingSize { get; set; } = 1;

        private Gtk.DrawingArea RedArea { get; set; }
        private Gtk.DrawingArea YellowArea { get; set; }
        private Gtk.DrawingArea BlueArea { get; set; }
        private Gtk.DrawingArea OrangeArea { get; set; }

        private Cairo.Color Red { get; set; } = new Cairo.Color(0.95, 0, 0);
        private Cairo.Color Yellow { get; set; } = new Cairo.Color(0.8, 0.8, 0);
        private Cairo.Color Blue { get; set; } = new Cairo.Color(0, 0, 0.95);
        private Cairo.Color Orange { get; set; } = new Cairo.Color(0.95, 0.61, 0);
        private Cairo.Color InsensitiveColor { get; set; } = new Cairo.Color(1, 1, 1, 0.5);

        private Gtk.Table Table { get; set; }
        private bool IsGuiUpdating { get; set; } = false;
    }
}
