// /home/epsilon/Projects/Asteroid/ABot/Framework1/GLVisualizer/Visualizer.cs created with MonoDevelop
// User: epsilon at 17:39 20.04.2008
//

using System;
using System.Collections.Generic;
using GLUtils;

namespace GLVisualizer
{
	public class Visualizer : Framework1.VisualizationPluginTemplate
	{
		// constants
		public const float thePlayfieldZ = -5.0f; // z coordinate of the playfield plane
		public const float theOffsetX = -500.0f;
		public const float theOffsetY = -500.0f;
		public const float theScale = 200.0f;
		
		public static readonly Vector3 theSmallAsteroidSize = new Vector3(0.1f, 0.1f, 0.1f);
		public static readonly Vector3 theMediumAsteroidSize = new Vector3(0.2f, 0.2f, 0.2f);
		public static readonly Vector3 theLargeAsteroidSize = new Vector3(0.4f, 0.4f, 0.4f);
		
		private static readonly Vector3 theBulletSize = new Vector3(0.01f, 0.01f, 0.01f);
		private static readonly Vector3 theBulletColor = new Vector3(1.0f, 0.0f, 0.0f);
		
		// fields
		private GLUtils.GLScreen theScreen;
		private Framework1.Playfield thePlayfield;
		private System.Collections.Generic.List<GLAsteroid> theAsteroids = new List<GLVisualizer.GLAsteroid>();
		private System.Collections.Generic.List<GLTargetHUD> theTargetHUDs = new List<GLVisualizer.GLTargetHUD>();
		private System.Collections.Generic.List<GLObject> theBullets = new List<GLObject>();
		private GLShip theShip;
		private GLUfo theUfo;
		private GLTargetHUD theUfoHUD;
		private Framework1.BotStatus theBotStatus = new Framework1.BotStatus();
		
		// constructor
		public Visualizer()
		{
			LogOutput.LogInfo("GLVisualizer", "Starting GLVisualizer");
		}
		
		// create the opengl window
		public override void StartVisualization()
		{
			LogOutput.LogDebug("GLVisualizer", "Starting Visualization");
			
			theScreen = new GLScreen();
			theScreen.CreateScreen();

			LogOutput.LogInfo("GLVisualizer", "Loading textures");
			GLUtils.GLTexture[] textures = new GLUtils.GLTexture[] {
				// executing assembly is GLVisualizer
				new GLUtils.GLTexture("stone01", System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("stein-BRECCIO.jpg")),
				new GLUtils.GLTexture("stone02", System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("stein-E_MIXED.jpg")),
				new GLUtils.GLTexture("stone03", System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("stein-MAGNUS.jpg")),
				new GLUtils.GLTexture("stone04", System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("stein-stone01.jpg"))
			};
			GLUtils.GLScreen.LoadTextures(textures);

			// start drawing (thread will be locked here)
			theScreen.ActivateScreen();

		}
		
		// kill opengl window
		public override void StopVisualization()
		{
			LogOutput.LogDebug("GLVisualizer", "Stopping Visualization");
			theScreen.Kill();
		}
		
		public override void UpdatePlayfield(Framework1.Playfield playfield)
		{
			LogOutput.LogDebug("GLVisualizer", "Receiving frame number " + playfield.FrameNumber.ToString());
			thePlayfield = playfield;
			
			// add ship to screen
			if (theShip == null)
			{
				theShip = new GLShip();
				theScreen.AddGLObject(theShip);
			}
			
			if (thePlayfield.ShipPresent)
			{
				// position ship
				theShip.Position.X = ((float)thePlayfield.ShipSprite.Position.X + theOffsetX)/theScale;
				theShip.Position.Y = ((float)thePlayfield.ShipSprite.Position.Y + theOffsetY)/theScale;
				theShip.Position.Z = thePlayfieldZ;
				LogOutput.LogDebug("GLVisualizer", "Ship rotation: " + thePlayfield.ShipSprite.Phi * 180 / Math.PI);
				theShip.Rotation.Z = (float)(thePlayfield.ShipSprite.Phi); // FIXME: currently shifted by 90° !!
				
				theShip.IsVisible = true;
			}
			else
			{
				// hide ship (currently not needed)
				theShip.IsVisible = false;
			}
			
			
			if (thePlayfield.SaucerPresent)
			{
				// add ufo to screen
				if (theUfo == null)
				{
					theUfo = new GLUfo();
					theScreen.AddGLObject(theUfo);
					
					theUfoHUD = new GLTargetHUD();
					theScreen.AddGLObject(theUfoHUD);
				}
				
				// position ufo
				theUfo.Position.X = ((float)thePlayfield.SaucerSprite.Position.X + theOffsetX)/theScale;
				theUfo.Position.Y = ((float)thePlayfield.SaucerSprite.Position.Y + theOffsetY)/theScale;
				theUfo.Position.Z = thePlayfieldZ;
				theUfo.IsVisible = true;
				
				// position ufo HUD
				theUfoHUD.UpdateProperties(thePlayfield.SaucerSprite);
				theUfoHUD.UseAlphaBlending = true;
				theUfoHUD.AlphaBlending = 0.75f;
				theUfoHUD.IsVisible = true;

				// is marked as type 1 target?
				if (theBotStatus.Targets1.Contains(thePlayfield.SaucerSprite))
					theUfoHUD.IsTarget1 = true;
				else
					theUfoHUD.IsTarget1 = false;

				// is marked as type 2 target?
				if (theBotStatus.Targets2.Contains(thePlayfield.SaucerSprite))
					theUfoHUD.IsTarget2 = true;
				else
					theUfoHUD.IsTarget2 = false;
			}
			else
			{
				// hide ufo (currently not needed)
				if (theUfo != null)
					theUfo.IsVisible = false;

				if (theUfoHUD != null)
					theUfoHUD.IsVisible = false;
			}

			
			// make sure the list contains enough asteroid
			while (thePlayfield.Asteroids.Count > theAsteroids.Count)
			{
				// asteroid
				GLAsteroid asteroid = new GLAsteroid(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.1f,0.1f,0.1f));
				asteroid.Texture = "stone04";
				theAsteroids.Add(asteroid);
				theScreen.AddGLObject(asteroid);

				// HUD
				GLTargetHUD targetHUD = new GLTargetHUD(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.1f,0.1f,0.1f));
				targetHUD.UseAlphaBlending = true;
				targetHUD.AlphaBlending = 0.75f;
				theTargetHUDs.Add(targetHUD);
				theScreen.AddGLObject(targetHUD);
			}
			
