using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;

namespace SDL2Idiomatic
{
	public struct GlyphMetrics
	{
		public int MinX, MaxX, MinY, MaxY, Advance;

		public GlyphMetrics(int minX, int maxX, int minY, int maxY, int advance)
		{
			MinX = minX;
			MaxX = maxX;
			MinY = minY;
			MaxY = maxY;
			Advance = advance;
		}
	}

	public class Truetype: SDLFontObject
	{
		[Flags]
		public enum FontStyle: int
		{
			Normal = 0x00,
			Bold = 0x01,
			Italic = 0x02,
			Underline = 0x04,
			Strikethrough =	0x08
		}

		public struct FontMetrics
		{
			public int Height, Ascent, Descent, LineSkip;

			public FontMetrics(int height, int ascent, int descent, int lineSkip)
			{
				Height = height;
				Ascent = ascent;
				Descent = descent;
				LineSkip = lineSkip;
			}
		}

		private FontMetrics metrics;

		public FontMetrics Metrics
		{
			get { return metrics; }
		}

		private bool Monospace
		{
			get { return (0 != SDL2.SDL_ttf.TTF_FontFaceIsFixedWidth(native)); }
		}

		public FontStyle Style
		{
			get { return (FontStyle)SDL2.SDL_ttf.TTF_GetFontStyle(native); }
			set { SDL2.SDL_ttf.TTF_SetFontStyle(native, (int)value); }
		}

		private FontMetrics GetFontMetrics()
		{
			return new FontMetrics(SDL2.SDL_ttf.TTF_FontHeight(native),
				SDL2.SDL_ttf.TTF_FontAscent(native),
				SDL2.SDL_ttf.TTF_FontDescent(native),
				SDL2.SDL_ttf.TTF_FontLineSkip(native));
		}

		public int OutlineWidth
		{
			get { return SDL2.SDL_ttf.TTF_GetFontOutline(native); }
			set
			{
				if (SDL2.SDL_ttf.TTF_GetFontOutline(native) != value) SDL2.SDL_ttf.TTF_SetFontOutline(native, value);
			}
		}

		private IntPtr native, bufferAddress;

		public IntPtr Handle
		{
			get { return native; }
		}

		public Size RenderedSize(string text)
		{
			int width, dummy;

			int retval = SDL2.SDL_ttf.TTF_SizeUNICODE(native, text, out width, out dummy);
			if (retval != 0) throw new SDLException("Error getting printed size of text");

			return new Size(width, metrics.Height);
		}

		public enum RenderQuality
		{
			Fast,
			AAWithBackground,
			AAWithAlpha
		}

		public Texture RenderToTexture(Renderer renderer, string text, RenderQuality quality, Color foreground, Color backgrond)
		{
			IntPtr surface = IntPtr.Zero;

			switch (quality)
			{
				case RenderQuality.Fast:
					surface = SDL2.SDL_ttf.TTF_RenderUNICODE_Solid(native, text, SDL2.Supplementary.ConvertColor(foreground));
					break;
				case RenderQuality.AAWithBackground:
					surface = SDL2.SDL_ttf.TTF_RenderUNICODE_Shaded(native, text, SDL2.Supplementary.ConvertColor(foreground), SDL2.Supplementary.ConvertColor(backgrond));
					break;
				case RenderQuality.AAWithAlpha:
					surface = SDL2.SDL_ttf.TTF_RenderUNICODE_Blended(native, text, SDL2.Supplementary.ConvertColor(foreground));
					break;
			}

			if (surface == IntPtr.Zero) throw new SDLException("No surface created by rendering font");

			return new Texture(renderer, new Surface(surface));
		}

		public void Render(Renderer renderer, Rectangle destination, string text, RenderQuality quality, Color foreground, Color background)
		{
			Texture tex = RenderToTexture(renderer, text, quality, foreground, background);
			tex.Draw(destination);
			tex.Free();
		}

		public bool ProvidesGlyph(char c)
		{
			return (0 != SDL2.SDL_ttf.TTF_GlyphIsProvided(native, c));
		}

		public GlyphMetrics GlyphMetrics(char c)
		{
			int minx, miny, maxx, maxy, advance;

			int retval = SDL2.SDL_ttf.TTF_GlyphMetrics(native, c, out minx, out maxx, out miny, out maxy, out advance);
			if (retval != 0) throw new SDLException("Error getting glyph metrics");

			return new GlyphMetrics(minx, maxx, miny, maxy, advance);
		}

		//		private GCHandle handle;
		//		private bool pinned = false;

		public Truetype(byte[] buffer, int pointSize)
		{
			try
			{
//				handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
//				pinned = true;
				bufferAddress = Marshal.AllocHGlobal(buffer.Length);
				Marshal.Copy(buffer, 0, bufferAddress, buffer.Length);

				IntPtr rwop = SDL2.Supplementary.CreateConstMemRWop(bufferAddress, buffer.Length);
				if (rwop == IntPtr.Zero)
				{
					throw new SDLException("Error creating RWOp");
				}

				native = SDL2.Supplementary.LoadFontUsingRWop(rwop, 0, pointSize);
				if (native == IntPtr.Zero) throw new SDLException("Error loading font");
			}
			catch
			{
//				handle.Free();
//				pinned  = false;
				Marshal.FreeHGlobal(bufferAddress);
				throw;
			}

			CommonSetup();
		}

		public Truetype(string filename, int pointSize)
		{
			if (!File.Exists(filename)) throw new ArgumentException(string.Format("No such file: {0}", filename));
			native = SDL2.SDL_ttf.TTF_OpenFont(filename, pointSize);
			if (native == IntPtr.Zero) throw new SDLException("Error loading font");

			CommonSetup();
		}

		private void CommonSetup()
		{
			metrics = GetFontMetrics();
			SDL2.SDL_ttf.TTF_SetFontHinting(native, SDL2.SDL_ttf.TTF_HINTING_LIGHT);
		}

		public override void Free()
		{
			SDL2.SDL_ttf.TTF_CloseFont(native);
			Marshal.FreeHGlobal(bufferAddress);

//			if (pinned)
//			{
//				handle.Free();
//				pinned = false;
//			}

			base.Free();
		}
	}
}

