using System;
using WellspringCS;

namespace MoonWorks.Graphics.Font
{
	public class TextBatch
	{
		private GraphicsDevice GraphicsDevice { get; }
		public IntPtr Handle { get; }

		public Buffer VertexBuffer { get; protected set; } = null;
		public Buffer IndexBuffer { get; protected set; } = null;
		public Texture Texture { get; protected set; }
		public uint PrimitiveCount { get; protected set; }

		private byte[] StringBytes;

		public TextBatch(GraphicsDevice graphicsDevice)
		{
			GraphicsDevice = graphicsDevice;
			Handle = Wellspring.Wellspring_CreateTextBatch();
			StringBytes = new byte[128];
		}

		public void Start(Packer packer)
		{
			Wellspring.Wellspring_StartTextBatch(Handle, packer.Handle);
			Texture = packer.Texture;
			PrimitiveCount = 0;
		}

		public unsafe void Draw(
			string text,
			float x,
			float y,
			float depth,
			Color color,
			HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left,
			VerticalAlignment verticalAlignment = VerticalAlignment.Baseline
		) {
			var byteCount = System.Text.Encoding.UTF8.GetByteCount(text);

			if (StringBytes.Length < byteCount)
			{
				System.Array.Resize(ref StringBytes, byteCount);
			}

			fixed (char* chars = text)
			fixed (byte* bytes = StringBytes)
			{
				System.Text.Encoding.UTF8.GetBytes(chars, text.Length, bytes, byteCount);

				var result = Wellspring.Wellspring_Draw(
					Handle,
					x,
					y,
					depth,
					new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A },
					(Wellspring.HorizontalAlignment) horizontalAlignment,
					(Wellspring.VerticalAlignment) verticalAlignment,
					(IntPtr) bytes,
					(uint) byteCount
				);

				if (result == 0)
				{
					throw new System.ArgumentException("Could not decode string!");
				}
			}
		}

		// Call this after you have made all the Draw calls you want.
		public unsafe void UploadBufferData(CommandBuffer commandBuffer)
		{
			Wellspring.Wellspring_GetBufferData(
				Handle,
				out uint vertexCount,
				out IntPtr vertexDataPointer,
				out uint vertexDataLengthInBytes,
				out IntPtr indexDataPointer,
				out uint indexDataLengthInBytes
			);

			if (VertexBuffer == null)
			{
				VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
			}
			else if (VertexBuffer.Size < vertexDataLengthInBytes)
			{
				VertexBuffer.Dispose();
				VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
			}

			if (IndexBuffer == null)
			{
				IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, indexDataLengthInBytes);
			}
			else if (IndexBuffer.Size < indexDataLengthInBytes)
			{
				IndexBuffer.Dispose();
				IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, indexDataLengthInBytes);
			}

			if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
			{
				commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes);
				commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes);
			}

			PrimitiveCount = vertexCount / 2; // FIXME: is this jank?
		}
	}
}
