// 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);
		}
	}
}

