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

        public GraphicsSize MeasureText(string text, GraphicsFont font)
        {
            GraphicsText item = new GraphicsText();
            item.Text = text;
            item.Font = font;

            return this.MeasureText(item);
        }

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