using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectDraw;
using Microsoft.DirectX.DirectSound;

namespace Audiogram
{
	/// <summary>
	/// Enumeration GraphType
	/// Values: SPL, HL
	/// SPL is used to set the Graph Type to an SPL Type audiogram
	/// HL is used to set the Graph Type to a HL Type audiogram
	/// </summary>
	public enum GraphType { SPL, HL };
	/// <summary>
	/// Summary description for AudiogramClass.
	/// AudiogramClass
	/// 
	/// </summary>
	public class AudiogramClass : object
	{
		private System.Windows.Forms.Panel panel;
		private Microsoft.DirectX.DirectDraw.Device draw = null; // Holds the DrawDevice object.
		private SurfaceDescription description;
		private SurfaceDescription description2;
		private Surface primary = null; // Holds the primary destination surface.
		private Surface offscreen = null; // Holds the offscreen surface that the bitmap will be loaded on.
		private Clipper clip = null; // Holds the clipper object.
		private Rectangle destination = new Rectangle();
		private Font font = null;//screen text font;
		private Font font2 = null;//screen text font;
		public Color BackgroundColor;
		public Color ForeColor;
		private Size size;
		public ArrayList listPts;
		
		public GraphType Type;

		public AudiogramClass()
		{
			panel = null;
			Type = GraphType.HL;
			BackgroundColor = Color.Black;
			ForeColor = Color.Red;
			listPts = new ArrayList();
		}
		/*
		 * Create the DirectX Device on the panel
		 * */
		
