//-----------------------------------------------------------------------------
// File: Lights.cs
//
// Desc: Rendering 3D geometry is much more interesting when dynamic lighting
//       is added to the scene. To use lighting in D3D, you must create one or
//       more lights, setup a material, and make sure your geometry contains surface
//       normals. Lights may have a position, a color, and be of a certain type
//       such as directional (light comes from one direction), point (light
//       comes from a specific x,y,z coordinate and radiates in all directions)
//       or spotlight. Materials describe the surface of your geometry,
//       specifically, how it gets lit (diffuse color, ambient color, etc.).
//       Surface normals are part of a vertex, and are needed for the D3D's
//       internal lighting calculations.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;
using Microsoft.Samples.SqlServer;

namespace hcalc
{
	public class D3HtmForm : Form
	{
		// Our global variables for this project
		Device device = null; // Our rendering device
		VertexBuffer vertexBuffer = null;
		PresentParameters presentParams = new PresentParameters();
		bool pause = false;
		private const int nSegs = 9; // 50; was 100, 10 is 20
		private HtmTrixel _trixel;
		private int nTriangles;
		public D3HtmForm() {
			// Set the initial size of our form
			this.ClientSize = new System.Drawing.Size(400, 400);
			// And its caption
			this.Text = "HTM Animator";
		}

