// Created with MonoDevelop
//
//    Copyright (C) 2010 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.Drawing;
using System.IO;
using System.Xml.Serialization;

using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.NPlotEngine.Overlay.Annotation
{

    public enum AnnotationType
    {
        Text,
        Line,
        Rectangle,
        Ellipse,
        Arrow,
        DoubleArrow
    }
    
    [Serializable()]
    public class Annotation
    {
        protected IPlotEngine plot;
        
        private bool interactive;
        
        public double X            { get; set; }
        public double Y            { get; set; }
        public double Width        { get; set; }
        public double Height       { get; set; }
        
        public bool Visible        { get; set; }
        
        public int ColorArgb       { get; set; }
        public string Text         { get; set; }
        public string FontFamily   { get; set; }
        public float  FontSize     { get; set; }
        public FontStyle FontStyle { get; set; }
        
        public float     LineWidth { get; set; }
        public LineStyle LineStyle { get; set; }
        public CapStyle  CapStyle  { get; set; }
        
        public int PlotTypeId      { get; set; }
        
        public AnnotationType Type;
        
        
        protected Annotation()
        {
        }
        
        protected Annotation(IPlotEngine plot, AnnotationType type)
        {
            this.plot = plot;
            this.Visible = false;
            this.Interactive = false;
            this.PlotTypeId = plot.PlotTypeId;
            this.Text = string.Empty;
            this.FontFamily = string.Empty;
            this.Type = type;
        }
        
        protected Annotation(IPlotEngine plot, Annotation a)
        {
            this.plot = plot;
            this.X = a.X;
            this.Y = a.Y;
            this.Width = a.Width;
            this.Height = a.Height;
            this.Visible = a.Visible;
            this.Interactive = a.Interactive;
            this.Text = a.Text;
            this.FontFamily = a.FontFamily;
            this.FontSize = a.FontSize;
            this.ColorArgb = a.ColorArgb;
            this.LineWidth = a.LineWidth;
            this.LineStyle = a.LineStyle;
            this.CapStyle = a.CapStyle;
            this.PlotTypeId = a.PlotTypeId;
            this.Type = a.Type;
        }
        
        public virtual void Draw(Graphics g)
        {
        }
        
        public virtual void Motion(int x, int y, Gdk.ModifierType modifier)
        {
        }
        
        public virtual void ButtonPressed(int x, int y, Gdk.ModifierType modifier)
        {
        }
        
        public virtual void ButtonReleased(int x, int y, Gdk.ModifierType modifier)
        {
        }
        
        public override bool Equals(object obj)
        {
            if (!(obj is Annotation a))
                return false;

            return (this.CapStyle.Equals(a.CapStyle)     &&
                    this.ColorArgb.Equals(a.ColorArgb)   &&
                    this.FontFamily.Equals(a.FontFamily) &&
                    this.FontSize.Equals(a.FontSize)     &&
                    this.FontStyle.Equals(a.FontStyle)   &&
                    this.Height.Equals(a.Height)         &&
                    this.LineStyle.Equals(a.LineStyle)   &&
                    this.LineWidth.Equals(a.LineWidth)   &&
                    this.PlotTypeId.Equals(a.PlotTypeId) &&
                    this.Text.Equals(a.Text)             &&
                    this.Width.Equals(a.Width)           &&
                    this.X.Equals(a.X)                   &&
                    this.Y.Equals(a.Y)                   &&
                    this.Type.Equals(a.Type));
        }
        
        public override int GetHashCode ()
        {
            return  this.CapStyle.GetHashCode()   ^
                    this.ColorArgb.GetHashCode()  ^
                    this.FontFamily.GetHashCode() ^
                    this.FontSize.GetHashCode()   ^
                    this.FontStyle.GetHashCode()  ^
                    this.Height.GetHashCode()     ^
                    this.LineStyle.GetHashCode()  ^
                    this.LineWidth.GetHashCode()  ^
                    this.PlotTypeId.GetHashCode() ^
                    this.Text.GetHashCode()       ^
                    this.Width.GetHashCode()      ^
                    this.X.GetHashCode()          ^
                    this.Y.GetHashCode()          ^
                    this.Type.GetHashCode();
        }
        
        public static Annotation Deserialize(IPlotEngine plot, byte[] data)
        {
            using (MemoryStream stream = new MemoryStream(data))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Annotation));
                Annotation a = (Annotation)serializer.Deserialize(stream);

                if (a == null)
                    throw new Exception(Catalog.GetString("Error while deserialisation."));
                
                switch (a.Type)
                {
                    
                case AnnotationType.Text:
                    return new Text(plot, a);
                    
                case AnnotationType.Line:
                    return new Line(plot, a);
                    
                case AnnotationType.Rectangle:
                    return new Rectangle(plot, a);
                    
                case AnnotationType.Ellipse:
                    return new Ellipse(plot, a);
                    
                case AnnotationType.Arrow:
                    return new Arrow(plot, a);
                    
                case AnnotationType.DoubleArrow:
                    return new DoubleArrow(plot, a);
                    
                default:
                    // annotation type unknown
                    return null;
                    
                }                    
            }
        }

        public byte[] Serialize()
        {
            using (MemoryStream stream = new MemoryStream())
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Annotation));
                serializer.Serialize(stream, new Annotation(null, this));
                return stream.ToArray();
            }
        }
        
        public virtual bool Interactive
        {
            get { return this.interactive;  }
            set 
            { 
                if (this.plot != null && this.plot.GtkWidget.GdkWindow != null)
                {
                    if (value)
                        this.plot.GtkWidget.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Pencil);
                    else
                        this.plot.GtkWidget.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Tcross);
                }
                this.interactive = value; 
            }
        }
        
        protected Font Font
        {
            get 
            { 
                try
                {
                    return new Font(this.FontFamily, this.FontSize, this.FontStyle, GraphicsUnit.Pixel); 
                }
                catch
                {
                    return new Font(System.Drawing.FontFamily.GenericSansSerif, 12f, GraphicsUnit.Pixel);
                }
            }
        }
        
        protected Color Color
        {
            get { return Color.FromArgb(this.ColorArgb); }
        }
    
        public Region ClippingRegion
        {
            get
            {
                int x = Math.Min(this.plot.PhysicalXMin, this.plot.PhysicalXMax);
                int y = Math.Min(this.plot.PhysicalYMin, this.plot.PhysicalYMax);
                int w = Math.Abs(this.plot.PhysicalXMax - this.plot.PhysicalXMin);
                int h = Math.Abs(this.plot.PhysicalYMax - this.plot.PhysicalYMin);
                System.Drawing.Rectangle r = new System.Drawing.Rectangle(x, y, w, h);
                return new Region(r);
            }
        }
    }
}