using Sandbox.ModAPI;
using System;
using System.Collections.Generic;
using VRage.Utils;
using VRageMath;
using UIFun.StringProcessor;
using UIFun.Definition;

namespace UIFun.Messagesv2
{
	public class ModHUDMessage : ModMessageBase
	{
		private enum EntityMembers : int
		{
			Origin = 10,
			Options,
			ShadowColor,
			Font,
			InitialColor
		}
		public Vector2D Origin = Vector2D.Zero;
		public Options Options = Options.None;
		public Color ShadowColor = Color.Black;
		public Color InitialColor = Color.White;
		public Vector2I WordWrapMax = Vector2I.Zero;
		public bool WordWrap = false;
		//caching
		public bool hasCache = false;
		string m_LastString = "";
		List<DrawCallValues> m_Cache = new List<DrawCallValues>();
		List<TextProcessor.Cache> m_CacheDraw = new List<TextProcessor.Cache>();
		double shadow_scale = 1.1d;


		Vector2I m_Size = Vector2I.Zero;

		uint oldHash = 0;

		FontDefinition Font;
		const string FONTNAME = "white";
		MyStringId m_font = MyStringId.GetOrCompute(FONTNAME);
		struct DrawCallValues
		{
			internal char Letter;
			internal Vector2D Position;
			internal double Scale;
			internal Vector4 ShadowColor;
			internal Vector4 TextColor;
		}
		public ModHUDMessage()
		{
			Font = FontTexture.GetFont(m_font);
        }
		public override object GetMember(int MemberEnum)
		{
			switch ((EntityMembers)MemberEnum)
			{
				case EntityMembers.Origin:
					return Origin;
				case EntityMembers.Options:
					return (byte)Options;
				case EntityMembers.ShadowColor:
					return ShadowColor;
				case EntityMembers.Font:
					return m_font.String;
				case EntityMembers.InitialColor:
					return InitialColor;
                default:
					return base.GetMember(MemberEnum);
			}
		}
		public override void SetMember(int MemberEnum, object Value)
		{
			switch ((EntityMembers)MemberEnum)
			{
				case EntityMembers.Origin:
					Origin = (Vector2D)Value;
					return;
				case EntityMembers.Options:
					Options = (Options)Value;
					return;
				case EntityMembers.ShadowColor:
					ShadowColor = (Color)Value;
					return;
				case EntityMembers.Font:
					m_font = MyStringId.GetOrCompute((string)Value);
					Font = FontTexture.GetFont(m_font);
					Flush();
					return;
				case EntityMembers.InitialColor:
					InitialColor = (Color)Value;
                    return;
                default:
					base.SetMember(MemberEnum, Value);
					return;
			}
		}

		public override void Flush()
		{
			base.Flush();
			hasCache = false;
		}