		public void SetPanel(System.Windows.Forms.Panel panelDraw)
		{
			panel = panelDraw;
			draw = new Microsoft.DirectX.DirectDraw.Device(); // Create a new DrawDevice, using the default device.
			draw.SetCooperativeLevel(panel, CooperativeLevelFlags.Normal); // Set the coop level to normal windowed mode.
			SetDescriptions();
			CreateSurfaces();

		}
		public void SetSize(Size s)
		{
			size = s;
		}
		private void SetDescriptions()
		{
			description = new SurfaceDescription(); // Create a new SurfaceDescription struct.
			description.SurfaceCaps.PrimarySurface = true; // The caps for this surface is simply primary.
			description2 = new SurfaceDescription();
			description2.SurfaceCaps.OffScreenPlain = true;
			description2.Height = panel.Height;
			description2.Width = panel.Width;
		}
		private void CreateSurfaces()
		{
			if(primary!=null)
				primary.Dispose();
			primary = new Surface(description, draw); // Create the primary surface.
			clip = new Clipper(draw); // Create a new clipper.
			clip.Window = panel; // The clipper will use the main window handle.
			primary.Clipper = clip; // Assign this clipper to the primary surface.
			
			
			offscreen = new Surface(description2, draw); // Create the surface using the specified file.
		}

		
		public void Draw(Point l)
		{
			try
			{
			if (null == primary)
				return;
			//Height = Height < 50 ? 50 : Height; // Make sure the height is always valid.
            
			// Get the new client size to Draw to.
			destination = new Rectangle(l,size);
			if(font!=null)
				font.Dispose();
			if(font2!=null)
				font2.Dispose();
			font = new Font("Arial",panel.Height/28,FontStyle.Bold);
			font2 = new Font("Arial",panel.Height/20,FontStyle.Bold);
			if(description!=null)
			{
				IntPtr ptr;
				ptr = font.ToHfont();
				offscreen.FontHandle = ptr;
				offscreen.ColorFill(BackgroundColor);
				if(Type==GraphType.HL)
					DrawHLGraph();
				else
					DrawSPLGraph();
			}
			
				// Try and Draw the offscreen surface on to the primary surface.
				primary.Draw(destination, offscreen, DrawFlags.Wait);
			}
			catch(SurfaceLostException)
			{
				// The surface can be lost if power saving
				// mode kicks in, or any other number of
				// reasons.
				CreateSurfaces(); // Surface was lost. Recreate them.
			}
		}
		private void DrawHLGraph()
		{
			
			//Draw Audiogram Frame in offscreen1
			offscreen.ForeColor = ForeColor;
			offscreen.DrawText(panel.Width/2-10,panel.Height/40,"Frequency",false);
			offscreen.DrawText(8,panel.Height/2-12,"HL (dB)",false);
			//frame lines
			//yaxis
			for(int i=1;i<9;i++)
				offscreen.DrawLine(i*panel.Width/9,8*panel.Height/9,i*panel.Width/9,panel.Height/9);
			//xaxis
			for(int i=1;i<9;i++)
				offscreen.DrawLine(panel.Width/9,i*panel.Height/9,8*panel.Width/9,i*panel.Height/9);
			int l = 250;
			for(int i=2;i<8;i++)
			{
				offscreen.DrawText(i*panel.Width/9-10,panel.Height/9-14,l.ToString(),false);
				l*=2;
			}
			l = 0;
			for(int i=1;i<9;i++)
			{
				offscreen.DrawText(panel.Width/9-18,i*panel.Height/9-6,l.ToString(),false);
				l+=20;
			}
		}
		private void DrawSPLGraph()
		{
			IntPtr ptr;
			//Draw Audiogram Frame in offscreen1
			offscreen.ForeColor = ForeColor;
			offscreen.DrawText(panel.Width/2-10,panel.Height-15,"Frequency",false);
			offscreen.DrawText(8,panel.Height/2-20,"SPL (dB)",false);
			//frame lines
			//yaxis
			for(int i=1;i<10;i++)
				offscreen.DrawLine(i*panel.Width/9,8*panel.Height/9,i*panel.Width/9,panel.Height/9);
			//xaxis
			for(int i=1;i<9;i++)
				offscreen.DrawLine(panel.Width/9,i*panel.Height/9,8*panel.Width/9,i*panel.Height/9);
			//x axis labels
			int l = 250;
			for(int i=2;i<8;i++)
			{
				offscreen.DrawText(i*panel.Width/9-10,7*panel.Height/8+4,l.ToString(),false);
				l*=2;
			}
			//yaxis labels
			l = 120;
			for(int i=1;i<9;i++)
			{
				offscreen.DrawText(panel.Width/9-panel.Width/26,i*panel.Height/9-6,l.ToString(),false);
				l-=20;
			}

			//draw graph points
			AudPt lastRed = null;
			AudPt lastBlue = null;
			SortPoints();
			float f = panel.Width/100;
			foreach(AudPt p in listPts)
			{
				if(p.Symbol==1)
				{
					ptr = font2.ToHfont();
					offscreen.FontHandle = ptr;
					offscreen.ForeColor = Color.Blue;
					
					offscreen.DrawText((int)(p.xPos-f),(int)p.yPos,"X",false);
					ptr = font.ToHfont();
					offscreen.FontHandle = ptr;
					if(lastBlue != null)
					{
						offscreen.DrawWidth = 2;
						offscreen.DrawLine((int)lastBlue.xPos,(int)(lastBlue.yPos+f+f),(int)p.xPos,(int)(p.yPos+f+f));
						offscreen.DrawWidth = 1;
					}
					lastBlue = p;
				}
				else if(p.Symbol==2)
				{
					offscreen.ForeColor = Color.Red;
					offscreen.FillColor = Color.Red;
					offscreen.DrawEllipse((int)(p.xPos-f),(int)(p.yPos),(int)(p.xPos-f+panel.Height/25),(int)(p.yPos+panel.Height/25));
					if(lastRed != null)
					{
						offscreen.DrawWidth = 2;
						offscreen.DrawLine((int)lastRed.xPos,(int)(lastRed.yPos+f),(int)p.xPos,(int)(p.yPos+f));
						offscreen.DrawWidth = 2;
					}
					lastRed = p;
				}
				else if(p.Symbol==3)
				{
					ptr = font2.ToHfont();
					offscreen.FontHandle = ptr;
					offscreen.ForeColor = Color.Blue;
					offscreen.DrawText((int)(p.xPos-f),(int)p.yPos,"X",false);
					ptr = font.ToHfont();
					offscreen.FontHandle = ptr;
				}
				else if(p.Symbol==4)
				{
					offscreen.ForeColor = Color.Red;
					offscreen.FillColor = Color.Red;
					offscreen.DrawEllipse((int)(p.xPos-f),(int)(p.yPos),(int)(p.xPos-f+panel.Height/25),(int)(p.yPos+panel.Height/25));
				}
				
			}
		}

