// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2018 Thomas Klose & 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 Gtk;
using Glade;
using System.Drawing;
using System;
using System.Drawing.Drawing2D;

namespace Hiperscan.GtkExtensions.Widgets
{
    public partial class ExtToolTip
    {
        public delegate void ExtToolTipDesigner(Gtk.Window window);

        public enum ToolTipPosition
        {
            TopLeft,
            TopRight,
            BottomLeft,
            BottomRigth,
        }

        [Widget] Gtk.Dialog main_dialog = null;
        [Widget] Gtk.Button close_button = null;
        [Widget] Gtk.Label top_spacer = null;
        [Widget] Gtk.Label bottom_spacer = null;
        [Widget] Gtk.Label spacer_left = null;
        [Widget] Gtk.Label spacer_right = null;
        [Widget] Gtk.LinkButton linkbutton = null;
        [Widget] Gtk.Label title_label = null;
        [Widget] Gtk.Label content_label = null;
        [Widget] Gtk.Image image = null;

        private ExtToolTip(bool is_dialog,
                          int x_mouse_position,
                          int y_mouse_position,
                           string stock,
                          string title,
                          string message,
                           string link_text = null,
                           EventHandler link_event_handler = null)
        {
            //fixes a bug libglade
            Gtk.LinkButton tmp = new LinkButton("tmp"); 
            this.IsDialog = is_dialog;
            this.XPosParent = x_mouse_position;
            this.YPosParent = y_mouse_position;

            Glade.XML gui = new Glade.XML(null, "ext_tooltip.glade", "main_dialog", "Hiperscan");
            gui.Autoconnect(this);

            ExtToolTip.CurrentToolTip = this;

            if (title == null)
                title = String.Empty;

            this.image.Stock = stock;
            this.title_label.Markup = "<b>" + title + "</b>";
            this.content_label.Text = message;
            this.linkbutton.Label = link_text ?? String.Empty;
            this.LinkEvent = link_event_handler;

            this.Build();
        }

        private void Build()
        {
            this.close_button.UseUnderline = true;

            this.top_spacer.HeightRequest = this.TailHeight;
            this.bottom_spacer.HeightRequest = this.TailHeight;
            this.spacer_left.WidthRequest = RoundRectRadius;
            this.spacer_right.WidthRequest = RoundRectRadius;

            if (this.IsDialog == true)
            {
                this.main_dialog.Modal = true;
                this.close_button.Visible = true;
            }
            else
            {
                this.main_dialog.Modal = false;
                this.close_button.Visible = false;
            }

            if (String.IsNullOrEmpty(this.linkbutton.Label) == false)
            {
                if (this.LinkEvent == null)
                    throw new Exception("Event handler is not set.");

                this.linkbutton.Clicked += this.LinkEvent;
                this.linkbutton.Clicked += (sender, e) =>
                {
                    this.main_dialog.Destroy();
                };
                this.linkbutton.Visible = true;
            }

            this.main_dialog.SizeAllocated += (o, args) =>
            {
                this.SetMainWindowPosition();
                this.RenderTooltip();
                this.main_dialog.QueueDraw();
            };

            this.main_dialog.ExposeEvent += this.OnMainWindowExposeEvent;
            this.close_button.Clicked += this.OnCloseDialogButtonClicked;
            this.close_button.KeyPressEvent += this.OnCloseDialogButtonKeyPressed;
              
            this.Show();
        }

        private void Show()
        {
            if (this.IsDialog)
            {
                ExtToolTip.SetExtToolTipDesign?.Invoke(this.main_dialog);
                this.main_dialog.Run();
            }
            else
            {
                GLib.Timeout.Add(ExtToolTip.ToolTipInterval, () =>
                {
                    if (this.IsDestroyed == false)
                    {
                        ExtToolTip.SetExtToolTipDesign?.Invoke(this.main_dialog);
                        this.main_dialog.ShowAll();
                    }

                    return false;
                });
            }
        }

