using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using MhsModelCSharp;
using Mhs=MhsModelCSharp;

namespace MhsApplication
{
	/// <summary>
	/// Summary description for WaypointRenderer.
	/// </summary>
	public class WaypointRenderer : 
		System.Windows.Forms.UserControl
	{
		private System.Windows.Forms.Label lblWaypointName;
		private System.Windows.Forms.Panel textContainingPanel;
		private System.Windows.Forms.Label lblHabitatType;
		private System.Windows.Forms.Label lblGpsCoords;
		private WaypointDrawingPanel waypointDrawingPanel;
		private System.ComponentModel.IContainer components;
		private System.Windows.Forms.Label lblPlantPatchSpecies;
		private System.Windows.Forms.Label lblPlantPatchStats;
		private System.Windows.Forms.Label lblPlantPatchName;
		private System.Windows.Forms.GroupBox groupboxPlantPatch;
		private System.Windows.Forms.GroupBox groupboxWaypoint;
		private System.Windows.Forms.ToolTip tooltip1;

		/// <summary>
		/// the waypoint to be rendered
		/// </summary>
		public Waypoint Waypoint
		{
			get { return waypoint; }
			set 
			{ 
				waypoint = value;
				UpdateDisplayableValues();
			}
		}
		private Waypoint waypoint = null;

		/// <summary>
		/// the selected plant patch in this waypoint 
		/// </summary>
		public int SelectedPlantPatchIndex
		{
			set 
			{ 
				selectedPatchIdx = value; 
				UpdateDisplayableValues();
			}
		}
		private int selectedPatchIdx = -1;


		/// <summary>
		/// Public accessor to control render pausing switch. True means don't render.
		/// </summary>
		public bool PauseRender
		{
			get { return renderPaused; }
			set { renderPaused = value; }
		}
		private bool renderPaused = false;

		/// <summary>
		/// The amount of space in pixels to leave at the edges of the waypoint circle
		/// </summary>
		private const int margin = 10;
		
		/// <summary>
		/// The scaling factor to translate model coordinates to screen coordinates.
		/// This is ScreenDimension divided by ModelDimension (screen/model).
		/// For example if the screen is 100 pixels wide and the model is
		/// 30 meters wide. Then the scale is 3.3 pixels per meter. So something
		/// at 15 meters would be at about 50 pixel.
		/// </summary>
		private float drawingScale = 1f;
		private int horizontalCenter = 0;
		private int verticalCenter   = 0;


		/// <summary>
		/// Default constructor. Just does standard Windows Designer 
		/// InitializeComponent() call.
		/// </summary>
		public WaypointRenderer()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();

			InitializeTooltips();

			UpdateDisplayableValues();
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			this.textContainingPanel = new System.Windows.Forms.Panel();
			this.groupboxPlantPatch = new System.Windows.Forms.GroupBox();
			this.lblPlantPatchSpecies = new System.Windows.Forms.Label();
			this.lblPlantPatchStats = new System.Windows.Forms.Label();
			this.lblPlantPatchName = new System.Windows.Forms.Label();
			this.groupboxWaypoint = new System.Windows.Forms.GroupBox();
			this.lblHabitatType = new System.Windows.Forms.Label();
			this.lblGpsCoords = new System.Windows.Forms.Label();
			this.lblWaypointName = new System.Windows.Forms.Label();
			this.waypointDrawingPanel = new MhsApplication.WaypointDrawingPanel();
			this.tooltip1= new System.Windows.Forms.ToolTip(this.components);
			this.textContainingPanel.SuspendLayout();
			this.groupboxPlantPatch.SuspendLayout();
			this.groupboxWaypoint.SuspendLayout();
			this.SuspendLayout();
			// 
			// textContainingPanel
			// 
			this.textContainingPanel.BackColor = System.Drawing.SystemColors.Window;
			this.textContainingPanel.Controls.Add(this.groupboxPlantPatch);
			this.textContainingPanel.Controls.Add(this.groupboxWaypoint);
			this.textContainingPanel.Dock = System.Windows.Forms.DockStyle.Top;
			this.textContainingPanel.DockPadding.Bottom = 5;
			this.textContainingPanel.DockPadding.Left = 5;
			this.textContainingPanel.DockPadding.Right = 5;
			this.textContainingPanel.DockPadding.Top = 5;
			this.textContainingPanel.Location = new System.Drawing.Point(0, 0);
			this.textContainingPanel.Name = "textContainingPanel";
			this.textContainingPanel.Size = new System.Drawing.Size(568, 80);
			this.textContainingPanel.TabIndex = 0;
			// 
			// groupboxPlantPatch
			// 
			this.groupboxPlantPatch.Controls.Add(this.lblPlantPatchSpecies);
			this.groupboxPlantPatch.Controls.Add(this.lblPlantPatchStats);
			this.groupboxPlantPatch.Controls.Add(this.lblPlantPatchName);
			this.groupboxPlantPatch.Dock = System.Windows.Forms.DockStyle.Fill;
			this.groupboxPlantPatch.Location = new System.Drawing.Point(165, 5);
			this.groupboxPlantPatch.Name = "groupboxPlantPatch";
			this.groupboxPlantPatch.Size = new System.Drawing.Size(398, 70);
			this.groupboxPlantPatch.TabIndex = 4;
			this.groupboxPlantPatch.TabStop = false;
			this.groupboxPlantPatch.Text = "PlantPatch";
			// 
			// lblPlantPatchSpecies
			// 
			this.lblPlantPatchSpecies.Dock = System.Windows.Forms.DockStyle.Top;
			this.lblPlantPatchSpecies.Location = new System.Drawing.Point(3, 52);
			this.lblPlantPatchSpecies.Name = "lblPlantPatchSpecies";
			this.lblPlantPatchSpecies.Size = new System.Drawing.Size(392, 16);
			this.lblPlantPatchSpecies.TabIndex = 5;
			this.lblPlantPatchSpecies.Text = "Species";
			// 
			// lblPlantPatchStats
			// 
			this.lblPlantPatchStats.Dock = System.Windows.Forms.DockStyle.Top;
			this.lblPlantPatchStats.Location = new System.Drawing.Point(3, 36);
			this.lblPlantPatchStats.Name = "lblPlantPatchStats";
			this.lblPlantPatchStats.Size = new System.Drawing.Size(392, 16);
			this.lblPlantPatchStats.TabIndex = 4;
			this.lblPlantPatchStats.Text = "Location: 0.0, 000.0° Ellipse: 000.0, 000.0, 000.0°";
			// 
			// lblPlantPatchName
			// 
			this.lblPlantPatchName.Dock = System.Windows.Forms.DockStyle.Top;
			this.lblPlantPatchName.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.lblPlantPatchName.Location = new System.Drawing.Point(3, 16);
			this.lblPlantPatchName.Name = "lblPlantPatchName";
			this.lblPlantPatchName.Size = new System.Drawing.Size(392, 20);
			this.lblPlantPatchName.TabIndex = 3;
			this.lblPlantPatchName.Text = "PlantPatch Name";
			// 
			// groupboxWaypoint
			// 
			this.groupboxWaypoint.Controls.Add(this.lblHabitatType);
			this.groupboxWaypoint.Controls.Add(this.lblGpsCoords);
			this.groupboxWaypoint.Controls.Add(this.lblWaypointName);
			this.groupboxWaypoint.Dock = System.Windows.Forms.DockStyle.Left;
			this.groupboxWaypoint.Location = new System.Drawing.Point(5, 5);
			this.groupboxWaypoint.Name = "groupboxWaypoint";
			this.groupboxWaypoint.Size = new System.Drawing.Size(160, 70);
			this.groupboxWaypoint.TabIndex = 3;
			this.groupboxWaypoint.TabStop = false;
			this.groupboxWaypoint.Text = "Waypoint";
			// 
			// lblHabitatType
			// 
			this.lblHabitatType.Dock = System.Windows.Forms.DockStyle.Top;
			this.lblHabitatType.Location = new System.Drawing.Point(3, 52);
			this.lblHabitatType.Name = "lblHabitatType";
			this.lblHabitatType.Size = new System.Drawing.Size(154, 16);
			this.lblHabitatType.TabIndex = 2;
			this.lblHabitatType.Text = "Habitat Type";
			// 
			// lblGpsCoords
			// 
			this.lblGpsCoords.Dock = System.Windows.Forms.DockStyle.Top;
			this.lblGpsCoords.Location = new System.Drawing.Point(3, 36);
			this.lblGpsCoords.Name = "lblGpsCoords";
			this.lblGpsCoords.Size = new System.Drawing.Size(154, 16);
			this.lblGpsCoords.TabIndex = 1;
			this.lblGpsCoords.Text = "Lat: 0000.00 Lon: 0000.00";
			// 
			// lblWaypointName
			// 
			this.lblWaypointName.Dock = System.Windows.Forms.DockStyle.Top;
			this.lblWaypointName.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.lblWaypointName.Location = new System.Drawing.Point(3, 16);
			this.lblWaypointName.Name = "lblWaypointName";
			this.lblWaypointName.Size = new System.Drawing.Size(154, 20);
			this.lblWaypointName.TabIndex = 0;
			this.lblWaypointName.Text = "Waypoint Name";
			// 
			// waypointDrawingPanel
			// 
			this.waypointDrawingPanel.Dock = System.Windows.Forms.DockStyle.Fill;
			this.waypointDrawingPanel.Location = new System.Drawing.Point(0, 80);
			this.waypointDrawingPanel.Name = "waypointDrawingPanel";
			this.waypointDrawingPanel.Size = new System.Drawing.Size(568, 200);
			this.waypointDrawingPanel.TabIndex = 1;
			this.waypointDrawingPanel.Resize += new System.EventHandler(this.waypointDrawingPanel_Resize);
			this.waypointDrawingPanel.SizeChanged += new System.EventHandler(this.waypointDrawingPanel_SizeChanged);
			this.waypointDrawingPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.waypointDrawingPanel_Paint);
			// 
			// ttGroupboxWaypoint
			// 
			this.tooltip1.AutoPopDelay = 10000;
			this.tooltip1.InitialDelay = 500;
			this.tooltip1.ReshowDelay = 100;
			// 
			// WaypointRenderer
			// 
			this.BackColor = System.Drawing.SystemColors.Control;
			this.Controls.Add(this.waypointDrawingPanel);
			this.Controls.Add(this.textContainingPanel);
			this.Name = "WaypointRenderer";
			this.Size = new System.Drawing.Size(568, 280);
			this.textContainingPanel.ResumeLayout(false);
			this.groupboxPlantPatch.ResumeLayout(false);
			this.groupboxWaypoint.ResumeLayout(false);
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}


		/// <summary>
		/// Add tooltips to appropriate controls. 
		/// Tooltips have helpful text.
		/// </summary>
		private void InitializeTooltips()
		{
			// Waypoint GroupBox ToolTips
			tooltip1.SetToolTip( groupboxWaypoint, "Selected Waypoint's properties");
			tooltip1.SetToolTip( lblWaypointName, "Selected Waypoint's name");
			tooltip1.SetToolTip( lblGpsCoords, "Selected Waypoint's GPS coordinates in UTM format");
			tooltip1.SetToolTip( lblHabitatType, "Selected Waypoint's habitat type");
			
			// PlantPatch GroupBox ToolTips
			tooltip1.SetToolTip( groupboxPlantPatch, "Selected PlantPatch's properties");
			tooltip1.SetToolTip( lblPlantPatchName, "Selected PlantPatch's name");
			tooltip1.SetToolTip( lblPlantPatchStats, "Selected PlantPatch's Stats\nLocation is the distance from the waypoint followed by the bearing in degress\nEllipse is the bounding ellipse of the plant patch as long axis length, short axis length, and the bearing of the long axis");
			tooltip1.SetToolTip( lblPlantPatchSpecies, "Selected PlantPatch's species");

			// WaypointDrawingArea ToolTips
			tooltip1.SetToolTip( waypointDrawingPanel, "Waypoint Drawing area");
		}


		/// <summary>
		/// Paint method. Draws the Waypoint.
		/// </summary>
		/// <param name="sender">waypoint panel as object</param>
		/// <param name="e">PaintEventArg has Graphics object in it.</param>
		private void waypointDrawingPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			if( ! renderPaused )
			{
				// get our tools from the parameters
				Graphics gfx = e.Graphics;
				Control ctl = (Control) sender;

				// clear
				gfx.Clear(Color.White);
			
				// draw the base circle
				int diam = Math.Min(ctl.Width,ctl.Height) - (2*margin);
				int nx = horizontalCenter - diam / 2;
				int ny = verticalCenter   - diam / 2;
				gfx.DrawEllipse(Pens.Black,nx,ny,diam,diam);

				// draw each plant patch
				if( waypoint != null && waypoint.PlantPatches.Count > 0 )
				{
					int i=0;
					foreach( PlantPatch pp in waypoint.PlantPatches )
					{
						bool isSelected = (i == selectedPatchIdx);
						DrawPlantPatch(gfx,pp,isSelected);
						i++;
					}
				}
			}
		}



		/// <summary>
		/// Renders a single plant patch in the proportionally
		/// correct place using a matrix transformation. The selected
		/// PlantPatch is rendered highlighted.
		/// </summary>
		/// <param name="gfx">Graphics object to use to draw</param>
		/// <param name="pp">Plant patch to render</param>
		/// <param name="isSelectedPatch">Whether this patch is selected and should be rendered highlighted</param>
		public void DrawPlantPatch( Graphics gfx, PlantPatch pp,  bool isSelectedPatch )
		{
			Matrix mtx = new Matrix();

			// translate size
			// scale ellipse size to match screen scale
			int ppEffectiveDistance    = (int) Math.Round(pp.Distance * drawingScale);
			int effectiveShortDiameter = (int) Math.Round((pp.ShortAxisDiameter * (float)drawingScale));
			int effectiveLongDiameter  = (int) Math.Round((pp.LongAxisDiameter  * (float)drawingScale));

			// center at origin
			Matrix translate1 = new Matrix();
			translate1.Translate((float)-pp.ShortAxisDiameter/2f,(float)-pp.LongAxisDiameter/2f);

			// scale to screen size
			Matrix scale = new Matrix();
			scale.Scale(drawingScale,drawingScale,MatrixOrder.Append);
			// now all transform distances are screen distances

			// rotate in place
			// since we'll do another rotation for the bearing below
			// we need to subtract that bearing rotation from this one
			Matrix rotate1 = new Matrix();
			rotate1.RotateAt(pp.LongAxisBearing-pp.Bearing,new PointF(0f,0f),MatrixOrder.Append);

			// translate center for length, move up length*scale
			Matrix translate2 = new Matrix();
			translate2.Translate(0f,-ppEffectiveDistance,MatrixOrder.Append);

			// rotate for bearing...too much rotation because it 
			// messes up the local rotation...must subtract rotation earlier
			Matrix rotate2 = new Matrix();
			rotate2.RotateAt(pp.Bearing,new PointF(0f,0f),MatrixOrder.Append);
			
			// translate screen orgin to center origin
			Matrix translate3 = new Matrix();
			translate3.Translate(horizontalCenter,verticalCenter,MatrixOrder.Append);

			// finally paint a PlantPatch ellipse
			// note that fill ellipse takes integers as the width/height
			// but our model uses floats, if we don't use ceiling then
			// plant patches less than 1 are truncated to zero and 
			// aren't rendered
			Matrix transform = new Matrix();
			if( isSelectedPatch )
			{
				// assign our built up transformation to the Graphics object
				transform.Reset();
				transform.Multiply(translate1,MatrixOrder.Append);
				transform.Multiply(scale,MatrixOrder.Append);
				transform.Multiply(rotate1,MatrixOrder.Append);
				transform.Multiply(translate2,MatrixOrder.Append);
				transform.Multiply(rotate2,MatrixOrder.Append);
				transform.Multiply(translate3,MatrixOrder.Append);
				gfx.Transform = transform;
				gfx.FillEllipse(Brushes.Yellow,
					0, 0,
					(int)Math.Ceiling(pp.ShortAxisDiameter),
					(int)Math.Ceiling(pp.LongAxisDiameter));


				// assign our built up transformation to the Graphics object
				transform.Reset();
				transform.Multiply(translate1,MatrixOrder.Append);
				transform.Multiply(rotate1,MatrixOrder.Append);
				transform.Multiply(translate2,MatrixOrder.Append);
				transform.Multiply(rotate2,MatrixOrder.Append);
				transform.Multiply(translate3,MatrixOrder.Append);
				gfx.Transform = transform;
				gfx.DrawEllipse(Pens.DarkBlue,
								-effectiveShortDiameter/2, 
								-effectiveLongDiameter/2,
								(int)effectiveShortDiameter,
								(int)effectiveLongDiameter);

				// draw a line
				transform.Reset();
				transform.Scale(1f,drawingScale,MatrixOrder.Append);
				transform.Multiply(rotate2,MatrixOrder.Append);
				transform.Multiply(translate3,MatrixOrder.Append);
				gfx.Transform = transform;
				gfx.DrawLine(Pens.Black,
					0, 0,
					0,
					(int)Math.Round(-pp.Distance));
			}
			else
			{
				transform.Reset();
				transform.Multiply(translate1,MatrixOrder.Append);
				transform.Multiply(rotate1,MatrixOrder.Append);
				transform.Multiply(translate2,MatrixOrder.Append);
				transform.Multiply(rotate2,MatrixOrder.Append);
				transform.Multiply(translate3,MatrixOrder.Append);
				gfx.Transform = transform;
				gfx.DrawEllipse(Pens.OliveDrab,
								-effectiveShortDiameter/2, 
								-effectiveLongDiameter/2,
								(int)effectiveShortDiameter,
								(int)effectiveLongDiameter);
			}
			gfx.Transform.Reset();
		}


		/// <summary>
		/// Anytime the size changes we need to update the drawing scale and redraw
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void waypointDrawingPanel_SizeChanged(object sender, System.EventArgs e)
		{
			UpdateDrawingScale((Control)sender);
			waypointDrawingPanel.Invalidate();
		}

		/// <summary>
		/// Anytime the size changes we need to update the drawing scale and redraw
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void waypointDrawingPanel_Resize(object sender, System.EventArgs e)
		{
			UpdateDrawingScale((Control)sender);
			waypointDrawingPanel.Invalidate();
		}

		/// <summary>
		/// adjust the drawing scale so that the image is proportional
		/// to the model
		/// </summary>
		/// <param name="ctl">The control to read width & height from</param>
		private void UpdateDrawingScale( Control ctl )
		{
			// the coordinates of the center of the screen
			horizontalCenter = ctl.Width  / 2;
			verticalCenter   = ctl.Height / 2;

			// we draw in a square bounding box, so find 
			// the smallest dimension and subtract the margins
			int effectiveDimension = Math.Min(ctl.Width,ctl.Height) - (2 * margin);

			// now calculate the drawing scale given 
			// the drawing dimension against the model dimension
			drawingScale = ((float)effectiveDimension) /  ModelParameters.WAYPOINT_DIAMETER_METERS;
		}

		private void UpdateDisplayableValues()
		{
			if( waypoint != null )
			{
				lblWaypointName.Text = waypoint.Name;
				lblGpsCoords.Text    = "Lat: " + waypoint.Latitude + " Lon: " + waypoint.Longitude;
				lblHabitatType.Text  = waypoint.Habitat;
				if( selectedPatchIdx > -1 &&
					waypoint.PlantPatches != null &&
					waypoint.PlantPatches.Count > selectedPatchIdx )
				{
					lblPlantPatchName.Text = waypoint.PlantPatches[selectedPatchIdx].Name;
					lblPlantPatchStats.Text = "Location: " + waypoint.PlantPatches[selectedPatchIdx].Distance +
						", " + waypoint.PlantPatches[selectedPatchIdx].Bearing + "° " +
						" Ellipse: " +
						waypoint.PlantPatches[selectedPatchIdx].LongAxisDiameter  +", " +
						waypoint.PlantPatches[selectedPatchIdx].ShortAxisDiameter + ", " +
						waypoint.PlantPatches[selectedPatchIdx].LongAxisBearing + "°";
					lblPlantPatchSpecies.Text = waypoint.PlantPatches[selectedPatchIdx].Species;
				}
				else
				{
					lblPlantPatchName.Text ="";
					lblPlantPatchStats.Text = "";
					lblPlantPatchSpecies.Text = "";
				}
			}
			else
			{
				lblWaypointName.Text = "";
				lblGpsCoords.Text    = "";
				lblHabitatType.Text  = "";
				lblPlantPatchName.Text ="";
				lblPlantPatchStats.Text = "";
				lblPlantPatchSpecies.Text = "";
			}	
		}


	}

}