		public override Vector2D GetLength()
		{
			Vector2D Length = Vector2D.Zero;
			
			if (Font == null)
			{
				Font = FontTexture.GetFont(m_font);
				if (Font == null)
					return Length;
			}
			if (Message.Length == 0)
			{
				if(Options.HasFlag(Options.Pixel))
				{
					return new Vector2D(0, 0);
				}
				var move = WriteLetter(Vector2D.Zero, Vector2I.Zero, ' ', Color.Black, Scale,  false, false);
				Length.Y = move.Y;
				Length.Y *= -2 / (FontTexture.localscale);
				return Length;
			}

			Process();
			if(Options.HasFlag(Options.Pixel))
			{
				//Length = new Vector2D(m_Size.X * FontTexture.OneX * Scale * 0.03571428571d, m_Size.Y * FontTexture.OneY * Scale * 0.03571428571d);
				Length = new Vector2D(m_Size.X * Scale * 0.03571428571d, -m_Size.Y * Scale * 0.03571428571d);
				return Length;
			}
			Length = new Vector2D(m_Size.X * FontTexture.xPix * Scale, m_Size.Y * FontTexture.yPix * Scale);
			Length.X *= 2 / (FontTexture.localscale * FontTexture.aspectRatio);
			Length.Y *= 2 / (FontTexture.localscale);
			return Length;
		}
		public override void Draw()
		{

			if (Options.HasFlag(Options.HideHud))
			{
				if (FontTexture.hudHidden)
					return;//hud is hidden dont draw. 
			}
			if (Font == null)
			{
				Font = FontTexture.GetFont(MyStringId.GetOrCompute( FONTNAME ));
				if (Font == null)
					return;
			}
			Process();
			DrawCache();
		}
		private void Process()
		{
			Vector2I New_Size = Vector2I.Zero;
			uint newHash = 0;
			int CacheSize = 0;
			bool updated = false;
			
			newHash = TextProcessor.ProcessStringBuilder(this, Message, m_CacheDraw, InitialColor, oldHash, Font, hasCache && !FontTexture.InvalidateCache, out New_Size, out CacheSize, out updated, WordWrap, WordWrapMax);
			if (updated)
			{
				oldHash = newHash;
				m_Size = New_Size;
			}
			hasCache = true;
		}
		private void DrawCache()
		{
			Vector2D origin = Vector2D.Zero;
			var scale = Scale;
			if (Options.HasFlag( Options.Pixel))
			{
				var modulo = Origin;
				modulo.X += MyAPIGateway.Session.Camera.ViewportSize.X;
				modulo.Y += MyAPIGateway.Session.Camera.ViewportSize.Y;
				modulo.X %= MyAPIGateway.Session.Camera.ViewportSize.X;
				modulo.Y %= MyAPIGateway.Session.Camera.ViewportSize.Y;

				float xsize = (MyAPIGateway.Session.Camera.ViewportSize.X / 2f);
				float ysize = (MyAPIGateway.Session.Camera.ViewportSize.Y / 2f);
				origin.X = -1d;
				origin.Y = -1d;

				origin.X += modulo.X / xsize;
				origin.X += Offset.X / xsize;
				origin.Y += modulo.Y / ysize;
				origin.Y += Offset.Y / ysize;

				origin.Y *= -1d;
				scale /= 14d;
			}
			else
			{
				origin = Origin + Offset;
            }

			origin.X *= FontTexture.localscale * (FontTexture.aspectRatio);
			origin.Y *= FontTexture.localscale;
			


			for (int i = 0; i < m_CacheDraw.Count; i++)
			{
				TextProcessor.Cache cache = m_CacheDraw[i];
				if (cache.Letter == '\0')
					break;//EOS
				if (Options.HasFlag(Options.Shadowing))
					WriteShadow(origin, cache.Position, cache.Letter, ShadowColor, scale, shadow_scale, cache.Italics);
				WriteLetter(origin, cache.Position, cache.Letter, cache.TextColor, scale, cache.Italics);

			}
		}

