using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using iTextSharp.text.pdf;
using iTextSharp.text;
using Itenso.Rtf;
using Itenso.Rtf.Parser;
using Itenso.Rtf.Interpreter;
using Itenso.Rtf.Support;
using Volian.Controls.Library;
using VEPROMS.CSLA.Library;
namespace Volian.Print.Library
{
	public abstract partial class vlnPrintObject
	{
		// Used for debugging:
		private static int _UniqueDebugId = 0;
		public static int UniqueDebugId
		{
			get { return _UniqueDebugId++; }
			set { _UniqueDebugId = value; }
		}
		private int _DebugId = UniqueDebugId;
		public int DebugId
		{
			get { return _DebugId; }
			set { _DebugId = value; }
		}
		protected static float _WidthAdjust = 1;  // 1 Point - adjusted to match 16-bit line wrapping. For editting it's 3.
		protected static float _WidthAdjustBox = 6;
		private static float _SixLinesPerInch = 12;   // twips
		public static float SixLinesPerInch
		{
			get { return vlnPrintObject._SixLinesPerInch; }
			set { vlnPrintObject._SixLinesPerInch = value; }
		}
		protected static float _SevenLinesPerInch = 10.1F;//72F / 7F;
		protected float _XOffset;
		public float XOffset
		{
			get { return _XOffset; }
			set {_XOffset = value; }
		}
		protected float _YOffset;
		public float YOffset
		{
			get { return _YOffset; }
			set {	_YOffset = value; }
		}
		