		public bool InitializeGraphics() {
			try {
				presentParams.Windowed = true; // We don't want to run fullscreen
				presentParams.SwapEffect = SwapEffect.Discard; // Discard the frames 
				presentParams.EnableAutoDepthStencil = true; // Turn on a Depth stencil
				presentParams.AutoDepthStencilFormat = DepthFormat.D16; // And the stencil format
				device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); //Create a device
				device.DeviceReset += new System.EventHandler(this.OnResetDevice);
				this.OnCreateDevice(device, null);
				this.OnResetDevice(device, null);
				pause = false;
				return true;
			} catch (DirectXException) {
				// Catch any errors and return a failure
				return false;
			}
		}
		public void OnCreateDevice(object sender, EventArgs e) {
			Device dev = (Device)sender;
			// Now Create the VB
			_trixel = new HtmTrixel();

			vertexBuffer = new VertexBuffer(
				typeof(CustomVertex.PositionNormal),
				1600, dev, Usage.WriteOnly,
				CustomVertex.PositionNormal.Format,
				Pool.Default);
			vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
			this.OnCreateVertexBuffer(vertexBuffer, null);
		}
		public void OnResetDevice(object sender, EventArgs e) {
			Device dev = (Device)sender;
			// Turn off culling, so we see the front and back of the triangle
			device.RenderState.CullMode = Cull.None;
			// Turn on the ZBuffer
			device.RenderState.ZBufferEnable = true;
			device.RenderState.Lighting = true;    //make sure lighting is enabled
		}
		protected void hid2vert(CustomVertex.PositionNormal[] verts, int i, Int64 hid) {
			float x, y, z;
			//
			double[] v0, v1, v2, w;
			double norm;
			v0 = new double[3];
			v1 = new double[3];
			v2 = new double[3];
			w = new double[3];

			char[] name = new char[HtmTrixel.eMaxNameSize];
			_trixel.hid2name(name, hid);
			_trixel.name2Triangle(name, v0, v1, v2);
			w[0] = v0[0] + v1[0] + v2[0];
			w[1] = v0[1] + v1[1] + v2[1];
			w[2] = v0[2] + v1[2] + v2[2];
			norm = w[0] * w[0] + w[1] * w[1] + w[2] * w[2]; 
			Vector3 n1 = new Vector3((float) w[0], (float) w[1], (float) w[2]);

			x = (float) v0[0];
			y = (float) v0[1];
			z = (float) v0[2];
			verts[i].Position = new Vector3(x, y, z);
			verts[i].Normal = n1;

			i++;
			x = (float)v1[0];
			y = (float)v1[1];
			z = (float)v1[2];
			verts[i].Position = new Vector3(x, y, z);
			verts[i].Normal = n1;

			i++;
			x = (float)v2[0];
			y = (float)v2[1];
			z = (float)v2[2];
			verts[i].Position = new Vector3(x, y, z);
			verts[i].Normal = n1;

		}
		public void OnCreateVertexBuffer(object sender, EventArgs e) {
			VertexBuffer vb = (VertexBuffer)sender;

			// Create a vertex buffer (100 customervertex)
			// Lock the buffer (which will return our structs)
			CustomVertex.PositionNormal[]
				verts = (CustomVertex.PositionNormal[])vb.Lock(0, 0);

			Int64 hidlo = _trixel.name2ID("S0000");
			Int64 hidhi = _trixel.name2ID("N3333");
			int i=0;
			nTriangles = 0;
			for (Int64 hid = hidlo; hid < hidhi; hid++) {
				nTriangles++;
				hid2vert(verts, i, hid);
				i += 3;
			}

			// Unlock (and copy) the data
			vb.Unlock();
		}
		private void SetupMatrices() {
			//
			// MOUSE!
			//
			Point p = new Point(MousePosition.X, MousePosition.Y);
			Point mouse = this.PointToClient(p);
			float upordown = -5.0f * ((float) mouse.X/100f);
			//
			// For our world matrix, we will just rotate the object about the y-axis.

			//Vector3 transformaxis = new Vector3(
			//    (float)Math.Cos(Environment.TickCount / 8000.0f),
			//    1.0f,
			//    (float)Math.Sin(Environment.TickCount / 7500.0f));

			//float angle = Environment.TickCount / 150000.0f;
			Vector3 transformaxis = new Vector3(1, 0, 0);
			float angle = 0f;
			device.Transform.World = Matrix.RotationAxis(transformaxis, angle);


			// Set up our view matrix. A view matrix can be defined given an eye point,
			// a point to lookat, and a direction for which way is up. Here, we set the
			// eye five units back along the z-axis and up three units, look at the
			// origin, and define "up" to be in the y-direction.
			device.Transform.View = Matrix.LookAtLH(
				new Vector3(0.0f, 0.0f, -5), // -5 units up from the screen
				new Vector3(0.0f, 0.0f, 0.0f), // 
				new Vector3(0.0f, 1.0f, 0.0f)); // X is right, y is up 

			// For the projection matrix, we set up a perspective transform (which
			// transforms geometry from 3D view space to 2D viewport space, with
			// a perspective divide making objects smaller in the distance). To build
			// a perpsective transform, we need the field of view (1/4 pi is common),
			// the aspect ratio, and the near and far clipping planes (which define at
			// what distances geometry should be no longer be rendered).
			device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, 1.0f, 1.0f, 100.0f);
		}
		private void SetupLights() {
			System.Drawing.Color col = System.Drawing.Color.White;
			//Set up a material. The material here just has the diffuse and ambient
			//colors set to yellow. Note that only one material can be used at a time.
			Direct3D.Material mtrl = new Direct3D.Material();
			mtrl.Diffuse = col;
			// mtrl.Ambient = col;
			device.Material = mtrl;

			//we need to set the D3DRS_LIGHTING renderstate to enable lighting

			device.Lights[0].Type      = LightType.Directional;
			device.Lights[0].Diffuse   = System.Drawing.Color.Red;
			device.Lights[0].Direction = new Vector3(-5, -5, 10);
			device.Lights[0].Enabled   = true;

			device.Lights[1].Type      = LightType.Directional;
			device.Lights[1].Diffuse   = System.Drawing.Color.Green;
			device.Lights[1].Direction = new Vector3(5, 5, 10);
			device.Lights[1].Enabled   = true;

			device.Lights[2].Type      = LightType.Directional;
			device.Lights[2].Diffuse   = System.Drawing.Color.Blue;
			device.Lights[2].Direction = new Vector3(0, 0, 1);
			device.Lights[2].Enabled   = true;

			//Finally, turn on some ambient light.
			//Ambient light is light that scatters and lights all objects evenly
			device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x010101);
		}

		private void Render() {
			if (pause)
				return;

			//Clear the backbuffer to a color 
			device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.DarkSlateGray, 1.0f, 0);
			//
			// Between begin and end scene, setup device, camera, lights, matrices...
			// the world, view, and projection matrices

			device.BeginScene();
			SetupLights();
			SetupMatrices();

			device.SetStreamSource(0, vertexBuffer, 0);
			device.VertexFormat = CustomVertex.PositionNormal.Format;

			device.DrawPrimitives(PrimitiveType.TriangleList, 0, nTriangles); // number of triangles
			device.EndScene();
			device.Present(); // causes an update to it
		}

		protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
			this.Render(); // Render on painting
		}
		protected override void OnMouseMove(MouseEventArgs e) {
			if (e.Button == MouseButtons.Right) {
				throw new Exception("No Right Drag");
			}
		}

		protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e) {
			if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)
				this.Close(); // Esc was pressed
		}
		protected override void OnResize(System.EventArgs e) {
			pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);
		}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		public static void create() {

			using (D3HtmForm frm = new D3HtmForm()) {
				if (!frm.InitializeGraphics()) // Initialize Direct3D
                {
					MessageBox.Show("Could not initialize Direct3D.  This tutorial will exit.");
					return;
				}
				frm.Show();

				// While the form is still valid, render and process messages
				while (frm.Created) {
					frm.Render();
					Application.DoEvents();
				}
			}
		}
	}
}
