using System;
using System.Collections;
using System.Drawing;
using System.IO;
using Microsoft.Ink;
namespace MathSpeakWrite
{
	/// <summary>
	/// 
	/// </summary>
	public class HandwritingPointMatch
	{
		public ArrayList samples; //An array of the data from the test files
		int throwAwayLast = 0, matchRat=1;
		int randomSeed;
		double throwAwayRat=0.20;

		/// <summary>
		/// Initializer. You must call loadSamplesFromDir or manually put samples in the samples array
		/// in order to use this class for recognition.
		/// </summary>
		public HandwritingPointMatch()
		{
			samples = new ArrayList();
			
		}

		/// <summary>
		/// Initialize the HandwritingPointMatch with the first sample from each file 
		/// in the given directory.
		/// </summary>
		/// <param name="dirpath"></param>
		public void loadSamplesFromDir(String dirpath) 
		{
			int numfiles;
			String curdir = System.IO.Directory.GetCurrentDirectory();
			System.IO.Directory.SetCurrentDirectory(dirpath);
			String[] fileNames = System.IO.Directory.GetFiles(dirpath);

			numfiles = fileNames.Length;
			//this.numSamples = new int[numfiles];

			for(int i=0; i < numfiles; i++)
			{
				String nameCode;
				StreamReader sr;
				HandwritingSample hs;
				
				if(fileNames[i].EndsWith("FeatureMatrix"))
					continue;

				//Read in a file
				sr = new System.IO.StreamReader(fileNames[i]);
				sr.ReadLine(); //Read the line of junk
				sr.ReadLine(); //Read numSamples
				
				nameCode = sr.ReadLine();	//Read in the Sample
				sr.ReadLine();				//Read the whitespace

				//Read Body
				hs = new HandwritingSample(nameCode, sr);
				hs.normalize();
				samples.Add(hs); //Reads in the actual sample
				sr.Close();
			}
		
			System.IO.Directory.SetCurrentDirectory(curdir);
		}
		

		/// <summary>
		/// The standard testing scheme, tests all the files in the dirpath directory against those
		/// samples it has loaded.
		/// </summary>
		/// <param name="dirpath"></param>
		public void regTest(String dirpath) 
		{
			int numfiles, numwrong=0, numtested=0;
			String curdir = System.IO.Directory.GetCurrentDirectory();
			System.IO.Directory.SetCurrentDirectory(dirpath);
			String[] fileNames = System.IO.Directory.GetFiles(dirpath);

			numfiles = fileNames.Length;
			
			//Console.Out.WriteLine("Running Regression Test on " + numfiles + " files");

			for(int i=0; i < numfiles; i++) 
			{
				String nameCode, numSampStr;
				StreamReader sr;
				HandwritingSample presHS, recognized;
				int numsamps;

				if(fileNames[i].EndsWith("FeatureMatrix"))
					continue;

				sr = new System.IO.StreamReader(fileNames[i]);
				sr.ReadLine(); //Read the line of junk
				numSampStr = sr.ReadLine(); //Read numSamples
				nameCode = sr.ReadLine();	//Read in the Sample
				
				numsamps = System.Int32.Parse(numSampStr);

				for(int j=0; j < numsamps; j++)
				{
					sr.ReadLine();
					presHS = new HandwritingSample(nameCode, sr);
					presHS.normalize();
					recognized = RecognizeFromSample(presHS, matchRat, throwAwayLast);
					if(!recognized.name.Equals(nameCode))
					{
						numwrong ++;
			//			Console.Out.WriteLine("Misrecognized " + nameCode + " as " +  recognized.name);
					}
					numtested++;
				}
			}

			Console.Out.WriteLine("Tested: " + numtested + " Got : " +numwrong + " wrong");
			System.IO.Directory.SetCurrentDirectory(curdir);
		}
		

		/// <summary>
		/// Tests various parameters to the recognizer on the regTest.
		/// </summary>
		/// <param name="dirPath">Path to the files to test</param>
		public void RegTestRigor(String dirPath)
		{
			int i,j;

			for(i=1; i < 3; i++)
			{
				for(j=-10; j < 30; j+=5)
				{
					this.matchRat=i;
					this.throwAwayLast=j;
					Console.Out.Write("For matchrat=" + matchRat + " throwAwayLast=" +throwAwayLast+ " ");
					this.regTest(dirPath);
				}
			}
		}

