// 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;
using Cairo;


namespace Hiperscan.GtkExtensions.Compiler
{
	
	public class MonoCairoGraphicCompiler : Hiperscan.GtkExtensions.Compiler.GenericGraphicCompiler
	{
		
		private static Pango.Layout MeasureLayout = Pango.CairoHelper.CreateLayout(new Context(new ImageSurface(Format.ARGB32, 1, 1)));
		public static GraphicsSize MeasureGraphicsText(GraphicsText text)
		{
			MeasureLayout.FontDescription = new Pango.FontDescription();
			MeasureLayout.FontDescription.AbsoluteSize = 1024.0 * text.Font.FontSize;
			MeasureLayout.FontDescription.Family = text.Font.FontName;
			switch(text.Font.FontWeight)
			{
			case GraphicsFontWeights.Normal:
				MeasureLayout.FontDescription.Weight = Pango.Weight.Normal;
				break;
				
			case GraphicsFontWeights.Bold:
				MeasureLayout.FontDescription.Weight = Pango.Weight.Bold;
				break;
			}
			
			MeasureLayout.SetText(text.Text);
			int width, height;
			MeasureLayout.GetSize (out width, out height);
			
			return new GraphicsSize(width / 1024.0, height / 1024.0);
		}
		
		public static readonly double DPI = 72;
		
		public Cairo.Context Context {get;set;}
		private Pango.Layout PangoLayout {get;set;}
		
		public MonoCairoGraphicCompiler (Cairo.Context ctx)
		{
			this.StackAlpha = new Stack<double>();
			this.Context = ctx; 
			this.PangoLayout = Pango.CairoHelper.CreateLayout(ctx);
			this.PangoLayout.FontDescription = new Pango.FontDescription();
			this.ReferenceDpi = MonoCairoGraphicCompiler.DPI;
		}
		
		public override void Dispose()
		{
			((IDisposable) this.Context.Target).Dispose();                                      
        	((IDisposable) this.Context).Dispose();
		}
		
		#region implemented abstract members of Hiperscan.GtkExtensions.Compiler.CompileGraphicsTo
		
		
		public override void SetTranslateTransform (GraphicsTranslate translate)
		{
			this.Context.Translate(translate.XOffset, translate.YOffset);
		}
		
		
		public override void SetRotateTransform (GraphicsRotate rotate)
		{
			this.Context.Rotate(rotate.AngleDegree / 180.0 * Math.PI);
		}
		
		
		public override void SetScaleTransform (GraphicsScale scale)
		{
			this.Context.Scale(scale.XScale, scale.YScale);
		}
		
