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

namespace UIFun.Messagesv2
{


	public class ModEntityMessage : ModMessageBase
	{
		enum EntityMembers : int
		{
			Entity = 10,
			LocalPosition,
			Up,
			Forward,
			Orientation,
			Max,
			TransformMatrix,
			Font
		}

		IMyEntity Entity;
		Vector3D rel = Vector3D.Zero;
		Vector3D up = MatrixD.Identity.Up;
		Vector3D forward = MatrixD.Identity.Forward;
		MatrixD local = MatrixD.Identity;
		FontTexture.TextOrientation TxtOrientation;
		Vector2D Max = Vector2D.Zero;
		Color textcolor = Color.White;
		FontDefinition Font;
		const string FONTNAME = "white";
		MyStringId m_font = MyStringId.GetOrCompute(FONTNAME);

		private bool m_HasCache = false;
		private Vector2I m_Size = Vector2I.Zero;
		private uint hash = 0;
		private int m_CacheSize = 0;
        private List<TextProcessor.Cache> Cache = new List<TextProcessor.Cache>();

		public override void Close()
		{
			Entity = null;
		}

		void UpdateMatrix()
		{
			local = MatrixD.CreateWorld(rel, forward, up);
		}
		void UpdateForwardUp()
		{
			forward = local.Forward;
			up = local.Up;
			rel = local.Translation;
		}
		void UpdateForward(Vector3D Val)
		{
			if(forward != Val)
			{
				forward = Val;
				UpdateMatrix();
			}
		}
		void UpdateUp(Vector3D Val)
		{
			if (up != Val)
			{
				up = Val;
				UpdateMatrix();
			}
		}
		void UpdateLocalPosition(Vector3D Val)
		{
			if(Val != rel)
			{
				rel = Val;
				UpdateMatrix();
			}
		}

		public override object GetMember(int MemberEnum)
		{
			switch ((EntityMembers)MemberEnum)
			{
				case EntityMembers.Entity:
					return Entity;
				case EntityMembers.Forward:
					return local.Forward;
				case EntityMembers.Up:
					return local.Up;
				case EntityMembers.Max:
					return Max;
				case EntityMembers.LocalPosition:
					return local.Translation;
				case EntityMembers.Orientation:
					return (byte)TxtOrientation;
				case EntityMembers.TransformMatrix:
					return local;
				case EntityMembers.Font:
					return m_font.String;

				default:
					return base.GetMember(MemberEnum);
			}
		}
		public override void SetMember(int MemberEnum, object Value)
		{
			switch((EntityMembers)MemberEnum)
			{
				case EntityMembers.Entity:
					Entity = (IMyEntity)Value;
					return;
				case EntityMembers.Forward:
					UpdateForward((Vector3D)Value);
					return;
				case EntityMembers.Up:
					UpdateUp((Vector3D)Value);
					return;
				case EntityMembers.Max:
					Max = (Vector2D)Value;
					Flush();
					return;
				case EntityMembers.LocalPosition:
					UpdateLocalPosition((Vector3D)Value);
                    return;
				case EntityMembers.Orientation:
					TxtOrientation = (FontTexture.TextOrientation)(byte)Value;
					return;
				case EntityMembers.TransformMatrix:
					local = (MatrixD)Value;
					UpdateForwardUp();
					return;
				case EntityMembers.Font:
                    m_font = MyStringId.GetOrCompute((string)Value);
					Font = FontTexture.GetFont(m_font);
					Flush();
					return;
				default:
                    base.SetMember(MemberEnum, Value);
					return;
			}
			
		}
		public override Vector2D GetLength()
		{
			Process();
			Vector2D Length = Vector2D.Zero;
			if (Font == null)
			{
				Font = FontTexture.GetFont(m_font);
				if (Font == null)
					return Length;
			}
			Length = new Vector2D(m_Size.X, m_Size.Y) / 37d;
			return Length;
		}
		public override void Draw()
		{
			if (Entity == null)
			{
				MarkForClose();
				return;
			}

			if (Font == null)
			{
				Font = FontTexture.GetFont(m_font);
				if (Font == null)
					return;
			}
			double maxdistance = 200d * (Scale < 1.0d ? 1.0d : Scale);
			if (Vector3D.DistanceSquared(MyAPIGateway.Session.Camera.WorldMatrix.Translation, Entity.WorldMatrix.Translation) > maxdistance * maxdistance)
				return;
			
			Process();
			DrawCache();
        }

		public void DrawCache()
		{
			Vector2I offset = Vector2I.Zero;
			//instead of doing this, we can do it line by line because we have the max value, we just have to go in reverse. 
			if (TxtOrientation == FontTexture.TextOrientation.rtl)
			{
				//offset.X = -m_Size.X;
			}
			if (TxtOrientation == FontTexture.TextOrientation.center)
			{
				//offset.X = -m_Size.X / 2;
			}
			MatrixD current = local * Entity.WorldMatrix;

			if (Cache.Count > 0)
			{
				offset.X += 4;
            }
			//draw in reverse. 
			int lasty = -int.MaxValue;
			int xadj = 0;
			Vector2I Boundry = m_Size;
			if(Boundry.X / 37d * Scale < Max.X)
			{
				Boundry.X = (int)(Max.X * 37d / Scale);
			}
			if (Boundry.Y / 37d * Scale > Max.Y)
			{
				Boundry.Y = (int)(Max.Y * 37d / Scale);
			}
			for (int i = m_CacheSize - 1; i >= 0 ; i--)
			{
				TextProcessor.Cache cache = Cache[i];
				if(cache.Position.Y > lasty)
				{
					lasty = cache.Position.Y;
					xadj = Boundry.X - cache.Position.X - 37;
				}
				if (TxtOrientation == FontTexture.TextOrientation.rtl)
				{
					offset.X = xadj;
				}
				if (TxtOrientation == FontTexture.TextOrientation.center)
				{
					offset.X = xadj / 2;
				}
				WriteLetter(current, cache.Position + offset, cache.Letter, cache.TextColor);
			}

		}

		public void Process()
		{
			Vector2I Size = Vector2I.Zero;
			int cacheSize = 0;
			bool updated = false;
            var newhash = TextProcessor.ProcessStringBuilder(this, this.Message, Cache, Color.White.ToVector4(), hash, Font, m_HasCache, out Size, out cacheSize, out updated);
			if (updated)
			{
				m_HasCache = true;
				m_CacheSize = cacheSize;
				m_Size = Size;
				hash = newhash;
			}
		}
		public override void Flush()
		{
			base.Flush();
			m_HasCache = false;
		}
		private void WriteLetter(MatrixD world, Vector2I Position, char letter, Color fontcolor, bool draw = true)
		{
			FontDefinition.FontCharacterDefinition value;
			
			if (Font.Characters.TryGetValue(letter, out value))
			{
				if(draw)
				{
					var moveright = ((Position.X ) / 37d) * Scale;
					if (Max.X != 0 && moveright + ((value.sizex / 37d) * Scale) >= Max.X && Position.X <= 0)
					{
						return;
					}
					moveright += ((value.aw + value.lsb)/ 74d) * Scale;
					world.Translation += world.Right * moveright;

					var moveup = (Position.Y / 37d) * Scale;
					if (Max.Y != 0 && moveup - Scale <= -Max.Y)
						return;//dont render
					moveup -= (Font.Lineheight / 74d) * Scale; 
                    world.Translation += world.Up * moveup;

					VersionHelper.AddBillboardOriented(value, fontcolor.ToVector4(), world.Translation, world.Up, world.Left, (float)Scale, Blend);
					
				}
			}
		}

	}
}
