// 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.Reflection;
using System.IO;

using Hiperscan.Unix;
using Hiperscan.GtkExtensions.Widgets;
using Gtk;
using System.Drawing;

namespace Hiperscan.Extensions.ApoIdent.Widgets
{
    public class SplashScreen
    {
        public event EventHandler InterruptingPressed = null;
        
        
        private enum SplashScreenState
        {
            ApoIndentStart,
            ApoIdentUpdate,
            WarmUp,
            WarmUpCancelled,
        }

        private const double REF_WIDTH = 700;

        public SplashScreen(Gtk.Window parent, Cairo.ImageSurface picture)
        {
            this.Parent = parent;
            this.background = picture;
            this.Scale = SplashScreen.REF_WIDTH / this.background.Width;
        }

        public void StartApoIdentPhase()
        {
            this.IsRunningInitPhase = true;
            this.Show();
            this.Update(SplashScreenState.ApoIndentStart, 0, 0);

            System.Threading.Tasks.Task.Run(() =>
            {
                System.Threading.Thread.Sleep(2500);
                Application.Invoke(this.UpdateApoIdentPhase);
            });
        }

        private void UpdateApoIdentPhase(object sender, EventArgs e)
        {
            if (this.IsRunningInitPhase == true)
                this.Update(SplashScreenState.ApoIdentUpdate, 0, 0);
        }

        public void StopInitPhase(bool start_wrming_up)
        {
            this.IsRunningInitPhase = false;
            if (start_wrming_up == false)
                this.Destroy();
        }

        private void InterruptWarmingUp()
        {
            this.Destroy();
        }

        private void ContinueWarmingUp()
        {
            this.Show();
        }

        public void StopWarmingUp()
        {
            this.IsRunningCancellingPhase = false;
            this.Destroy();
        }

        public void UpdateWarmup(double temperature, double nominal_temperature)
        {
            this.Update(SplashScreenState.WarmUp, temperature, nominal_temperature);
        }

        private void CancelWarmUp()
        {
            this.IsRunningCancellingPhase = true;
            this.Show();
            this.Update(SplashScreenState.WarmUpCancelled, 0, 0);
        }

        private void Show()
        {
            if (this.Dialog != null)
                return;

            this.DrawingArea = new Gtk.DrawingArea();
            this.DrawingArea.ExposeEvent += this.OnExposeEvent;
            this.DrawingArea.WidthRequest = (int)(this.Background.Width * this.Scale);
            this.DrawingArea.HeightRequest = (int)(this.Background.Height * this.Scale);

            this.Dialog = new Dialog();
            this.Dialog.Decorated = false;
            this.Dialog.Modal = true;
            this.Dialog.TransientFor = this.Parent;
            this.Dialog.Remove(this.Dialog.Child);
            this.Dialog.Add(this.DrawingArea);
            this.Dialog.SizeAllocated += (o, args) => this.UpdateShape();
            this.Dialog.DeleteEvent += this.OnDeleteEvent;
            this.Dialog.WindowPosition = Gtk.WindowPosition.Center;
            this.Dialog.Resizable = false;
            this.Dialog.ShowAll();
            this.SetCursor(true);
        }

        private void Destroy()
        {
            if (this.Dialog == null)
                return;

            this.SetCursor(false);
            this.Dialog.Destroy();
            this.Dialog = null;
        }