		public override void Clip ()
		{
			this.Context.Clip();
		}
		
		
		public override void SaveContext ()
		{
			this.Context.Save();
		}
		
		
		public override void RestoreContext ()
		{
			this.Context.Restore();
		}
		
		
		public override void SetStroke (GraphicsPen pen)
		{
			this.Context.LineWidth = pen.LineWidth;
			double alpha = this.PeekAlpha() * pen.Color.A;
			Cairo.Color color = new Cairo.Color(pen.Color.R, pen.Color.G, pen.Color.B, alpha);
			this.Context.Color = color;
		}
		
		
		public override void SetFill (GraphicsBrush brush)
		{
			if (brush is GraphicsSolidColorBrush) 
			{
				GraphicsSolidColorBrush solid = brush as GraphicsSolidColorBrush;
				double alpha = this.PeekAlpha () * solid.Color.A;
				Cairo.Color color = new Cairo.Color (solid.Color.R, solid.Color.G, solid.Color.B, alpha);
				this.Context.Color = color;
			}
			else if (brush is GraphicsLinearGradientenBrush) {
				GraphicsLinearGradientenBrush linear = brush as GraphicsLinearGradientenBrush;

				
				Cairo.LinearGradient lgb = new Cairo.LinearGradient(linear.BoundingBox.X + linear.BoundingBox.Width * linear.Start.X, 
				                                                    linear.BoundingBox.Y + linear.BoundingBox.Height * linear.Start.Y,
				                                                    linear.BoundingBox.X + linear.BoundingBox.Width * linear.End.X,
				                                                    linear.BoundingBox.Y + linear.BoundingBox.Height * linear.End.Y);
				
				
				double alpha1 = this.PeekAlpha () * linear.ColorStop1.Color.A;
				Cairo.Color color1 = new Cairo.Color(linear.ColorStop1.Color.R, linear.ColorStop1.Color.G, linear.ColorStop1.Color.B, alpha1);
				lgb.AddColorStop(linear.ColorStop1.Offset, color1);
				
				double alpha2 = this.PeekAlpha () * linear.ColorStop2.Color.A;
				Cairo.Color color2 = new Cairo.Color(linear.ColorStop2.Color.R, linear.ColorStop2.Color.G, linear.ColorStop2.Color.B, alpha2);
				lgb.AddColorStop(linear.ColorStop2.Offset, color2);
				
				this.Context.Pattern = lgb;
			}
			else if (brush is GraphicsRadialGradientenBrush)
			{
				GraphicsRadialGradientenBrush radial = brush as GraphicsRadialGradientenBrush;
				double center_x = radial.BoundingBox.X + 0.5 * radial.BoundingBox.Width;;
				double center_y = radial.BoundingBox.Y + 0.5 * radial.BoundingBox.Height;
				
				double r1 = 0.5 * radial.Radius1; 
				double r2 = 0.5 * radial.Radius2;
				double scale_x = 1;
				double scale_y = 1;
				bool width_is_longer =  radial.BoundingBox.Width > radial.BoundingBox.Height;
				if (width_is_longer == true)
				{
					r1 *= radial.BoundingBox.Width;
					r2 *= radial.BoundingBox.Width;
					scale_y = radial.BoundingBox.Height / radial.BoundingBox.Width;
				}
				else
				{
					r1 *= radial.BoundingBox.Height;
					r2 *= radial.BoundingBox.Height;
					scale_x = radial.BoundingBox.Width / radial.BoundingBox.Height;
				}
				
				
				Cairo.RadialGradient rgb = new RadialGradient(0, 0, r1, 0, 0, r2);
				
				double alpha1 = this.PeekAlpha () * radial.ColorStop1.Color.A;
				Cairo.Color color1 = new Cairo.Color(radial.ColorStop1.Color.R, radial.ColorStop1.Color.G, radial.ColorStop1.Color.B, alpha1);
				rgb.AddColorStop(radial.ColorStop1.Offset, color1);
				
				double alpha2 = this.PeekAlpha () * radial.ColorStop2.Color.A;
				Cairo.Color color2 = new Cairo.Color(radial.ColorStop2.Color.R, radial.ColorStop2.Color.G, radial.ColorStop2.Color.B, alpha2);
				rgb.AddColorStop(radial.ColorStop2.Offset, color2);
				
				this.Context.Translate(center_x, center_y);
				this.Context.Scale(scale_x, scale_y);
				this.Context.Pattern = rgb;
			}
		}
		
			
		public override void DrawText (GraphicsText text)
		{
			this.PangoLayout.FontDescription.AbsoluteSize = 1024.0 * text.Font.FontSize;
			this.PangoLayout.FontDescription.Family = text.Font.FontName;
			switch(text.Font.FontWeight)
			{
			case GraphicsFontWeights.Normal:
				this.PangoLayout.FontDescription.Weight = Pango.Weight.Normal;
				break;
				
			case GraphicsFontWeights.Bold:
				this.PangoLayout.FontDescription.Weight = Pango.Weight.Bold;
				break;
			}
			this.CutText(text);
			this.PangoLayout.SetText(text.Text);
			this.SetFill(text.Brush);
			this.Context.MoveTo(text.Position.X, text.Position.Y);
			Pango.CairoHelper.ShowLayout (this.Context, this.PangoLayout);
		}
		
		
		public override GraphicsSize MeasureText (GraphicsText text)
		{
			return MeasureGraphicsText(text);
		}
		

		public override void DrawImage (GraphicsImage image)
		{
			this.SaveContext ();
			this.Context.Translate(image.Coordinates.X, image.Coordinates.Y);
			this.Context.Scale(image.Coordinates.Width / image.ImageWidth,
			                   image.Coordinates.Height / image.ImageHeight);
			this.Context.SetSource(image.ImageSurface);
			this.Context.Paint ();
			this.RestoreContext ();
		}
		
		
		public override void NewPath ()
		{
			this.Context.NewPath();
		}
		
		
		public override void ClosePath ()
		{
			this.Context.ClosePath();
		}
	
		
		public override void DrawLine (GraphicsShapeLine line)
		{
			this.Context.MoveTo(line.XStart, line.YStart);
			this.Context.LineTo(line.XEnd, line.YEnd);
		}
		
		
		public override void DrawRectangle (GraphicsShapeRectangle rectangle)
		{
			Cairo.Rectangle cairo_rect = new Rectangle(rectangle.Coordinates.X, rectangle.Coordinates.Y,
			                                           rectangle.Coordinates.Width, rectangle.Coordinates.Height);
			this.Context.Rectangle(cairo_rect);
		}
		
		public override void DrawCircle (GraphicsShapeCircle circle)
		{
			this.Context.Arc(circle.X, circle.Y, circle.Radius, 0, 2 * Math.PI);
		}
		
		public override void DrawArc (GraphicsShapeArc arc)
		{
			double arc1 = arc.StartArcInDegree * Math.PI / 180;
			double arc2 = arc.EndArcInDegree * Math.PI / 180;
			this.Context.Arc(arc.Position.X, arc.Position.Y, arc.Radius, arc1, arc2);
		}
		
		public override void DrawPolygone (GraphicsShapePolygone polygon)
		{
			if(polygon.Points.Length > 0)
				this.Context.MoveTo(polygon.Points[0].X, polygon.Points[0].Y);
					
			for (int i = 1; i < polygon.Points.Length; i++)
				this.Context.LineTo(polygon.Points[i].X, polygon.Points[i].Y);
					
			this.Context.ClosePath();
		}
		
		public override void Stroke ()
		{
			this.Context.Stroke();
		}
		
		public override void StrokePreverse ()
		{
			this.Context.StrokePreserve();
		}
		
		public override void Fill ()
		{
			this.Context.Fill();
		}
		
		#endregion
	}
}