		/// <summary>
		/// Recognizes from an InkCollector, returns the best sample on file.
		/// </summary>
		/// <param name="s"></param>
		/// <param name="g"></param>
		/// <param name="ic"></param>
		/// <returns></returns>
		public HandwritingSample RecognizeFromInk(Strokes s, Graphics g, InkCollector ic) 
		{
			HandwritingSample toTest;

			toTest = new HandwritingSample("sample", s, g, ic);
			return RecognizeFromSample(toTest, matchRat, throwAwayLast);
		}
		

		/// <summary>
		/// Takes a Handwriting sample and returns the closest matching sample using the pointMatch algorithm.
		/// </summary>
		/// <param name="toRec">The Sample to recognize</param>
		/// <param name="minlim">In a many to one matching, this is the max number of points in the from set to match
		/// to a single point in the to set.</param>
		/// <param name="rejectAfter">Throw away the last rejectAfter comparisons.</param>
		/// <returns></returns>
		public HandwritingSample RecognizeFromSample(HandwritingSample toRec, int minlim, int rejectAfter) 
		{
			int i;
			int minComp = -1;
			HandwritingSample bestChoice = null;
			
			for(i=0; i < samples.Count; i++) 
			{
				int compVal, reqlim, lim;
				HandwritingSample presSamp;
				presSamp = (HandwritingSample) samples[i];
				//reqlim = 1+((presSamp.xs.Length)/(toRec.xs.Length));
				reqlim = 0; //It's better this way than with the above line.
				lim = (reqlim < minlim) ? minlim : reqlim;
				compVal=compare(toRec, presSamp, lim, rejectAfter);

				if(minComp < 0 || minComp > compVal)
				{
					minComp = compVal;
					bestChoice = presSamp;
				}
			}
			return bestChoice;
		}
		
		/// <summary>
		/// Takes two handwriting samples and compares the toTest parameter to the against
		/// parameter by stepping through the points in the toTest parameter and matching 
		/// them against the closest points in against. Lim indicates the maximum number of 
		/// points in toTest that can be matched to a single point in against.
		/// </summary>
		/// <param name="toTest">Can be thought of as the from set, each member matches to a 
		/// point in against.</param>
		/// <param name="against">Can be thought of as the to set, each point in the toTest set
		/// matches to a point in against.</param>
		/// <param name="lim">The max number of points in toTest that can be matched to a single
		/// point in against.</param>
		/// <param name="rejectAfter"> Throw out the last rejectAfter connections because they are
		/// unreliable</param>
		/// <returns>A value between 0 and max_int</returns>
		public int compare(HandwritingSample toTest, HandwritingSample against, int lim, int rejectAfter) 
		{
			int[] usedArray;
			int i, totaldist=0;
			Random r;

			r=new Random(this.randomSeed);
			usedArray = new int[against.xs.Length];

			for(i=0; i < against.xs.Length; i++)
				usedArray[i] = 0;

			for(i=0; i < toTest.xs.Length - rejectAfter; i++) 
			{
				int presx, presy, closestval, closestindex;
				int index;
				index = i % toTest.xs.Length;
				presx = toTest.xs[index];
				presy = toTest.ys[index];
				closestval = 1999999999; //For purposes of comparison, this function uses distance squared to find closest match
				closestindex = -1;
				//Find the closed match in against to the presPoint, provided that the match isn't > lim	
				for(int j=0; j < against.xs.Length; j++) 
				{
					if(!(usedArray[j] > lim)) 
					{	
						int presdist, deltax, deltay;
						deltax = presx- against.xs[j];
						deltay = presy- against.ys[j];
						presdist = deltax*deltax+deltay*deltay; //We don't need to bother with the squareroot--wastes clock cycles

						if(presdist < closestval || closestindex == -1) 
						{
							closestindex = j;
							closestval = presdist;
						}
					}
				}
				if(closestindex > 0)
					usedArray[closestindex]++;
				totaldist+= closestval;
			}

			return totaldist;
		}
	}
}
