// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2012 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 System;
using System.Collections.Generic;


namespace Hiperscan.GtkExtensions.Compiler
{
    
    public abstract class GraphicsObject
    {
        public abstract GraphicsBrush Brush{get;set;}
    }
    
    public class GraphicsGroup : GraphicsObject
    {
        public GraphicsGroup ()
        {
            this.Children = new List<GraphicsObject>();
            this.TransformGroup = new List<GraphicsTransformation>();
            this.Opacity = 1.0;
            this.ClipRegion = null;
        }
        
        public List<GraphicsObject> Children {get; private set;}
        public List<GraphicsTransformation> TransformGroup {get; set;}
        public GraphicsShape ClipRegion {get;set;}
        public double Opacity{get;set;}

        public override GraphicsBrush Brush { get; set; }
    }
    
    public class GraphicsImage : GraphicsObject
    {
        public GraphicsImage ()
        {
            this.Coordinates = new GraphicsRectangle();
        }
        public System.Drawing.Bitmap Bitmap{get; private set;}
        public Cairo.ImageSurface ImageSurface {get; private set;}
        public double ImageWidth {get; private set;}
        public double ImageHeight {get; private set;}
        public GraphicsRectangle Coordinates {get; set;}

        public string FileName 
        { 

            set
            {
                this.Bitmap = new System.Drawing.Bitmap(value);
                this.ImageSurface = new Cairo.ImageSurface(value);
                this.ImageWidth = this.Bitmap.Width;
                this.ImageHeight = this.Bitmap.Height;
            }
        }
    
        public override GraphicsBrush Brush { get; set; }
    }
    
    public class GraphicsText : GraphicsObject
    {
        public GraphicsText ()
        {
            this.Font = new GraphicsFont();
            this.Position = new GraphicsPoint();
            this.Text = string.Empty;
            this.MaxWidth = -1;
            this.Brush = new GraphicsSolidColorBrush(GraphicsColors.Black);
        }

        public GraphicsPoint Position{get;set;}
        public GraphicsFont Font{get;set;}
        public string Text{get; set;}
        public double MaxWidth {get;set;}
        
        public override GraphicsBrush Brush { get; set; }
    }
    
    public class GraphicsTextParagraph : GraphicsObject
    {
        public GraphicsTextParagraph()
        {
            this.Coordinates = new GraphicsRectangle();
            this.Font = new GraphicsFont();
            this.Text = string.Empty;
            this.Brush = new GraphicsSolidColorBrush();
        }
    
        public GraphicsFont Font{get;set;}

        public string Text{get; set;}

        private GraphicsRectangle coordinates = null;
        public GraphicsRectangle Coordinates {
            get {
                return this.coordinates;
            }
            set {
                this.coordinates = value;
                if (this.Brush != null)
                    this.Brush.BoundingBox = value;
            }

        }

        private GraphicsBrush brush = null;
        public override GraphicsBrush Brush {
            get {
                return this.brush;
            }
            set {
                this.brush = value;
                if (value != null)
                    this.brush.BoundingBox = this.Coordinates;
            }
        }
    }
    
    public abstract class GraphicsShape : GraphicsObject
    {
        public GraphicsShape ()
        {
        }

        public GraphicsPen Pen{get;set;}
        public bool IsClosed{get;set;}
        public abstract GraphicsRectangle GetBoundingBox();
    }
    
    public class GraphicsShapeLine : GraphicsShape
    {
        public GraphicsShapeLine() : this (0.0, 0.0, 0.0, 0.0)
        {
        }
        
        
        public GraphicsShapeLine(GraphicsPoint start, GraphicsPoint end) : this (start.X, start.Y, end.X, end.Y)
        {
        }
        
        public GraphicsShapeLine(double x_start, double y_start, GraphicsPoint end) : this (x_start, y_start, end.X, end.Y)
        {
        }
        
        public GraphicsShapeLine(GraphicsPoint start, double x_end, double y_end) : this(start.X, start.Y, x_end, y_end)
        {
        }
        
        public GraphicsShapeLine(double x_start, double y_start, double x_end, double y_end)
        {
            this.XStart = x_start;
            this.YStart = y_start;
            this.XEnd = x_end;
            this.YEnd = y_end;
            this.IsClosed = false;
        }

        private double x_start = 0;
        public double XStart {
            get {
                return this.x_start;
            }

            set {
                this.x_start = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();}
        }
                