		private void SortPoints()
		{
			ArrayList l = (ArrayList) this.listPts.Clone();
			listPts.Clear();
			foreach(AudPt pt in l)
			{
				bool b = false;
				int j = 0;
				for(int i=0;i<listPts.Count;i++)
				{
					AudPt p = (AudPt) listPts[i];
					if(pt.Freq<p.Freq)
					{
						b=true;
						j=i;
						break;
					}
				}
				if(b==true)
				{
					listPts.Insert(j,pt);
				}
				else
					listPts.Add(pt);
			}
		}
		public AudPt AddPoint(int x, int y, int Symbol)
		{
			AudPt pt = new AudPt();
			pt.Freq= x;
			pt.Threshold = y;
			pt.Symbol = Symbol;
			SetPos(ref pt);
			for(int i=0;i<listPts.Count;i++)
			{
				AudPt p = (AudPt) listPts[i];
				if(p.Symbol==pt.Symbol)
				{
					if(p.Freq==pt.Freq)
						listPts.Remove(p);
				}
			}
			listPts.Add(pt);
			return pt;
		}
		public AudPt AddPoint(int x, int y, string strEar)
		{
			strEar = strEar.ToLower();
			AudPt pt = new AudPt();
			pt.Freq = x;
			pt.Threshold = y;
			if(strEar=="left")
				pt.Symbol = 1;
			else if(strEar=="right")
				pt.Symbol = 2;
			SetPos(ref pt);
			for(int i=0;i<listPts.Count;i++)
			{
				AudPt p = (AudPt) listPts[i];
				if(p.Symbol==pt.Symbol)
				{
					if(p.Freq==pt.Freq)
						listPts.Remove(p);
				}
			}
			listPts.Add(pt);
			return pt;
		}
		public AudPt AddPoint(AudPt p)
		{
			SetPos(ref p);
			listPts.Add(p);
			return p;
		}
		private void SetPos(ref AudPt pt)
		{
			double y;
			double fX;

			//find x
			//Scale is 125-16000 in log2
			float maxX = (float)8*panel.Width/9.0f;
			float minX = (float)panel.Width/9.0f;
			double XScale = (maxX - minX)/((Math.Log(16000,2))-(Math.Log(125,2)));
			double d = Math.Log(pt.Freq,2)-Math.Log(125,2);
			fX =d*XScale+panel.Width/9.0f;
			pt.xPos = (float) fX;

			if(Type==GraphType.SPL)
				y = 120-pt.Threshold;
			else
				y = pt.Threshold;
			float maxY = (float)8*panel.Height/9.0f;
			float minY = (float)panel.Height/9.0f;
			float YScale = (maxY - minY) / 140.0f;//full range
			if(pt.Symbol==1||pt.Symbol==3)
				y = y*YScale+panel.Height/9.0f-14;//+44.125 offset of from top, - size of half the symbol
			else
				y = y*YScale+panel.Height/9.0f - 7;//+44.125 offset of from top, - size of half the symbol
			
			pt.yPos = (int)y;
		}
		public void ClearPtList()
		{
			listPts.Clear();
		}
		public void Done()
		{
			for(int i=0;i<listPts.Count;i++)
			{
				AudPt pt = (AudPt) listPts[i];
				if(pt.Symbol>2)
				{
					listPts.Remove(pt);
					i--;
				}
			}
		}
	}

	/// <summary>
	/// Summary description for AudPt
	/// AudPt
	/// A single audiogram data point
	/// </summary>
	public class AudPt : object
	{
		/// <summary>
		/// Default constructor
		/// </summary>
		public AudPt()
		{
			Freq = 0;
			Threshold = 0;
			Symbol = 0;
		}
		/// <summary>
		/// Creates AudPt with initial x, y and symbol
		/// </summary>
		public AudPt(int x, int y, int s)
		{
			Freq = x;
			Threshold = y;
			Symbol = s;
		}
		/// <summary>
		/// Frequency (X value) of the data point
		/// </summary>
		public int Freq;
		/// <summary>
		/// Threshold (Y Value) of the data point
		/// </summary>
		public int Threshold;
		/// <summary>
		/// The Symbol of the data point, from enum Symbol
		/// </summary>
		public int Symbol;
		/// <summary>
		/// the on screen x position of the data point
		/// </summary>
		public float xPos;
		/// <summary>
		/// the on screen y position of the data point
		/// </summary>
		public float yPos;
	}
}