			// add asteroids to screen
			LogOutput.LogDebug("GLVisualizer", "Positioning " +
			                   thePlayfield.Asteroids.Count.ToString() + " asteroids");

			for (int iAsteroid = 0; iAsteroid < theAsteroids.Count; iAsteroid++)
			{
				if (iAsteroid < thePlayfield.Asteroids.Count)
				{
					theAsteroids[iAsteroid].UpdateProperties(thePlayfield.Asteroids[iAsteroid]);
					theTargetHUDs[iAsteroid].UpdateProperties(thePlayfield.Asteroids[iAsteroid]);
					LogOutput.LogDebug("GLVisualizer", "Asteroid position: " +
			                   theAsteroids[iAsteroid].Position);
					
					theAsteroids[iAsteroid].IsVisible = true;
					theTargetHUDs[iAsteroid].IsVisible = true;
					
					// is marked as type 1 target?
					if (theBotStatus.Targets1.Contains(thePlayfield.Asteroids[iAsteroid]))
						theTargetHUDs[iAsteroid].IsTarget1 = true;
					else
						theTargetHUDs[iAsteroid].IsTarget1 = false;

					// is marked as type 2 target?
					if (theBotStatus.Targets2.Contains(thePlayfield.Asteroids[iAsteroid]))
						theTargetHUDs[iAsteroid].IsTarget2 = true;
					else
						theTargetHUDs[iAsteroid].IsTarget2 = false;
				}
				else
				{
					// hide asteroid (currently not needed)
					theAsteroids[iAsteroid].IsVisible = false;
					theTargetHUDs[iAsteroid].IsVisible = false;
				}
			}

			// make sure the list contains enough bullets
			while (thePlayfield.Shots.Count > theBullets.Count)
			{
				GLObject bullet = new GLObject(new Vector3(0.0f, 0.0f, thePlayfieldZ), theBulletSize);
				bullet.RotationSpeed.Z = 5.0f;
				bullet.Color = theBulletColor;
				
				theBullets.Add(bullet);
				theScreen.AddGLObject(bullet);
			}
			
			// add bullets to screen
			LogOutput.LogDebug("GLVisualizer", "Positioning " +
			                   thePlayfield.Shots.Count.ToString() + " bullets");

			for (int iBullet = 0; iBullet < theBullets.Count; iBullet++)
			{
				if (iBullet < thePlayfield.Shots.Count)
				{
					theBullets[iBullet].Position.X =
						((float)thePlayfield.Shots[iBullet].Position.X + theOffsetX)/theScale;
					theBullets[iBullet].Position.Y =
						((float)thePlayfield.Shots[iBullet].Position.Y + theOffsetX)/theScale;
					LogOutput.LogDebug("GLVisualizer", "Bullet position: " +
			                   theBullets[iBullet].Position);
					
					theBullets[iBullet].IsVisible = true;
				}
				else
				{
					// hide bullet (currently not needed)
					theBullets[iBullet].IsVisible = false;
				}
			}
			
			theScreen.Invalidate();
		}
		
		public override void UpdateBotStatus(Framework1.BotStatus status)
		{
			theBotStatus = status;
		}
		
		public override string ToString()
		{
			return "GLVisualizer";
		}

	}
}