        private double y_start = 0;
        public double YStart {
            get {
                return this.y_start;
            }
            
            set {
                this.y_start = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }

        private double x_end = 0;
        public double XEnd {
            get {
                return this.x_end;
            }
            
            set {
                this.x_end = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }

        private double y_end = 0;
        public double YEnd {
            get {
                return this.y_end;
            }

            set {
                this.y_end = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }


        private GraphicsBrush brush = null;
        public override GraphicsBrush Brush 
        {
            get 
            {
                return this.brush;
            }
            set 
            {
                this.brush = value;
                if (value != null) {
                    this.brush.BoundingBox = this.GetBoundingBox();
                }
            }
        }
        
        public override GraphicsRectangle GetBoundingBox ()
        {
            double x = this.x_start < this.x_end ? this.x_start : this.x_end;
            double y = this.y_start < this.y_end ? this.y_start : this.y_end;
            
            double width = Math.Abs(this.x_start - this.x_end);
            double height = Math.Abs(this.y_start - this.y_end);
            
            return new GraphicsRectangle(x, y, width, height);
            
        }
}
    
    public class GraphicsShapeRectangle : GraphicsShape
    {
        public GraphicsShapeRectangle() : this(0.0, 0.0, 0.0, 0.0)
        {
        }
        
        public GraphicsShapeRectangle(GraphicsRectangle rect)
        {
            this.Coordinates = rect;
        }
        
        public GraphicsShapeRectangle(GraphicsPoint point, GraphicsSize size) : this(point.X, point.Y, size.Width, size.Height)
        {
        }
        
        public GraphicsShapeRectangle(GraphicsPoint point, double width, double height) : this (point.X, point.Y, width, height)
        {
        }
        
        public GraphicsShapeRectangle(double x, double y, GraphicsSize size) : this(x, y, size.Width, size.Height)
        {
        }
        
        public GraphicsShapeRectangle(double x, double y, double width, double height)
        {
            this.Coordinates = new GraphicsRectangle();
            this.Coordinates.X = x;
            this.Coordinates.Y = y;
            this.Coordinates.Width = width;
            this.Coordinates.Height = height;
            this.IsClosed = false;
        }
        private GraphicsRectangle coordinates = null;
        public GraphicsRectangle Coordinates 
        {
            get 
            {
                return this.coordinates;
            }
            set 
            {
                this.coordinates = value;
                if (this.Brush != null)
                    this.Brush.BoundingBox = this.GetBoundingBox();

            }
        }

        private GraphicsBrush brush;
        public override GraphicsBrush Brush 
        {
            get 
            {
                return this.brush;
            }
            set 
            {
                this.brush = value;
                if (value != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        public override GraphicsRectangle GetBoundingBox ()
        {
            return this.coordinates;
        }
    }
    
    public class GraphicsShapeCircle : GraphicsShape
    {
        public GraphicsShapeCircle() : this (0.0, 0.0, 0.0)
        {
        }
        
        public GraphicsShapeCircle(GraphicsPoint point, double radius) : this (point.X, point.Y, radius)
        {
        }
        
        public GraphicsShapeCircle(double x, double y, double radius)
        {
            this.X = x;
            this.Y = y;
            this.Radius = Radius;
            this.IsClosed = false;
        }
        
        private double x = 0;
        public double X 
        {
            get 
            {
                return this.x;
            }
            set 
            {
                this.x = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }

        private double y = 0;
        public double Y 
        {
            get 
            {
                return this.y;
            }
            set 
            {
                this.y = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }

        private double radius = 0;
        public double Radius
        {
            get
            {
                return this.radius;
            }
            set 
            {
                this.radius = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        

        private GraphicsBrush brush = null;
        public override GraphicsBrush Brush 
        {
            get 
            {
                return this.brush;
            }
            set 
            {
                this.brush = value;
                if (value != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        public override GraphicsRectangle GetBoundingBox()
        {
            double x = this.X - this.Radius;
            double y = this.Y - this.Radius;
            double width = 2 * this.Radius;
            double height = 2* this.Radius;
            
            return new GraphicsRectangle(x, y, width, height);
        }
    }
    
    public class GraphicsShapePolygone : GraphicsShape
    {
        public GraphicsShapePolygone(GraphicsPoint[] points)
        {
            this.Points = points;
        }
        
        public GraphicsShapePolygone(List<GraphicsPoint> points)
        {
            this.Points = points.ToArray();
        }

        public GraphicsPoint[] points = null;
        public GraphicsPoint[] Points 
        {
            get 
            {
                return this.points;
            }
            set 
            {
                this.points = value;
                if (this.Brush != null)
                    this.Brush.BoundingBox = this.GetBoundingBox();
            }
        }

        private GraphicsBrush brush = null;
        public override GraphicsBrush Brush 
        {
            get 
            {
                return this.brush;
            }
            set 
            {
                this.brush = value;
                if (value != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }

        public override GraphicsRectangle GetBoundingBox ()
        {
            if (this.points == null)
                return new GraphicsRectangle ();

            double x_min = double.MaxValue;
            double x_max = double.MinValue;
            double y_min = double.MaxValue;
            double y_max = double.MinValue;

            foreach (GraphicsPoint p in this.points) 
            {
                if ( p.X < x_min)
                    x_min = p.X;
                if (p.X > x_max)
                    x_max = p.X;
                if (p.Y < y_min)
                    y_min = p.Y;
                if (p.Y > y_max)
                     y_max = p.Y;
            }

            return new GraphicsRectangle(x_min, y_min, x_max - x_min, y_max - y_min);
        }
    }
    
    public class GraphicsShapeArc : GraphicsShape
    {
        public GraphicsShapeArc(GraphicsPoint position, double radius, double start_arc_in_degree, double end_arc_in_degree)
        {
            this.Radius = radius;
            this.StartArcInDegree = start_arc_in_degree;
            this.EndArcInDegree = end_arc_in_degree;
            this.Position = position;
        }
        
        private GraphicsPoint position = new GraphicsPoint();
        public GraphicsPoint Position
        {
            get 
            {
                return this.position;
            }
            set
            {
                this.position = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        private double radius = 0.0;
        public double Radius 
        {
            get 
            {
                return this.radius;
            }
            set
            {
                this.radius = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        private double start_arc_in_degree = 0.0;
        public double StartArcInDegree 
        {
            get
            {
                return this.start_arc_in_degree;
            }
            set
            {
                this.start_arc_in_degree = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        private double end_arc_in_degree = 0.0;
        public double EndArcInDegree 
        {
            get
            {
                return this.end_arc_in_degree;
            }
            set
            {
                this.end_arc_in_degree = value;
                if (this.Brush != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        private GraphicsBrush brush = null;
        public override GraphicsBrush Brush 
        {
            get 
            {
                return this.brush;
            }
            set 
            {
                this.brush = value;
                if (value != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        public override GraphicsRectangle GetBoundingBox()
        {
            double x1 = this.Radius * Math.Cos(this.StartArcInDegree / 180 * Math.PI) + this.Position.X;
            double y1 = this.Radius * Math.Sin(this.StartArcInDegree / 180 * Math.PI) + this.Position.Y;
            
            double x2 = this.Radius * Math.Cos(this.EndArcInDegree / 180 * Math.PI) + this.Position.X;
            double y2 = this.Radius * Math.Sin(this.EndArcInDegree / 180 * Math.PI) + this.Position.Y;

            double x = x1 < x2 ? x1 : x2;
            double y = y1 < y2 ? y1 : y2;
            
            double width = x1 < x2 ? x2 - x1 : x1 - x2;
            double height = y1 < y2 ? y2 - y1 : y1 - y2;
            
            return new GraphicsRectangle(x, y, width, height);
        }
    }
    
    public class GraphicsShapePath : GraphicsShape
    {
        public List<GraphicsShape> Shapes {get;set;}
        
        public GraphicsShapePath()
        {
            this.Shapes = new List<GraphicsShape>();
        }
        
        public void Add(GraphicsShape shape)
        {
            shape.IsClosed = false;
            this.Shapes.Add(shape);
            if (this.Brush != null)
                this.Brush.BoundingBox = this.GetBoundingBox();
        }
            
        private GraphicsBrush brush = null;
        public override GraphicsBrush Brush 
        {
            get 
            {
                return this.brush;
            }
            set 
            {
                this.brush = value;
                if (value != null)
                    this.brush.BoundingBox = this.GetBoundingBox();
            }
        }
        
        
        public override GraphicsRectangle  GetBoundingBox()
        {
            if (this.Shapes.Count == 0)
                return new GraphicsRectangle();
            
            double x_min = double.MaxValue;
            double x_max = double.MinValue;
            double y_min = double.MaxValue;
            double y_max = double.MinValue;
            
            foreach(GraphicsShape shape in this.Shapes)
            {
                GraphicsRectangle rect = shape.GetBoundingBox();
                
                if (rect.X < x_min)
                    x_min = rect.X;
                if (rect.X + rect.Width > x_max)
                    x_max = rect.X + rect.Width;
                if (rect.Y < y_min)
                    y_min = rect.Y;
                if (rect.Y + rect.Height > y_max)
                     y_max = rect.Y + rect.Height;
            }

            return new GraphicsRectangle(x_min, y_min, x_max - x_min, y_max - y_min);
        }
    }
}

