
#region ================== Copyright (c) 2007 Pascal vd Heiden

/*
 * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
 * This program is released under GNU General Public License
 * 
 * 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.
 * 
 */

#endregion

#region ================== Namespaces

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Drawing;
using System.ComponentModel;
using CodeImp.DoomBuilder.Map;
using SlimDX.Direct3D9;
using SlimDX;
using CodeImp.DoomBuilder.Geometry;
using System.Drawing.Imaging;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Editing;

using Configuration = CodeImp.DoomBuilder.IO.Configuration;

#endregion

namespace CodeImp.DoomBuilder.Rendering
{
	internal class TextFont : IDisposable
	{
		#region ================== Structures
		
		// This structure defines character properties
		private struct FontCharacter
		{
			// Variables
			public float width;
			public float height;
			public float u1;
			public float v1;
			public float u2;
			public float v2;
		}

		#endregion
		
		#region ================== Constants

		// Font resource name
		private const string FONT_RESOURCE = "Font.cfg";
		
		// Spacing adjustments
		private const float ADJUST_SPACING = -0.08f;
		
		#endregion

		#region ================== Variables

		// Characters
		private FontCharacter[] characters;
		
		// Disposing
		private bool isdisposed = false;

		#endregion

		#region ================== Properties

		public bool IsDisposed { get { return isdisposed; } }

		#endregion

		#region ================== Constructor / Disposer

		// Constructor
		internal TextFont()
		{
			Configuration cfg;
			Stream fontdata;
			StreamReader fontreader;
			string[] resnames;
			
			// Initialize
			characters = new FontCharacter[256];

			// Make chars configuration
			cfg = new Configuration();
			
			// Find a resource named Font.cfg
			resnames = General.ThisAssembly.GetManifestResourceNames();
			foreach(string rn in resnames)
			{
				// Found it?
				if(rn.EndsWith(FONT_RESOURCE, StringComparison.InvariantCultureIgnoreCase))
				{
					// Get a stream from the resource
					fontdata = General.ThisAssembly.GetManifestResourceStream(rn);
					fontreader = new StreamReader(fontdata, Encoding.ASCII);

					// Load configuration from stream
					cfg.InputConfiguration(fontreader.ReadToEnd());

					// Done
					fontreader.Dispose();
					fontdata.Dispose();
					break;
				}
			}
			
			// Get the charset from configuration
			IDictionary cfgchars = cfg.ReadSetting("chars", new Hashtable());

			// Go for all defined chars
			foreach(DictionaryEntry item in cfgchars)
			{
				// Get the character Hashtable
				IDictionary chr = (IDictionary)item.Value;
				int i = Convert.ToInt32(item.Key);

				// This is ancient code of mine.
				// The charater sizes were based on 800x600 resolution.
				characters[i].width = (float)(int)chr["width"] / 40f;
				characters[i].height = (float)(int)chr["height"] / 30f;
				characters[i].u1 = (float)chr["u1"];
				characters[i].v1 = (float)chr["v1"];
				characters[i].u2 = (float)chr["u2"];
				characters[i].v2 = (float)chr["v2"];
			}
			
			// We have no destructor
			GC.SuppressFinalize(this);
		}

		// Diposer
		public void Dispose()
		{
			// Not already disposed?
			if(!isdisposed)
			{
				// Done
				isdisposed = true;
			}
		}
		
		#endregion
		
		#region ================== Methods

		// This sets up vertices for a specific character
		// also advances vertsoffset and textx
		public void SetupVertices(DataStream stream, byte c, float scale, int color,
						ref float textx, float texty, float textheight, float offsetv)
		{
			FlatVertex vert = new FlatVertex();
			FontCharacter cinfo;
			float cwidth;
			
			// Get the character information
			cinfo = characters[c];
			cwidth = cinfo.width * scale;

			// Create lefttop vertex
			vert.c = color;
			vert.u = cinfo.u1;
			vert.v = cinfo.v1 * 0.5f + offsetv;
			vert.x = textx;
			vert.y = texty;
			stream.Write<FlatVertex>(vert);

			// Create leftbottom vertex
			vert.c = color;
			vert.u = cinfo.u1;
			vert.v = cinfo.v2 * 0.5f + offsetv;
			vert.x = textx;
			vert.y = texty + textheight;
			stream.Write<FlatVertex>(vert);

			// Create righttop vertex
			vert.c = color;
			vert.u = cinfo.u2;
			vert.v = cinfo.v1 * 0.5f + offsetv;
			vert.x = textx + cwidth;
			vert.y = texty;
			stream.Write<FlatVertex>(vert);

			// Create leftbottom vertex
			vert.c = color;
			vert.u = cinfo.u1;
			vert.v = cinfo.v2 * 0.5f + offsetv;
			vert.x = textx;
			vert.y = texty + textheight;
			stream.Write<FlatVertex>(vert);

			// Create righttop vertex
			vert.c = color;
			vert.u = cinfo.u2;
			vert.v = cinfo.v1 * 0.5f + offsetv;
			vert.x = textx + cwidth;
			vert.y = texty;
			stream.Write<FlatVertex>(vert);

			// Create rightbottom vertex
			vert.c = color;
			vert.u = cinfo.u2;
			vert.v = cinfo.v2 * 0.5f + offsetv;
			vert.x = textx + cwidth;
			vert.y = texty + textheight;
			stream.Write<FlatVertex>(vert);

			textx += (cwidth + (ADJUST_SPACING * scale));
		}
		
		// This checks if the given character exists in the charset
		public bool Contains(char c)
		{
			// Convert character to ASCII
			byte[] keybyte = Encoding.ASCII.GetBytes(c.ToString());
			return Contains(keybyte[0]);
		}
		
		// This checks if the given character exists in the charset
		public bool Contains(byte b)
		{
			// Check if the character has been set
			return ((characters[b].width > 0.000000001f) ||
			        (characters[b].height > 0.000000001f));
		}

		// This calculates the size of a text string at a given scale
		public SizeF GetTextSize(string text, float scale)
		{
			// Size items
			float sizex = 0, sizey = 0;

			// Get the ASCII bytes for the text
			byte[] btext = Encoding.ASCII.GetBytes(text);

			// Go for all chars in text to calculate final text size
			foreach(byte b in btext)
			{
				// Add to the final size
				sizex += characters[b].width * scale;
				sizey = characters[b].height * scale;
			}

			// Return size
			return new SizeF(sizex, sizey);
		}
		
		#endregion
	}
}