		const double D_SCALEMULTIMPLIER = 2.0d;
		private Vector2D WriteLetter(Vector2D Origin, Vector2I Offset, char letter, Vector4 fontcolor, double scale,   bool slant = false, bool draw = true)
		{
			scale *= D_SCALEMULTIMPLIER;
			FontDefinition.FontCharacterDefinition value;
			if (Font.Characters.TryGetValue(letter, out value))
			{
				Vector2D size, spacing, offset;
				if(Options.HasFlag(Options.Pixel))
				{
					size = new Vector2D(value.sizex * FontTexture.OneX * scale , (value.sizey * FontTexture.OneY * scale ));
					spacing = new Vector2D(value.aw * FontTexture.OneX * scale, Font.Lineheight * FontTexture.OneY * scale);
					offset = new Vector2D(Offset.X * FontTexture.OneX * scale, Offset.Y * FontTexture.OneY * scale);
				}
				else
				{
					size = new Vector2D((value.sizex * FontTexture.xPix) * scale , (value.sizey * FontTexture.yPix) * scale );
					spacing = new Vector2D((value.aw * FontTexture.xPix) * scale , (Font.Lineheight * FontTexture.yPix) * scale);
					offset = new Vector2D(Offset.X * FontTexture.xPix * scale, Offset.Y * FontTexture.yPix * scale);
				}

				if (draw)
				{
					MatrixD headmatrix = MyAPIGateway.Session.Camera.WorldMatrix;
					//Vector3D textpos = Vector3D.Transform(new Vector3D(Origin.X + offset.X + spacing.X / 2d, (Origin.Y + offset.Y) - (spacing.Y / 2d), -0.1), headmatrix);
					Vector3D textpos = Vector3D.Transform(new Vector3D(Origin.X + offset.X + size.X / 2d, (Origin.Y + offset.Y) - spacing.Y / 2d, -0.1), headmatrix);
					if (letter != ' ')
					{
						if(slant)
						{
							VersionHelper.AddBillboardOrientedSlant(value, fontcolor, textpos, headmatrix.Up, headmatrix.Left, (float)(size.X), (float)(size.Y), Blend);
						}
						else
						{
							VersionHelper.AddBillboardOriented(value, fontcolor, textpos, headmatrix.Up, headmatrix.Left, (float)(size.X), (float)(size.Y), Blend);
						}
					}
				}
				return spacing;
			}
			return Vector2D.Zero;
		}
		private void WriteShadow(Vector2D Origin, Vector2I Offset, char letter, Vector4 fontcolor, double scale, double shadowscale, bool slant = false)
		{
			scale *= D_SCALEMULTIMPLIER;
			FontDefinition.FontCharacterDefinition value;
			if (Font.Characters.TryGetValue(letter, out value))
			{
				Vector2D size, spacing, offset;
				if (Options.HasFlag(Options.Pixel))
				{
					size = new Vector2D(value.sizex * FontTexture.OneX * scale * shadowscale, (value.sizey * FontTexture.OneY * scale * shadowscale));
					var diff = size - new Vector2D(value.sizex * FontTexture.OneX * scale, (value.sizey * FontTexture.OneY * scale));
					diff /= 2d;
					spacing = new Vector2D(value.aw   * FontTexture.OneX * scale , Font.Lineheight * FontTexture.OneY * scale );
					offset = new Vector2D(Offset.X * FontTexture.OneX * scale, Offset.Y * FontTexture.OneY * scale);
					offset.X -= diff.X;
                    //offset.Y -= diff.Y;
                }
				else
				{
					size = new Vector2D((value.sizex * FontTexture.xPix) * scale *  shadowscale, (value.sizey * FontTexture.yPix) * scale *  shadowscale);
					var diff = size - new Vector2D(value.sizex * FontTexture.xPix * scale, (value.sizey * FontTexture.yPix * scale));
					diff /= 2d;
					spacing = new Vector2D((value.aw * FontTexture.xPix) * scale , (Font.Lineheight * FontTexture.yPix) * scale );
					offset = new Vector2D(Offset.X * FontTexture.xPix * scale, Offset.Y * FontTexture.yPix * scale);
					offset.X -= diff.X;
				}

				MatrixD headmatrix = MyAPIGateway.Session.Camera.WorldMatrix;
				//Vector3D textpos = Vector3D.Transform(new Vector3D(Origin.X + offset.X + spacing.X / 2d, (Origin.Y + offset.Y) - (spacing.Y / 2d), -0.1), headmatrix);
				Vector3D textpos = Vector3D.Transform(new Vector3D(Origin.X + offset.X + size.X / 2d, (Origin.Y + offset.Y) - spacing.Y / 2d, -0.1), headmatrix);
				if (letter != ' ')
				{
					if (slant)
					{
						VersionHelper.AddBillboardOrientedSlant(value, fontcolor, textpos, headmatrix.Up, headmatrix.Left, (float)(size.X), (float)(size.Y), Blend);
					}
					else
					{
						VersionHelper.AddBillboardOriented(value, fontcolor, textpos, headmatrix.Up, headmatrix.Left, (float)(size.X), (float)(size.Y), Blend);
					}
				}


			}

		}
	}
}