        private void SetCursor(bool busy)
        {
            if (this.Dialog.IsRealized == false || this.Dialog.GdkWindow == null)
                return;

            if (busy)
                this.Dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);
            else
                this.Dialog.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Arrow);
        }


        private void OnDeleteEvent(object sender, DeleteEventArgs e)
        {
            bool dont_interrupt = this.IsRunningInitPhase || this.IsRunningCancellingPhase;
            e.RetVal = dont_interrupt;

            if (dont_interrupt == false)
            {
                this.InterruptWarmingUp();
                this.Response = ResponseType.None;
                this.InterruptingPressed?.Invoke(this, EventArgs.Empty);

                if (this.Response == ResponseType.Yes)
                {
                    this.CancelWarmUp();
                }
                else
                {
                    this.ContinueWarmingUp();
                }
            }
        }

        private void UpdateShape()
        {
            if (this.Dialog.Requisition.Width < 1 || this.Dialog.Requisition.Height < 1)
                return;

            Bitmap bmp = this.CreateShape(this.Dialog.Requisition);
            MemoryStream stream = new MemoryStream();
            bmp.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            stream.Position = 0;
            Gdk.Pixbuf buf = new Gdk.Pixbuf(stream, bmp.Width, bmp.Height);
            buf.RenderPixmapAndMask(out Gdk.Pixmap map1, out Gdk.Pixmap map2, 255);
            this.Dialog.ShapeCombineMask(map2, 0, 0);
        }

        private Bitmap CreateShape(Requisition req)
        {;
            int r = 30;

            Bitmap bmp = new Bitmap(req.Width, req.Height);
            Graphics g = Graphics.FromImage(bmp);
            Brush b = new SolidBrush(Color.Red);
            g.FillEllipse(b, 0, 0, 2 * r, 2 * r);
            g.FillEllipse(b, req.Width - 2 * r, 0, 2 * r, 2 * r);
            g.FillEllipse(b, 0, req.Height - 2 * r, 2 * r, 2 * r);
            g.FillEllipse(b, req.Width - 2 * r, req.Height - 2 * r, 2 * r, 2 * r);
            g.FillRectangle(b, r, 0, req.Width - 2 * r, req.Height);
            g.FillRectangle(b, 0, r, req.Width, req.Height - 2 * r);

            return bmp;
        }

        private void OnExposeEvent(object sender, Gtk.ExposeEventArgs e)
        {
            Cairo.Context ctx = Gdk.CairoHelper.Create(e.Event.Window);

            ctx.Save();
            ctx.Scale(this.Scale, this.Scale);
            this.Background.Show(ctx, 0, 0);
            ctx.Restore();

            //TODO
            /*
            {
                double width = this.DrawingArea.Allocation.Width;
                double height = this.DrawingArea.Allocation.Height;

                ctx.MoveTo(0, 0.42 * height);
                ctx.LineTo(0, height);
                ctx.LineTo(width, height);
                ctx.LineTo(width, 0.07 * height);
                ctx.LineTo(0.33 * width, 0.59 * height);

                ctx.CurveTo(new Cairo.PointD(0.33 * width, 0.59 * height),
                         new Cairo.PointD(0.31 * width, 0.61 * height),
                        new Cairo.PointD(0.29 * width, 0.60 * height));

                ctx.ClosePath();

                ctx.SetSourceColor(new Cairo.Color(0, 0, 1, 1));
                ctx.Fill();

            }*/

            Pango.Layout pl = Pango.CairoHelper.CreateLayout(ctx);
            pl.FontDescription = new Pango.FontDescription();
            pl.FontDescription.Family = ApoIdentApplication.FONT_FAMILIY;
            pl.FontDescription.Size = (int)(SplashScreen.REF_WIDTH / 50 * 1024);
            pl.FontDescription.Weight = Pango.Weight.Bold;
            pl.SetText(this.Text);
            ctx.SetSourceColor(new Cairo.Color(1, 1, 1));
            ctx.MoveTo(0.05 * SplashScreen.REF_WIDTH,
                       0.78 * this.Background.Height * this.Scale);

            Pango.CairoHelper.ShowLayout(ctx, pl);

            double height = this.DrawingArea.Allocation.Height;
            double width = this.DrawingArea.Allocation.Width;
            double radius = 30;

            ctx.MoveTo(0, radius);
            ctx.Arc(radius, radius, radius,  Math.PI, 3 * Math.PI / 2);
            ctx.LineTo(width - radius, 0);
            ctx.Arc(width - radius, radius, radius, -Math.PI / 2, 0);
            ctx.LineTo(width, height - radius);

            ctx.Arc(width - radius, height - radius, radius, 0, Math.PI / 2);
            ctx.LineTo(radius, height);
            ctx.Arc(radius, height - radius, radius,Math.PI / 2, Math.PI);
            ctx.ClosePath();

            ctx.SetSourceColor(new Cairo.Color(0.3, 0.3, 0.3, 1));
            ctx.LineWidth = 3;
            ctx.Stroke();

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

        private void Update(SplashScreenState state, double temperature, double nominal_temperature)
        {
            switch (state)
            {
                case SplashScreenState.ApoIndentStart:
                    this.Text = String.Format("Copyright \u00A9 Hiperscan GmbH 2010 - {0}", DateTime.Now.ToString("yyyy"));
                    break;

                case SplashScreenState.ApoIdentUpdate:
                    this.Text = Catalog.GetString("is starting and will be ready in a few seconds ...");
                    break;

                case SplashScreenState.WarmUp:
                    this.Text = string.Format(Catalog.GetString("is warming up ({0:F1}°C of {1:F1}). Please wait ..."), temperature, nominal_temperature);
                    break;

                case SplashScreenState.WarmUpCancelled:
                    this.Text = Catalog.GetString("is cancelling warm-up. Please wait ...");
                    break;

                default:
                    break;
            }

            this.DrawingArea.QueueDraw();
        }

        private Cairo.ImageSurface background = null;
        public Cairo.ImageSurface Background 
        { 
            private get
            {
                return this.background;
            }

            set
            {
                this.background = value;
                this.DrawingArea.QueueDraw();
            }
        }

        private Gtk.Dialog Dialog { get; set; }
        private Gtk.Window Parent { get; set; }
        private bool IsRunningInitPhase { get; set; } = false;
        private bool IsRunningCancellingPhase { get; set; } = false;

        private double Scale { get; set; } = 0;
        private Gtk.DrawingArea DrawingArea { get; set; }

        private string Text { get; set; } = String.Empty;
        public Gtk.ResponseType Response { private get; set; } = ResponseType.None;
    }
}