        private void SetMainWindowPosition()
        {
            int monitors = this.main_dialog.Screen.NMonitors;
            int single_screen_x = this.main_dialog.Screen.Width / monitors;
            int current_monitor = this.XPosParent / single_screen_x;
            this.XPosition = this.XPosParent % single_screen_x;

            int screen_y = this.main_dialog.Screen.Height;
            this.YPosition = this.YPosParent;

            int distance = this.IsDialog ? 0 : this.NoDialogDistance;

            bool is_right = true;
            if (this.XPosition + this.main_dialog.Allocation.Width + distance > single_screen_x)
            {
                this.XPosition = this.XPosition - this.main_dialog.Allocation.Width - distance;
                is_right = false;
            }
            else
            {
                this.XPosition += distance;
            }

            bool is_bottom = true;
            if (this.YPosition + this.main_dialog.Allocation.Height + distance > screen_y)
            {
                this.top_spacer.Visible = false;
                this.YPosition = this.YPosition - this.main_dialog.Allocation.Height - distance;
                is_bottom = false;
            }
            else
            {
                this.YPosition += distance;
                this.bottom_spacer.Visible = false;
            }

            if (is_bottom == false)
            {
                if (is_right == false)
                    this.ToolTipPosition2 = ToolTipPosition.TopLeft;
                else
                    this.ToolTipPosition2 = ToolTipPosition.TopRight;
            }
            else
            {
                if (is_right == false)
                    this.ToolTipPosition2 = ToolTipPosition.BottomLeft;
                else
                    this.ToolTipPosition2 = ToolTipPosition.BottomRigth;
            }

            this.XPosition += current_monitor * single_screen_x;

            this.main_dialog.Move(this.XPosition, this.YPosition);
        }

        private Rectangle GetRectangle()
        {
            Rectangle rectangle = new Rectangle();

            int win_width = this.main_dialog.Allocation.Width;
            int win_height = this.main_dialog.Allocation.Height;

            switch (this.ToolTipPosition2)
            {
                case ToolTipPosition.TopLeft:
                case ToolTipPosition.TopRight:
                    rectangle.X = 0;
                    rectangle.Y = 0;
                    rectangle.Width = win_width;
                    rectangle.Height = win_height - this.TailHeight;
                    break;

                case ToolTipPosition.BottomLeft:
                case ToolTipPosition.BottomRigth:
                    rectangle.X = 0;
                    rectangle.Y = this.TailHeight;
                    rectangle.Width = win_width;
                    rectangle.Height = win_height - this.TailHeight;
                    break;
            }

            return rectangle;
        }

        private Point[] GetTailPoints()
        {
            Point[] tail_points = new Point[3];

            int win_width = this.main_dialog.Allocation.Width;
            int win_height = this.main_dialog.Allocation.Height;



            switch (this.ToolTipPosition2)
            {
                case ToolTipPosition.TopLeft:
                    tail_points[0].X = win_width - this.TailIndent / 2;
                    tail_points[0].Y = win_height - this.TailHeight;
                    tail_points[1].X = win_width;
                    tail_points[1].Y = win_height;
                    tail_points[2].X = win_width - this.TailIndent;
                    tail_points[2].Y = win_height - this.TailHeight;
                    break;

                case ToolTipPosition.TopRight:
                    tail_points[0].X = this.TailIndent / 2;
                    tail_points[0].Y = win_height - this.TailHeight;
                    tail_points[1].X = 0;
                    tail_points[1].Y = win_height;
                    tail_points[2].X = this.TailIndent;
                    tail_points[2].Y = win_height - this.TailHeight;
                    break;

                case ToolTipPosition.BottomLeft:
                    tail_points[0].X = win_width - this.TailIndent / 2;
                    tail_points[0].Y = this.TailHeight;
                    tail_points[1].X = win_width;
                    tail_points[1].Y = 0;
                    tail_points[2].X = win_width - this.TailIndent;
                    tail_points[2].Y = this.TailHeight;
                    break;


                case ToolTipPosition.BottomRigth:
                    tail_points[0].X = this.TailIndent / 2;
                    tail_points[0].Y = this.TailHeight;
                    tail_points[1].X = 0;
                    tail_points[1].Y = 0;
                    tail_points[2].X = this.TailIndent;
                    tail_points[2].Y = this.TailHeight;
                    break;

                default:
                    break;
            }
            return tail_points;

          
        }



