// 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 delegate GraphicsSize MeasureTextDelegate(GraphicsText text);


	public abstract class GenericGraphicCompiler : System.IDisposable
	{
		
		public virtual void Dispose()
		{
		}
		
		protected Stack<double> StackAlpha = null;
		
		public  double PopAlpha()
		{
			return this.StackAlpha.Pop();
		}
		
		public  void PushAlpha(double alpha)
		{
			if(this.StackAlpha.Count != 0)
				alpha = alpha * this.StackAlpha.Peek();
			this.StackAlpha.Push(alpha);
		}
		
		public  double PeekAlpha()
		{
			if (this.StackAlpha.Count == 0)
				return 1;
			else
				return this.StackAlpha.Peek();
		}
		
		public double ReferenceDpi {get; protected set;}
		
		public void SetTransformations (List<GraphicsTransformation> group)
		{
			foreach(GraphicsTransformation transform in group)
			{
				if (transform is GraphicsTranslate)
					this.SetTranslateTransform(transform as GraphicsTranslate);
				else if (transform is GraphicsRotate)
					this.SetRotateTransform(transform as GraphicsRotate);
				else if (transform is GraphicsScale)
					this.SetScaleTransform(transform as GraphicsScale);
			}
		}
		
		public void SetClip(GraphicsShape shape)
		{
			if (shape == null)
				return;
			
			this.SaveContext();
			if(shape is GraphicsShapeLine)
					this.DrawLine(shape as GraphicsShapeLine);
				else if (shape is GraphicsShapeRectangle)
					this.DrawRectangle(shape as GraphicsShapeRectangle);
				else if (shape is GraphicsShapeCircle)
					this.DrawCircle(shape as GraphicsShapeCircle);
				else if (shape is GraphicsShapePolygone)
					this.DrawPolygone(shape as GraphicsShapePolygone);
				else if (shape is GraphicsShapeArc)
					this.DrawArc(shape as GraphicsShapeArc);
				else if (shape is GraphicsShapePath)
					this.DrawPath(shape as GraphicsShapePath);
			
			if (shape.IsClosed == true)
					this.ClosePath();
			
			this.Clip();
			this.RestoreContext();
		}
		

		public abstract void SetTranslateTransform(GraphicsTranslate translate);
		public abstract void SetRotateTransform(GraphicsRotate rotate);
		public abstract void SetScaleTransform(GraphicsScale scale);
		
		public abstract void Clip();
		
		public abstract void SaveContext();
		public abstract void RestoreContext();
		
		public abstract void SetStroke(GraphicsPen pen);
		public abstract void SetFill(GraphicsBrush brush);
		
		public abstract void DrawText(GraphicsText text);
		public abstract GraphicsSize MeasureText(GraphicsText text);
		protected void CutText(GraphicsText text)
		{
			if (text.MaxWidth < 0 || this.MeasureText(text).Width <= text.MaxWidth)
				return;
			
			string temp = text.Text;
			for(;;)
			{
				temp = temp.Substring(0, temp.Length - 1);
				text.Text = temp + "...";
				
				if (this.MeasureText(text).Width <= text.MaxWidth)
					break;	
				if(temp.Length == 1)
				{
					text.Text = String.Empty;
					break;
				}
			}
		}
		
		public void DrawTextParagraph (GraphicsTextParagraph paragraph)
		{		
			double current_width = 0;
			GraphicsText line = new GraphicsText();
			line.Text = String.Empty;
			line.Position.X = paragraph.Coordinates.X;
			line.MaxWidth = paragraph.Coordinates.Width;
			line.Font = paragraph.Font;
			line.Brush = paragraph.Brush;
			
			GraphicsText one_word = new GraphicsText();
			one_word.Font = paragraph.Font;
			one_word.Text = " ";
			
			GraphicsSize space_size = this.MeasureText(one_word);
			double line_feed = space_size.Height * 1.2;
			int max_lines = (int)(paragraph.Coordinates.Height / line_feed);
			if (paragraph.Coordinates.Height - max_lines * line_feed >= space_size.Height)
				max_lines++;
			int current_line = 0;

			string[] text_data = paragraph.Text.Split(new char[]{' ', '\t', '\n'}, StringSplitOptions.RemoveEmptyEntries);
			one_word.Text = String.Empty;
			
			for(int j= 0; j < text_data.Length; j++)
			{
				one_word.Text = text_data[j];
				GraphicsSize word_size = this.MeasureText(one_word);
				
				if (word_size.Width > paragraph.Coordinates.Width)
				{
					if (line.Text == String.Empty)
					{
						line.Position.Y = paragraph.Coordinates.Y + current_line * line_feed;
						line.Text = one_word.Text;
						this.DrawText(line);
						
						line.Text = String.Empty;
						current_width = 0;
						current_line ++;
					}
					else
					{
						line.Position.Y = paragraph.Coordinates.Y + current_line * line_feed;
						this.DrawText(line);
						
						line.Text = String.Empty;
						current_width = 0;
						current_line++;
						j--;	
					}				
				}				
				else if (word_size.Width + current_width > paragraph.Coordinates.Width)
				{
					line.Position.Y = paragraph.Coordinates.Y + current_line * line_feed;
					this.DrawText(line);					

					line.Text = String.Empty;
					current_line++;
					current_width = 0;
					j--;	
				}
				else
				{
					line.Text += text_data[j] + " ";
					current_width += word_size.Width + space_size.Width;
				}
			}	
			if (current_line < max_lines && line.Text != String.Empty)
			{
				line.Position.Y = paragraph.Coordinates.Y + current_line * line_feed;
				this.DrawText(line);
			}
		}
		
		
		public abstract void DrawImage(GraphicsImage image);
		
		public abstract void NewPath();
		public abstract void ClosePath();
		
		public abstract void DrawLine(GraphicsShapeLine line);
		public abstract void DrawRectangle(GraphicsShapeRectangle rectangle);
		public abstract void DrawCircle(GraphicsShapeCircle circle);
		public abstract void DrawPolygone(GraphicsShapePolygone polygon);
		public abstract void DrawArc(GraphicsShapeArc arc);
		
		public void DrawPath (GraphicsShapePath path)
		{
			if (path.Shapes.Count > 0)
			{
				foreach(GraphicsShape shape in path.Shapes)
				{
					if (shape is GraphicsShapeLine)
					{
						this.DrawLine(shape as GraphicsShapeLine);
					}
				}
			}
		}

		
		public abstract void Stroke();
		public abstract void StrokePreverse();
		public abstract void Fill();
		
	}
}