		protected vlnParagraph _MyParent;
		public vlnParagraph MyParent
		{
			get { return _MyParent; }
			set 
			{
				_MyParent = value;
				if (_MyParent != null)
				{
				}
			}
		}
		private string _ImageText;  // ro definition, value part of #Link in case of image/figure
		public string ImageText
		{
			get { return _ImageText; }
			set { _ImageText = value; }
		}
		private string _Rtf;   // may become iTextSharp paragraph
		public string Rtf
		{
			get { return _Rtf; }
			set { _Rtf = value; }
		}
		iTextSharp.text.Paragraph _IParagraph;
		public iTextSharp.text.Paragraph IParagraph
		{
			get 
			{
				if (_IParagraph == null)
				{
					_IParagraph = RtfToParagraph(Rtf);
				}
				return _IParagraph; 
			}
			set { _IParagraph = value; }
		}
		private float _Width;
		public float Width
		{
			get { return _Width; }
			set { _Width = value; }
		}
		protected float _Height;
		public float Height
		{
			get 
			{
				if (_Height == 0) 
					_Height = GetParagraphHeight(MyContentByte, IParagraph, Width);
				return _Height; 
			}
			set { _Height = value; }
		}
		public float GetParagraphHeight(PdfContentByte cb, Paragraph iParagraph, float width)
		{
			return GetParagraphHeight(cb, iParagraph, width, true);
		}
		public float GetParagraphHeight(PdfContentByte cb, Paragraph iParagraph, float width, bool throwException)
		{
			VlnSvgPageHelper ph = null;
			if(cb != null) ph = cb.PdfWriter.PageEvent as VlnSvgPageHelper;
			float heightAll = GetHeight(cb, iParagraph, width, throwException);
			if (ph != null && ph.MyPromsPrinter.DebugOutput && !MyPageHelper.Back32BitPROMS)
			{
				vlnParagraph myParagraph = this as vlnParagraph;
				if (myParagraph != null)
				{
					//if (myParagraph.MyItemInfo.FormatStepData != null && myParagraph.MyItemInfo.FormatStepData.Font.Family == "Prestige Elite Tall")
					if (!myParagraph.MyItemInfo.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.WrapSameAsEdit)
					{
						Chunk chk = RemoveLastCharacter(iParagraph);
						float heightAllButOne = GetHeight(cb, iParagraph, width, throwException);
						if (heightAll != heightAllButOne)
							return heightAllButOne;
						if (chk != null)
							RestoreLastCharacter(iParagraph, chk);
					}
				}
			}
			return heightAll;
		}
		private static void RestoreLastCharacter(Paragraph iParagraph, Chunk chk)
		{
			iParagraph.RemoveAt(iParagraph.Count - 1);
			iParagraph.Add(chk);
		}
		private static Chunk RemoveLastCharacter(Paragraph iParagraph)
		{
			if (iParagraph.Count == 0)
				return null;
			object obj = iParagraph[iParagraph.Count-1];
			Chunk chk = obj as Chunk;
			if (chk == null)
				return null;
			string s = chk.Content;
			if (s.Length > 1 || iParagraph.Count > 1) // don't remove last character if it's the only one
			{
				iParagraph.RemoveAt(iParagraph.Count - 1);
				if (s.Length > 0)
				{
					if (s == "\xA0") // If this is a space at the end put it back
					{
						iParagraph.Add(chk);
						return null;
					}
					else
					{
				s = s.Substring(0, s.Length - 1);
				iParagraph.Add(new Chunk(s, chk.Font));
			}
				}
			}
			return chk;
		}
		private static float GetHeight(PdfContentByte cb, Paragraph iParagraph, float width, bool throwException)
		{
			ColumnText myColumnText = new ColumnText(cb);
			myColumnText.SetSimpleColumn(0, 792F, width, 0); // Bottom margin
			myColumnText.AddElement(iParagraph);
			//myColumnText.UseAscender = true;// Adjusts to the top of the text box.
			int status = myColumnText.Go(true); // Check to see if it will fit on the page.
			if (ColumnText.HasMoreText(status) && throwException)
				//throw (new Exception("Paragraph longer than a page"));
				Console.WriteLine("Paragraph longer than a page");
			return 792F - myColumnText.YLine; // This gives the height of the Paragraph
		}
		public float GetTableWidth(PdfContentByte cb, Paragraph iParagraph, float? maxWidth)
		{
			int iWidth = (int)(maxWidth ?? 72 * 8.5F);
			float h = GetParagraphHeight(cb, iParagraph, iWidth);
			int iWidthMax = iWidth;		// maximum width in Characters
			int iDelta = iWidth / 2;
			iWidth = iWidth / 2;
			while (iDelta > 0)
			{
				float h2 = GetParagraphHeight(cb, iParagraph, iWidth,false);
				iDelta = iDelta / 2;
				if (h2 == h) iWidthMax = iWidth;
				iWidth +=  (h2>h ? 1: -1) * iDelta;
			}
			return (float) iWidthMax;
		}
		private PdfContentByte _MyContentByte;
		public PdfContentByte MyContentByte
		{
			get { return _MyContentByte; }
			set { _MyContentByte = value; }
		}
		private VlnSvgPageHelper _MyPageHelper;
		public VlnSvgPageHelper MyPageHelper
		{
			get 
			{
				if(_MyPageHelper == null)
					_MyPageHelper = MyContentByte.PdfWriter.PageEvent as VlnSvgPageHelper;
				return _MyPageHelper; 
			}
			set { _MyPageHelper = value; }
		}
		public vlnPrintObject()
		{
		}
		/// 
		/// No conversion necessary: twips -> twips
		/// 
		/// 
		/// 
		public static int ToInt(int value)
		{
			return value;
		}
		/// 
		/// No conversion necessary: int? twips -> int twips
		/// 
		/// 
		/// 
		public static int ToInt(int? value)
		{
			return ToInt((int)value);
		}
		/// 
		/// No conversion necessary: string twips -> int twips
		/// 
		/// 
		/// 
		public static int ToInt(string value)
		{
			return ToInt((int)Convert.ToSingle(value));
		}
		/// 
		/// No conversion necessary: value from a list in a string, twips -> int twips
		/// 
		/// 
		/// 
		public static int ToInt(string value, int i)
		{
			string s = (value.Contains(",")) ? value.Split(",".ToCharArray())[i] : value;
			return ToInt(s);
		}
		public static string GetRtf(string text, VE_Font vFont)
		{
			StringBuilder rtfSB = new StringBuilder();
			//DisplayText vlntxt = new DisplayText(text.TrimStart(" ".ToCharArray()), vFont, false);
			DisplayText vlntxt = new DisplayText(text, vFont, false);
			//rtfSB.Append(AddFontTable(vlntxt.TextFont.WindowsFont));
			rtfSB.Append(AddFontTable(vFont.WindowsFont));
			rtfSB.Append(vlntxt.StartText);
			rtfSB.Append("}");
			return rtfSB.ToString();
		}
		public static string AddFontTable(System.Drawing.Font font)
		{
			StringBuilder rtfSB = new StringBuilder();
			StringBuilder sbbeg = new StringBuilder();
			StringBuilder sbend = new StringBuilder();
			if (font.Bold)
			{
				sbbeg.Append(@"\b");
				sbend.Append(@"\b0");
			}
			if (font.Underline)
			{
				sbbeg.Append(@"\ul");
				sbend.Insert(0, @"\ulnone");
			}
			if (font.Italic)
			{
				sbbeg.Append(@"\i");
				sbend.Insert(0, @"\i0");
			}
			rtfSB.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset2 " + font.FontFamily.Name + @";}"); //}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}";
			if (!FontIsFixed(font))
				rtfSB.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}{\colortbl ;\red255\green0\blue0;}");
			else
				rtfSB.Append(@"{\f1\fnil\fcharset0 VESymbFix;}}{\colortbl ;\red255\green0\blue0;}");
			rtfSB.Append("\r\n");
			// use styles to construct rtf commands to insert into next line (where \b, etc is)
			rtfSB.Append(@"\viewkind4\uc1\pard\sl-240\slmult0" + sbbeg.ToString() + @"\fs" + Convert.ToInt32(font.SizeInPoints * 2).ToString() + @" "); // \f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}";
			return rtfSB.ToString();
		}
		private static bool FontIsFixed(System.Drawing.Font font)
		{
			iTextSharp.text.Font iFont = Volian.Svg.Library.VolianPdf.GetFont(font);
			float fW = iFont.BaseFont.GetWidthPointKerned("W", 12);
			float fE = iFont.BaseFont.GetWidthPointKerned("!", 12);
			return fW == fE;
		}
		public static iTextSharp.text.Paragraph RtfToParagraph(string rtf)
		{
			IRtfDocument rtfDoc = RtfInterpreterTool.BuildDoc(rtf);
			Rtf2iTextSharp rtf2IText = new Rtf2iTextSharp(rtfDoc);
			iTextSharp.text.Paragraph para = rtf2IText.Convert();
			para.SetLeading(_SixLinesPerInch, 0);
			if (rtf.Contains("\x05"))
			{
				// if there is a hanging indent, the iTextSharp paragraph properties must be set
				// to print the indent.  Replace the indent 'token' with a non-used symbol, this will
				// create a chunk with that symbol.  Then loop through all of the chunks until we find
				// this symbol, adding up the widths to that point.  This width is the value that
				// needs to be used to set the indent.
				// Notes:
				//	A hard return will reset the chkW (indent width) back to zero.
				//  We jump out of the processing loop after the first indent token is found and ignor any other ones
				IRtfDocument rtfDoc2 = RtfInterpreterTool.BuildDoc(rtf.Replace("\x05", @"\f1 \u9999? \f0 "));
				Rtf2iTextSharp rtf2IText2 = new Rtf2iTextSharp(rtfDoc2);
				iTextSharp.text.Paragraph para2 = rtf2IText2.Convert();
				float chkW = 0;
				foreach (Chunk chk in para2.Chunks)
				{
					if (chk.Content[0] == 9999) break;
					if (chk.Content.Contains("\n")) chkW = 0; //hard return - reset chkW (indent start)
					chkW += chk.GetWidthPoint();
				}
				para.IndentationLeft = chkW;
				para.FirstLineIndent = -chkW;
			}
			return para;
		}
		public abstract float ToPdf(PdfContentByte cb, float yPageStart, ref float yTopMargin, ref float yBottomMargin);
		protected float CalculateYOffset(float yPageStart, float yTopMargin)
		{
			float yLocation = yPageStart - YOffset;
			if (MyPageHelper.YMultiplier != 1)
			{
				yLocation = -1 + yTopMargin - (yTopMargin - yLocation) * MyPageHelper.YMultiplier;
				if (Rtf != null)
					IParagraph.Leading = _SevenLinesPerInch;
			}
			return yLocation;
		}
		protected float CalculateYLocation(float yLocation, float yTopMargin)
		{
			if (MyPageHelper.YMultiplier != 1)
				yLocation = -1 + yTopMargin - (yTopMargin - yLocation) * MyPageHelper.YMultiplier;
			return yLocation;
		}
		public void DebugPdf(PdfContentByte cb, float left, float top)
		{
			VlnSvgPageHelper _MyPageHelper = cb.PdfWriter.PageEvent as VlnSvgPageHelper;
			PdfLayer debugLayer = _MyPageHelper == null ? null : _MyPageHelper.DebugLayer;
			if (debugLayer == null) return;
			cb.SaveState();
			cb.BeginLayer(debugLayer);
			ColumnText ct = new ColumnText(cb);
			ct.SetSimpleColumn(left, top, left+50, top - 50);
			iTextSharp.text.Font font = FontFactory.GetFont("Arial", 2);
			Chunk chk = new Chunk(DebugId.ToString(), font);
			Phrase ph = new Phrase(chk);
			ct.AddElement(ph);
			cb.SetColorFill(new iTextSharp.text.Color(PrintOverride.OverrideDebugColor(System.Drawing.Color.Gray)));
			ct.Go();
			cb.EndLayer();
			cb.RestoreState();
		}
		public virtual float YBottom
		{	get { return YOffset + Height;}	}
	}
	public partial class vlnPrintObjects : List
	{
		public float ToPdf(PdfContentByte cb, float yPageStart, ref float yTopMargin, ref float yBottomMargin)
		{
			foreach (vlnPrintObject part in this)
			{
				yPageStart = part.ToPdf(cb, yPageStart, ref yTopMargin, ref yBottomMargin);
			}
			return yPageStart;
		}
	}
	public enum PartLocation : int
	{
		None = 0,			// Non-printable
		Below = 1,			// RNO Separator
		Above = 2,			// Tab Headers, Separator?
		Right = 3,			// Change Bars
		Left = 4,			// Tabs, Checkoffs? (maybe part of tab)
		Container = 5		// Box
	};
	public enum ChildLocation : int
	{
		None = 0,
		Below = 1,
		Above = 2,
		Right = 3,			// RNO
		Left = 4
	}
	
}