        private void RenderTooltip()
        {
            Bitmap bmp = new Bitmap(this.main_dialog.Allocation.Width, this.main_dialog.Allocation.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;

            GraphUtil g2 = new GraphUtil(g);
            Brush b = new SolidBrush(Color.Red);

            Rectangle rectangle = this.GetRectangle();
            g2.FillRoundRectangle(b,
                                  rectangle.X,
                                  rectangle.Y,
                                  rectangle.Width,
                                  rectangle.Height,
                                  this.RoundRectRadius);

            b = new SolidBrush(Color.Blue);
            Point[] points = this.GetTailPoints();
            g.FillPolygon(b, points);
            g.Dispose();

            this.ModifyWindowShape(bmp, this.main_dialog);
        }

        private void ModifyWindowShape(System.Drawing.Bitmap bmp, Gtk.Window window)
        {
            try
            {
                //save bitmap to stream
                System.IO.MemoryStream stream = new System.IO.MemoryStream();
                bmp.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                //verry important: put stream on position 0
                stream.Position = 0;
                //get the pixmap mask
                Gdk.Pixbuf buf = new Gdk.Pixbuf(stream, bmp.Width, bmp.Height);
                Gdk.Pixmap map1, map2;
                buf.RenderPixmapAndMask(out map1, out map2, 255);
                //shape combine window 
                window.ShapeCombineMask(map2, 0, 0);
                //dispose
                buf.Dispose();
                map1.Dispose();
                map2.Dispose();
                bmp.Dispose();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "\r\n" + ex);
            }
        }

        protected void OnMainWindowExposeEvent(object sender, Gtk.ExposeEventArgs evnt)
        {

            Graphics g = Gtk.DotNet.Graphics.FromDrawable(evnt.Event.Window);

            Rectangle rect = new Rectangle(this.main_dialog.Allocation.X, 
                                           this.main_dialog.Allocation.Y, 
                                           this.main_dialog.Allocation.Width,
                                           this.main_dialog.Allocation.Height);

            Brush b = new LinearGradientBrush(rect, this.Color1, this.Color2, 90, true);
            g.FillRectangle(b, rect);

            //draw border
            SolidBrush bb = new SolidBrush(Color.FromArgb(147, 148, 166));
            GraphUtil g2 = new GraphUtil(g);
            Pen border_pen = new Pen(bb, 5F);
            Rectangle rectangle = this.GetRectangle();
            g2.DrawRoundRectangle(border_pen, 
                                  rectangle.X,
                                  rectangle.Y,
                                  rectangle.Width,
                                  rectangle.Height,
                                  this.RoundRectRadius);

            Point[] tail_points = this.GetTailPoints();
            //g.FillPolygon(b, tail_points);
            Pen border_pen2 = new Pen(b, 5F);
            g.DrawLine(border_pen2, tail_points[0], tail_points[2]);
 
            g.DrawLine(border_pen, tail_points[0], tail_points[1]);
            g.DrawLine(border_pen, tail_points[1], tail_points[2]);


            this.main_dialog.Child.SendExpose(evnt.Event);
        }


        private void Destroy()
        {
            this.IsDestroyed = true;
            this.main_dialog.Destroy();
        }

        private void OnCloseDialogButtonClicked(object sender, System.EventArgs e)
        {
            this.IsDestroyed = true;
            this.main_dialog.Destroy();
        }

        private void OnCloseDialogButtonKeyPressed(object o, Gtk.KeyPressEventArgs args)
        {
            if (args.Event.Key == Gdk.Key.Escape)
            {
                this.IsDestroyed = true;
                this.main_dialog.Destroy();
            }
        }

        private EventHandler LinkEvent { get; set; }
        private bool IsDialog { get; set; } = false;
        private int XPosition { get; set; } = 0;
        private int YPosition { get; set; } = 0;
        private ToolTipPosition ToolTipPosition2 { get; set; } = ExtToolTip.ToolTipPosition.BottomLeft;

        private int TailIndent { get; set; } = 60;
        private int TailHeight { get; set; } = 30;
        private int RoundRectRadius { get; set; } = 15;

        private Color Color1 { get; set; } = Color.FromArgb(251, 254, 255);
        private Color Color2 { get; set; } = Color.FromArgb(229, 229, 239);

        private bool IsDestroyed { get; set; } = false;

        private int XPosParent { get; set; } = 0;
        private int YPosParent { get; set; } = 0;
        private int NoDialogDistance { get; set; } = 15;

        public static ExtToolTipDesigner SetExtToolTipDesign { private get; set; }
    }
}
