using System.Threading.Tasks;
using Mox.Shared.Renderer;
using SharpDX;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using SharpDX.Windows;
using SharpDX.WIC;
using AlphaMode2D = SharpDX.Direct2D1.AlphaMode;
using BitmapInterpolationMode = SharpDX.Direct2D1.BitmapInterpolationMode;
using Factory2D = SharpDX.Direct2D1.Factory;
using FeatureLevel = SharpDX.Direct2D1.FeatureLevel;
using PixelFormat = SharpDX.Direct2D1.PixelFormat;
using TextAntialiasMode2D = SharpDX.Direct2D1.TextAntialiasMode;
using DirectWrite = SharpDX.DirectWrite;
using Mox.Shared.Math;
using Mox.Shared;

namespace Mox.SharpDX.Renderer
{
	internal class DirectXRenderer : RendererBase
	{
		public DirectXRenderer(RenderForm _form)
		{
			m_fDpi = _form.CreateGraphics().DpiX;
			m_Factory2D = new Factory2D();
			m_FactoryDWrite = new DirectWrite.Factory();

			m_WicFactory = new ImagingFactory();

			var windowProperties = new HwndRenderTargetProperties()
			{
				Hwnd = _form.Handle,
				PixelSize = new Size2(_form.ClientSize.Width, _form.ClientSize.Height),
				PresentOptions = PresentOptions.None
			};

			var renderProperties = new RenderTargetProperties()
			{
				PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode2D.Premultiplied),
				DpiX = m_fDpi,
				DpiY = m_fDpi,
				Type = RenderTargetType.Hardware,
				Usage = RenderTargetUsage.None,
				MinLevel = FeatureLevel.Level_9
			};

			m_RenderTarget2D = new WindowRenderTarget(m_Factory2D, renderProperties, windowProperties)
			{
				AntialiasMode = AntialiasMode.PerPrimitive,
				TextAntialiasMode = TextAntialiasMode2D.Grayscale
			};

			SetTextStyle("Arial", 30);
			SetTextColor(new Color(0, 0, 0, 1));

			m_ClientRectangle = new RawRectangleF(0, 0, 0, 0);

			Instance = this;
		}

		public override void BeginDraw()
		{
			m_RenderTarget2D.BeginDraw();
		}

		public override async Task<ImageBase> CreateImage(string _sPath)
		{
			return new DirectXImage(m_RenderTarget2D, m_WicFactory, await IOHelper.GetBytes(_sPath));
		}

		public override void FillRect(Rect _rect, Color _color)
		{
			var rect = new RawRectangleF(_rect.Left, _rect.Top, _rect.Left + _rect.Width, _rect.Top + _rect.Height);
			var brush = new SolidColorBrush(m_RenderTarget2D, new RawColor4(_color.R, _color.G, _color.B, _color.A));
			m_RenderTarget2D.FillRectangle(rect, brush);
			brush.Dispose();
		}

		public override void DrawImage(ImageBase _image, Rect _source, Rect _target)
		{
			var bitmap = ((DirectXImage) _image).Image;

			var source = new RawRectangleF(_source.Left, _source.Top, _source.Left + _source.Width,
			                               _source.Top + _source.Height);

			var target = new RawRectangleF(_target.Left, _target.Top, _target.Left + _target.Width,
			                               _target.Top + _target.Height);

			m_RenderTarget2D.DrawBitmap(bitmap, target, 1.0f, BitmapInterpolationMode.Linear, source);
		}

		public override void EndDraw()
		{
			m_RenderTarget2D.EndDraw();
		}

		public override void Clear(Color _color)
		{
			m_RenderTarget2D.Clear(new RawColor4(_color.R, _color.G, _color.B, _color.A));
		}

		public override void SetTextColor(Color _color)
		{
			m_Brush?.Dispose();
			m_Brush = new SolidColorBrush(m_RenderTarget2D, new RawColor4(_color.R, _color.G, _color.B, _color.A));
		}

		public override void SetTextStyle(string _sFont, int _iSize)
		{
			m_TextFormat?.Dispose();

			m_TextFormat = new TextFormat(m_FactoryDWrite, _sFont, _iSize * m_fDpi / 72)
			{
				TextAlignment = TextAlignment.Center,
				ParagraphAlignment = ParagraphAlignment.Center,
				WordWrapping = WordWrapping.NoWrap
			};
		}

		public override void DrawText(string _sText, int _iX, int _iY)
		{
			m_ClientRectangle.Left = _iX;
			m_ClientRectangle.Right = _iX;
			m_ClientRectangle.Top = _iY;
			m_ClientRectangle.Bottom = _iY;
			m_RenderTarget2D.DrawText(_sText, m_TextFormat, m_ClientRectangle, m_Brush);
		}

		public override Rect MeasureText(string _sText)
		{
			var layout = new TextLayout(m_FactoryDWrite, _sText, m_TextFormat, float.MaxValue, float.MaxValue);

			return new Rect(0, 0, (int) layout.Metrics.Width, (int) layout.Metrics.Height);
		}

		public override void Dispose()
		{
			m_TextFormat.Dispose();
			m_RenderTarget2D.Dispose();
			m_Brush.Dispose();
			m_Factory2D.Dispose();
			m_FactoryDWrite.Dispose();
			m_WicFactory.Dispose();
		}

		private readonly Factory2D m_Factory2D;
		private readonly DirectWrite.Factory m_FactoryDWrite;
		private readonly float m_fDpi;
		private readonly WindowRenderTarget m_RenderTarget2D;
		private readonly ImagingFactory m_WicFactory;

		private RawRectangleF m_ClientRectangle;
		private TextFormat m_TextFormat;
		private SolidColorBrush m_Brush;
	}
}
