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

namespace HearingAidLab
{
	/// <summary>
	/// Summary description for Star.
	/// </summary>
	public class StarAudio : System.Windows.Forms.Form
	{
		private System.Windows.Forms.Panel panelgame;
		public bool bSingleScreen = false;
		private StarResults res;
		public StarGame.Game game;
		private Rectangle destination = new Rectangle();
		private AR ar;
		public Run run;
		public WaveLib.WavePlayer player = new WavePlayer();
		public Form1 mainmenu;
		
		//this one creates the form here, and uses the game class directly
		//DeadRegions uses the GameForm class, and lets it create the form.
	
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		
		public System.Windows.Forms.Timer timer;//response timer
		public System.Windows.Forms.Timer timer2;//ISI timer
		public System.Windows.Forms.Timer timer3;//wait for stim to finish timer
		private Timer ITimerOn;//Interval ON (button lit)
		private Timer ITimerSpace;//Between intervals
		public System.Windows.Forms.Timer timerCatch;//times catch trials

		public string strInitFile;
		public FileInfo outfile;
		public StreamWriter output;
		public StreamWriter iwriter;

		private const int NORMAL = 0;
		private const int MASKING = 1;
		private int Mode = NORMAL;
		private string strButton;
		private bool bInterval = false;
		private ArrayList IntervalList;
		private int nReversals = 0;
		private int nGameCount = 0;
		private int nGameDelay = 0;
		private int nGameCorrect = 0;

		public bool bButtons = true;
		private bool bFirst = true;
		private bool bCheck = true;
		private bool Summary = true;
		private int nCatchCount = 0; //used to see if the user responds positively 5 times in a row
		private ArrayList listMasking;
		private ArrayList listNoise;
				
		public StarAudio(bool SingleScreen)
		{
			//
			// Required for Windows Form Designer support
			//
			if(player.Open()==false)
			{
				MessageBox.Show(res,"Unable to open sound card!","Audio Error",MessageBoxButtons.OK,MessageBoxIcon.Stop);
				return;
			}
			bSingleScreen = SingleScreen;
			if(bSingleScreen==false)
			{
				this.Location = new Point(1020,-4);
				this.WindowState = FormWindowState.Maximized;
			}
			else
			{
				this.Location = new Point(100,100);
				this.WindowState = FormWindowState.Normal;
			}
			InitializeComponent();
			panelgame.Width = (int)(this.Width * .7); //80% of window is pic
			panelgame.Height= (int)(this.Height* .7); 
			panelgame.Left = (int)(this.Width * .28);
			panelgame.Top = 4;

			run = new Run();
			ar = new AR();
			this.Closing += new CancelEventHandler(Star_Closing);
		}

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

		#region Windows Form 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()
		{
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(StarAudio));
			this.panelgame = new System.Windows.Forms.Panel();
			this.SuspendLayout();
			// 
			// panelgame
			// 
			this.panelgame.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right)));
			this.panelgame.BackColor = System.Drawing.SystemColors.ControlLightLight;
			this.panelgame.Location = new System.Drawing.Point(32, 8);
			this.panelgame.Name = "panelgame";
			this.panelgame.Size = new System.Drawing.Size(744, 500);
			this.panelgame.TabIndex = 1;
			// 
			// StarAudio
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(792, 702);
			this.ControlBox = false;
			this.Controls.Add(this.panelgame);
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.Name = "StarAudio";
			this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
			this.Text = "Star";
			this.ResumeLayout(false);

		}
		#endregion

		private void Draw()
		{
			/*
			if(bFirst==true)
			{
				if(bSingleScreen==false)
				{
					this.Location = new Point(1020,-4);
					this.WindowState = FormWindowState.Maximized;
				}
				else
				{
					this.Location = new Point(100,100);
					this.WindowState = FormWindowState.Normal;
				}
			}*/
			if(game!=null)
				game.Draw(PointToScreen(panelgame.Location));
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			base.OnPaint (e);
			Draw();
			
		}

		public void Init()
		{
			player.Init();
			this.Show();
			InitTestSpace();
			if(mainmenu.chkAppend.Checked == true)
			{
				if(ReadResultFile()==false)
					MessageBox.Show(res,"There was an error reading the results file!");
			}
			InitStims();
			int n = run.Stims.Count;
            InitResultScreen();
			if(mainmenu.def.bInterval==true)
				InitInterval();
			res.Update();
			ar.nStepSize = mainmenu.def.nSecondaryStep;
			ar.StdErrVal = mainmenu.def.dwStdErrVal;
			ar.PrimaryReversals = mainmenu.def.nChangeReversals;
			ar.Mode = mainmenu.def.nStopMode;
			ar.NumOfReversals = mainmenu.def.nStopReversals;
			
			
			
			timer = new Timer();
			timer.Interval = mainmenu.def.nTimer;
			timer.Tick += new EventHandler(timer_Tick);
			timer2 = new Timer();
			timer2.Interval = mainmenu.def.nDelay;
			timer2.Tick += new EventHandler(timer2_Tick);
			timerCatch = new Timer();
			timerCatch.Interval = mainmenu.def.nCatchLength;
			timerCatch.Tick += new EventHandler(timerCatch_Tick);
			timer3 = new Timer();
			timer3.Interval = 100;
			timer3.Tick += new EventHandler(timer3_Tick);

			string strTemp = outfile.FullName;
			if(strTemp.IndexOf(".txt")>0)
				strTemp = strTemp.Replace(".txt",".dat");
			else
				strTemp += ".dat";
		
			iwriter = new StreamWriter(strTemp,false);	
			iwriter.AutoFlush = true;
			iwriter.WriteLine("HAL trial by trial");
			iwriter.WriteLine(string.Empty);
			iwriter.WriteLine("SUBJECT DATA");
			iwriter.WriteLine(mainmenu.txtName.Text);
			iwriter.WriteLine(mainmenu.txtBirth.Text);
			iwriter.WriteLine(mainmenu.cmbGender.Text);
			iwriter.WriteLine(mainmenu.txtGroup.Text);
			iwriter.WriteLine(mainmenu.txtId.Text);
			iwriter.WriteLine(this.strInitFile);
			iwriter.WriteLine(DateTime.Now.ToShortDateString());
			iwriter.WriteLine(DateTime.Now.ToShortTimeString());
			iwriter.WriteLine(string.Empty);
			iwriter.WriteLine("Stim,Level,Actual,Desired,Correct");
			
			if(res.Start()==true)
				StartStar();
			else
			{
				res.Close();
				this.Close();
			}
		}
		
		public string GetCal()
		{
			return mainmenu.GetCal();
		}
		private void SetupGame(int nGame)
		{
			game = new Game();
			game.SetPanel(this.panelgame);
			game.SetSize(panelgame.Size);
			game.LoadGame(mainmenu.def.strFeedbackDir,(string) mainmenu.def.Feedback[nGame]);
			Draw();
		}
		private void InitTestSpace()
		{
			if(mainmenu.def==null)
				return;
			if(mainmenu.def.Feedback.Count>0)
				SetupGame(0);
			DrawButtons(mainmenu.def.Buttons);
		}
		private void DrawButtons(ArrayList Buttons)
		{
			int n=1;
			foreach(Button b in Buttons)
			{
				if(b.Text.ToLower()!="repeat")
				{
					if(b.Location.X==0&&b.Location.Y==0)
						//b.Location = new Point(10,n*b.Height+n*8);
						b.Location = new Point(10+n*b.Width,this.Height - b.Height);
					b.Click += new EventHandler(Button_Click);
					b.KeyPress += new KeyPressEventHandler(b_KeyPress);
					b.Tag = n.ToString();
					this.Controls.Add(b);
					n++;
				}
			}
		}
		private void InitResultScreen()
		{
			res = new StarResults();
			res.Star = this;
			res.Show();
			res.Format();
			res.FillList(this.run.Stims);
			res.CreateSurfaces();
			res.Update();
			foreach(Stim s in run.PreviousStims)
			{
				if(s.fThreshold>0)
					res.AddAudPoint(s,0.0,0);
			}
		}
		private void InitStims()
		{
			
			try
			{
				Stim s;
				//EAR
				/*foreach(string ear in mainmenu.def.Ears)
				 * {
				 * */
				foreach(int meth in mainmenu.def.Methods)
				{
					foreach(Stim a in mainmenu.def.PrimaryStims)
					{
						s = new Stim();
						s = a.Clone();
						s.strEar = a.strEar;
						s.nMethod = meth;
						s.nFreq = a.nFreq;
						s.buffer = new WaveBuffer();
						mainmenu.CreateStim(ref s);
						s.buffer.AmplificationType = s.AmpType;
						/*if(a.strType=="pulse")
							//s.buffer.Pulse(mainmenu.MaxAmplitude,s.nFreq,s.nLength,s.nRise,s.nFreq,s.nInterval,s.strEar,false);
							s.buffer.Pulse(mainmenu.MaxAmplitude,s.nFreq,s.nLength,s.nRise,s.nFall,s.nInterval,s.strEar,false);
						else
							s.buffer.Tone(mainmenu.MaxAmplitude,s.nFreq,s.nLength,s.nRise,s.nFreq,s.strEar,false);
						s.buffer.Status = WaveBuffer.WaveStatus.Done;*/
						run.Stims.Add(s);
					}
				}
			
				if(mainmenu.def.bRandom==true)
				{
					run.RandomOrder = new ArrayList();
					Random r = new Random(System.DateTime.Now.Millisecond);
					while(run.RandomOrder.Count < run.Stims.Count)
					{
						int n = r.Next(run.Stims.Count);
						if(run.RandomOrder.IndexOf(n)<0)
							run.RandomOrder.Add(n);
					}
				}
				run.nStepType = 0;
				run.nStepSize = mainmenu.def.nInitialStep;
			}
			catch(Exception err)
			{
				
				MessageBox.Show("Error in Stimulus Initialization: "+err.Message+".  Please check your .def file and .wav files","Parse Error",
					MessageBoxButtons.OK,MessageBoxIcon.Stop);
				return;
			}
			
		}
		
		private void InitInterval()
		{
			
			iwriter = new StreamWriter(outfile.FullName+".dat",false);
			iwriter.WriteLine("HAL trial by trial");
			iwriter.WriteLine(string.Empty);
			iwriter.WriteLine("SUBJECT DATA");
			iwriter.WriteLine(mainmenu.txtName.Text);
			iwriter.WriteLine(mainmenu.txtBirth.Text);
			iwriter.WriteLine(mainmenu.cmbGender.Text);
			iwriter.WriteLine(mainmenu.txtGroup.Text);
			iwriter.WriteLine(mainmenu.txtId.Text);
			iwriter.WriteLine(this.strInitFile);
			iwriter.WriteLine(DateTime.Now.ToShortDateString());
			iwriter.WriteLine(DateTime.Now.ToShortTimeString());
			iwriter.WriteLine(string.Empty);
			iwriter.WriteLine("Actual,Desired,Correct");

			bInterval = true;
			ITimerOn = new Timer();
			ITimerOn.Interval = mainmenu.def.nIntervalLen;
			ITimerOn.Tick += new EventHandler(ITimerOn_Tick);
			ITimerSpace = new Timer();
			ITimerSpace.Interval = mainmenu.def.nIntervalSpace;
			ITimerSpace.Tick += new EventHandler(ITimerSpace_Tick);
			int nB = mainmenu.def.Buttons.Count-1;
			IntervalList = new ArrayList();
			int nX = 0;
			for(int i=0;i<100;i++)
			{
				IntervalList.Add(nX++);
				if(nX==nB)
					nX = 0;
			}
			int nLen = mainmenu.def.nIntervalLen;
			foreach(Stim s in run.Stims)
			{
				s.buffer.SetLength(nLen);
			}
		}
		
		private void Star_Closing(object sender, CancelEventArgs e)
		{
			if(iwriter!=null)
			{
				try
				{
					iwriter.Flush();
					iwriter.Close();
				}
				catch(Exception){}
			}
			if(output!=null)
			{
				if(Summary==false)
					WriteResults();
				try	{output.Close();}
				catch(Exception){}
			}
			try
			{
				timer.Stop();
				timer2.Stop();
				timer3.Stop();
				player.Stop();
				player.Close();
				player.Dispose();

				GC.Collect();
			}
			catch(Exception ){}
			finally 
			{
				mainmenu.Show();
			}
		}


		private void Button_Click(object sender, EventArgs e)
		{
			
			if(bButtons==false&&player.ibolArscRunning==false)
			{
				if(this.bInterval==false)
				{
					if(timer2.Enabled==true)
					{
						timer2.Stop();
						timer2.Start(); 						
					}
					return;
				}
				else
					return;
			}
			timer.Stop();

			Button b = (Button) sender;
			strButton = (string) b.Tag;
			
			if(bButtons==true&&player.ibolArscRunning==true&&this.bInterval==false)
			{
				bButtons=false;
				timer3.Start();
				return;
			}
		
			bButtons=false;

			if(bInterval==false)
			{
				if(player.Signals[0].Status!=WaveBuffer.WaveStatus.Done)
					timer3.Start();//wait for stim to finish
				else
					CheckButton(strButton);//inter stim delay
			}
			else
			{
				strButton = mainmenu.def.Buttons.IndexOf(b).ToString();
				CheckIntervalButton(strButton);
			}
		}
		
		private void CheckButton(string strButton)
		{
			nGameCount++;
			if(run.CatchTrial==true)	//we are doing a catch trial
			{
				if(run.CurrentStim.strDesiredResponse!=strButton)//they didn't click the Yes button
					run.nCatches++;
				else if(run.CurrentStim.strDesiredResponse==strButton)
					run.nFalse++;
				run.Levels.RemoveAt(run.Levels.Count-1);//remove the last level, because it was a catchtrial, that level won't have an associated response
			}
			else//we are not doing a catch trial
			{
				if(run.Levels.Count != ar.Responses.Count +1) 
			  {  timer2.Stop();
				  timer2.Start();
				  return; 
			  }
				Action a;
				if(run.CurrentStim.strDesiredResponse==strButton)
				{
					//correct response
					WriteRes("{0},{1},{2},{3},{4}",run.CurrentStim.nFreq,run.nCurrentLevel,strButton,run.CurrentStim.strDesiredResponse,"Correct");
					a = EvaluateResponse(true);
					if(run.nStepSize == mainmenu.def.nInitialStep)
						run.nStepSize = mainmenu.def.nPrimaryStep;
					nGameCorrect++;
				}
				else
				{
					//incorrect response
					WriteRes("{0},{1},{2},{3},{4}",run.CurrentStim.nFreq,run.nCurrentLevel,strButton,run.CurrentStim.strDesiredResponse,"Incorrect");
					a = EvaluateResponse(false);
				}
				if(ar.Responses.Count != run.Levels.Count)MessageBox.Show("number mismatch; break here");

				//what do we do about it?
				if(a==Action.Complete)
				{
					Complete();
					return;
				}
				if(a==Action.NStepSizeChange||a==Action.PStepSizeChange)
				{
					//Go to next step size
					run.CalcLevels.Clear();
					nReversals = 0;
					run.nStepType++;
					run.nStepSize = mainmenu.def.nSecondaryStep;
				}
				if(a==Action.PositiveReturn || a==Action.PStepSizeChange)
					PositiveReturn();
				else if(a==Action.NegativeReturn|| a==Action.NStepSizeChange)
					NegativeReturn();
			}
			res.ShowResponses(ar.Responses,run.Levels);
			res.ShowStdErr(ar.standardError);
			//step the game
			if(nGameCount>=nGameDelay)
			{
				if(mainmenu.def.nGameDelayType==0)
				{
					for(int kk=0;kk<nGameCorrect;kk++)
						StepGame();
				}
				else
					StepGame();
				nGameCount=0;
				nGameCorrect=0;
			}

			//calculate jitter
			Random r = new Random(DateTime.Now.Millisecond);
			double d = Math.Round(r.NextDouble(),0);
			timer2.Interval = (int) (mainmenu.def.nDelay + d*mainmenu.def.nJitter);
			//start inter stim delay

			timer2.Start();
			
		}
		
		private void CheckIntervalButton(string strButton)
		{
			nGameCount++;
			Action a;
			if(run.CurrentStim.strDesiredResponse==strButton)
			{
				//correct response
				a = EvaluateResponse(true);
				WriteRes("{0},{1},{2},{3},{4}",run.CurrentStim.nFreq,run.nCurrentLevel,strButton,run.CurrentStim.strDesiredResponse,"Correct");
				if(run.nStepSize == mainmenu.def.nInitialStep)
					run.nStepSize = mainmenu.def.nPrimaryStep;
				nGameCorrect++;
			}
			else
			{
				//incorrect response
				a = EvaluateResponse(false);
				WriteRes("{0},{1},{2},{3},{4}",run.CurrentStim.nFreq,run.nCurrentLevel,strButton,run.CurrentStim.strDesiredResponse,"Incorrect");
			}
			//what do we do about it?
			if(a==Action.Complete)
			{
				Complete();
				return;
			}
			if(a==Action.NStepSizeChange||a==Action.PStepSizeChange)
			{
				//Go to next step size
				run.CalcLevels.Clear();
				run.nStepType++;
				nReversals = 0;
				run.nStepSize = mainmenu.def.nSecondaryStep;
			}
			if(a==Action.PositiveReturn || a==Action.PStepSizeChange)
				PositiveReturn();
			else if(a==Action.NegativeReturn|| a==Action.NStepSizeChange)
				NegativeReturn();
			
			res.ShowResponses(ar.Responses,run.Levels);
			res.ShowStdErr(ar.standardError);
			//step the game
			if(nGameCount>=nGameDelay)
			{
				if(mainmenu.def.nGameDelayType==0)
				{
					for(int kk=0;kk<nGameCorrect;kk++)
						StepGame();
				}
				else
					StepGame();
				nGameCount=0;
				nGameCorrect=0;
			}
			//calculate jitter
			Random r = new Random(DateTime.Now.Millisecond);
			double d = r.NextDouble();
			timer2.Interval = (int) (mainmenu.def.nDelay + d*mainmenu.def.nJitter);
			//start inter stim delay
			timer2.Start();
		}
		private void WriteRes(string astrFormat, params object[] aobj_)
		{
			if(iwriter==null)
				return;
			if(iwriter.BaseStream.CanWrite==false)
				return;
			iwriter.WriteLine( astrFormat, aobj_ );
		}
		private void StepGame()
		{
			if(game.StepGame(PointToScreen(panelgame.Location))==false)
			{
				run.nCurrentGame++;
				if(run.nCurrentGame==mainmenu.def.Feedback.Count)
					run.nCurrentGame=0;
				this.SetupGame(run.nCurrentGame);
			}
		}
		
		private AdaptiveResponse.Action EvaluateResponse(bool b)
		{
			Action a = ar.AddResponse(b,run.nCurrentLevel);
			return a;
		}
		private void Complete()
		{
			//step the game
			if(nGameCount>=nGameDelay)
			{
				if(mainmenu.def.nGameDelayType==0)
				{
					for(int kk=0;kk<nGameCorrect;kk++)
						StepGame();
				}
				else
					StepGame();
				nGameCount=0;
				nGameCorrect=0;
			}

			if(Mode == NORMAL)
			{
				if(GetNextStim()!=true)
				{
					if(CheckForMasking()==true)
					{
						Masking();
						timer2.Start();
						return;
					}
					res.Done();
					WriteResults();
					MessageBox.Show(res,"Test Complete","Done",MessageBoxButtons.OK,MessageBoxIcon.Information);
					this.bButtons = false;
					timer.Stop();timer.Enabled = false;
					timer2.Stop();timer.Enabled = false;
					timer3.Stop();timer.Enabled = false;
					player.Stop();
					player.Signals[0] = null;
					player.Signals[1] = null;
					player.Signals[2] = null;
				}
				else
				{
					//bButtons = true;
					//Shouldn't need these.  They get set in GetNextStim
					//nReversals = 0;
					//run.nCurrentLevel = run.CurrentStim.nOrigin;
					timer2.Start();//inter stim delay
				}
			}
			else//Mode = MASKING
			{
				if(Masking()==true)
					timer2.Start();
				else
				{
					res.Done();
					WriteResults();
					MessageBox.Show(res,"Test Complete","Done",MessageBoxButtons.OK,MessageBoxIcon.Information);
					this.bButtons = false;
					timer.Stop();timer.Enabled = false;
					timer2.Stop();timer.Enabled = false;
                    timer3.Stop();timer.Enabled = false;
					player.Stop();
					player.Signals[0] = null;
					player.Signals[1] = null;
					player.Signals[2] = null;
				}
			}
			/*else
				CreateStim();*/

		}
		private void PositiveReturn()
		{
			run.nHits++;
			run.bCurrentResponse = true;
			run.strAction = "+";
			bFirst = false;
			nCatchCount++;
			DecreaseLevel();

		}
		private void NegativeReturn()
		{
			run.bCurrentResponse = false;
			run.strAction = "-";
			IncreaseLevel();
		}
		private void DecreaseLevel()
		{
			run.nCurrentLevel -= run.nStepSize;
			if(run.nCurrentLevel<=-20)
			{
				run.nCurrentLevel = -15;
				if(((int)run.Levels[run.Levels.Count-2]==-15) && ((int)run.Levels[run.Levels.Count-3]==-15))
				{
					for(int k=0;k<run.CalcLevels.Count;k++)
						run.CalcLevels[k] = -15;
					Complete();
				}
			}
		}
		private void IncreaseLevel()
		{
			//Don't let the signal go higher than
			//CalDb, or MaxLevel, whichever is less.
			int nMax = (int) mainmenu.CalDb;
			if(mainmenu.CalDb>mainmenu.def.nMaxLevel)
				nMax = mainmenu.def.nMaxLevel;
			run.nCurrentLevel += run.nStepSize;
			if(run.nCurrentLevel>nMax)
			{
				run.nCurrentLevel=nMax;
				if(((int)run.Levels[run.Levels.Count-2]== nMax) && ((int)run.Levels[run.Levels.Count-3]== nMax))
				{
					for(int k=0;k<run.CalcLevels.Count;k++)
						run.CalcLevels[k] = nMax+10;
					Complete();
				}
			}
		}
		
	
		private void StartStar()
		{
			Summary = false;
			System.Threading.Thread.Sleep(2000);
			//Get First Stim
			try
			{
				if(GetNextStim()==true)
				{
					//run.nCurrentLevel = 20;
					res.ShowStatus("Working");
					GetThreshold();
				}
			}
			catch(Exception err)
			{
				MessageBox.Show("Error: " + err.Message);
				mainmenu.Show();
				this.Close();
			}
		}
		private bool GetNextStim()
		{
			nCatchCount = 0;
			//Don't do this on first stim
			if(run.nCurrentLevel>-100)
			{
				//set the stim fields
				Stim s = run.CurrentStim;
				s.Responses = ar.Responses;
				s.Levels = run.Levels; 
				
				SetMethod(s.nMethod);
				float f=0.0f;
				//calc the threshold
				for(int k=0;k<run.CalcLevels.Count;k++)
				{
					f+=(int)run.CalcLevels[run.CalcLevels.Count-1-k];
				}
				s.fThreshold = f/run.CalcLevels.Count;
				if(ar.Responses.Count != run.Levels.Count)MessageBox.Show("number mismatch; break here");

				//add it to the display
				res.AddAudPoint(s,ar.standardError,nReversals);
				//remove and reinsert it to the list, so the changes will be kept
				int n = run.Stims.IndexOf(s);
				run.Stims.RemoveAt(n);
				run.Stims.Insert(n,s);
				run.nCurrentStim++;
				ar.Clear();
				int x=0;
				//make sure the stim is done playing
				while(player.Signals[0].Status!=WaveLib.WaveBuffer.WaveStatus.Done&&x<5)
				{
					System.Threading.Thread.Sleep(100);
					x++;
				}
			}
			//Are we out of stims?  if so, return false
			if(run.Stims.Count<=run.nCurrentStim)
				return false;
			//get the next stim
			if(mainmenu.def.bRandom==true)
			{
				int n = (int) run.RandomOrder[run.nCurrentStim];
				run.CurrentStim = (Stim) run.Stims[n];
			}
			else
			{
				run.CurrentStim = (Stim) run.Stims[run.nCurrentStim];
			}
			//clear and initialize everything for this threshold
			ar.Clear();
			run.Levels = new ArrayList();
			run.CalcLevels.Clear();
			run.nStepType = 1;
			run.nStepSize = mainmenu.def.nInitialStep;
			run.nCurrentLevel = run.CurrentStim.nOrigin;//40;
			run.nTrialCount = 0;
			run.StdErr = 5.0f;
			run.bZeroed = false;
			run.CatchTrial = false;
			nReversals = 0;
			player.Signals[0] = run.CurrentStim.buffer;
			ar.SetMethod(run.CurrentStim.nMethod);
			//set the step size!
			return true;	
		}
		private void SetMethod(int n)
		{
			if(n==1)
				ar.Method = ARMethod.Fifty;
			else if(n==2)
				ar.Method = ARMethod.Seventy;
			else if(n==3)
				ar.Method = ARMethod.Thirty;	
		}

		private void GetThreshold()
		{
			run.CurrentStim.strDesiredResponse="1";
			PresentStim(run.nCurrentLevel);
		}

		private void PresentStim(int nLevel)
		{
			run.V2 = GetLevel(nLevel);//convert nLevel dB to a DAC integer
			run.Levels.Add(nLevel);
			run.nTrialCount++;
			run.nCurrentLevel = nLevel;
			res.ShowCurrentLevel(run.CurrentStim,run.nCurrentLevel);
			//Not using intervals
			if(this.bInterval==false)
			{
				DoNonInterval();
			}
			else if(bInterval == true)
			{
				DoInterval();
			}
		}
		private int GetLevel(int nLevel)
		{
			if(nLevel<=-15)
				nLevel = -15;
			//if greater than defined max level, reduce to max level
			if(nLevel>mainmenu.def.nMaxLevel)
				nLevel = mainmenu.def.nMaxLevel;
			int V2 = mainmenu.GetV2(nLevel,run.CurrentStim.nFreq,run.CurrentStim.strEar);
			if(V2>mainmenu.MaxAmplitude)
				V2 = mainmenu.MaxAmplitude;
			return V2;
		}
		private void DoNonInterval()
		{
			//do we want to do a catch trial?
			if(DoCatchTrial()==true)
				return;
			//If we are here, then we aren't doing a catch trial
			run.CatchTrial = false;
			CheckReversals();
			run.CurrentStim.strDesiredResponse = "1";
			//play it
			if(player.Signals[0]!=null)
			{
				//player.Signals[0].Stop();
				player.Signals[0].SetLevel((int) run.V2);
				//FileInfo f = new FileInfo(@"C:\"+run.CurrentStim.nFreq+run.CurrentStim.strType+".wav");
				//player.Signals[0].WriteWave(f);
				player.Signals[0].Play();

				if(player.Play()==false)
				{
					if(bCheck==false)
					{
						MessageBox.Show(res,"Audio Error!");
						bCheck = true;
						bButtons = true;
						return;
					}
					bCheck=false;
				}
			}
			bButtons = true;
			res.Update();
			timer.Start();
		}

		private void DoInterval()
		{
			//we are using intervals
			bButtons = false;
			Button but = (Button) mainmenu.def.Buttons[mainmenu.def.Buttons.Count-1];
			but.BackColor = this.BackColor;
			//decide which interval to use
			Random r = new Random(DateTime.Now.Millisecond);
			if(IntervalList.Count<=1)
			{
				int nB = mainmenu.def.Buttons.Count-1;
				int nX = 0;
				for(int i=0;i<100;i++)
				{
					IntervalList.Add(nX++);
					if(nX==nB)
						nX = 0;
				}
			}
			int x = r.Next(IntervalList.Count);
			run.CatchTrial = false;
			run.nInterval = (int) IntervalList[x];//interval to use
			run.nCurrentInterval = 0;//interval we are currently on
			IntervalList.RemoveAt(x);
			CheckReversals();
			run.CurrentStim.strDesiredResponse = run.nInterval.ToString();
			IntervalPlay();
		}
		private void IntervalPlay()
		{
			//Calls itself recursively to do an interval for each button
			int zz = 1;//subtract 1 from the number of buttons for the respond button
			if(mainmenu.def.bWaitForNextButton==true)
				zz++;//if we are using wait for next button, we will subtract 2 from the number of buttons
			if(run.nCurrentInterval<mainmenu.def.Buttons.Count-zz)
			{
				//if we aren't out of buttons
				Button b = (Button) mainmenu.def.Buttons[run.nCurrentInterval];
				b.BackColor = Color.White;
				this.Update();
				//do next button
				if(run.nInterval==run.nCurrentInterval)
				{
					//If the tone is going to be in this interval, play tone
					if(player.Signals[0]!=null)
					{
						//player.Signals[0].Stop();
						player.Signals[0].SetLevel((int) run.V2);
						//FileInfo f = new FileInfo(@"C:\"+run.CurrentStim.nFreq+run.CurrentStim.strType+".wav");
						//player.Signals[0].WriteWave(f);
						player.Signals[0].Play();

						//player.bStreaming = false;
						if( player.PlaySingleSignal()==false )
						{
							if(bCheck==false)
							{
								MessageBox.Show(res,"Audio Error!");
								bCheck = true;
								bButtons = true;
								return;
							}
							bCheck=false;
						}
					}
					b.BackColor = this.BackColor;
					this.Update();
					run.nCurrentInterval++;
					if(run.nCurrentInterval<mainmenu.def.Buttons.Count-zz)
						ITimerSpace.Start();
					else
						IntervalPlay();
				}
				else
				{
					//not in this interval, just wait for the length of the interval
					this.Update();
					ITimerOn.Start();
				}
			}
			else
			{
				//We're out of buttons, set them all to regular color
				bButtons = true;
				//excpet the last, which is supposed to be the "Respond" button.  Set it to white.
				Button but = (Button) mainmenu.def.Buttons[mainmenu.def.Buttons.Count-1];
				but.BackColor = Color.White;
			}
		}

		private bool DoCatchTrial()
		{
			if(mainmenu.def.bCatchTrial==false)
				return false;//not doing catchtrials
			if(mainmenu.def.bInterval==true)
				return false;//not doing catchtrials on intervals
			if(mainmenu.def.bBackToBackCatch==false && run.CatchTrial==true)
				return false;//not allowing back to backs, and we just did one
			if(run.nCurrentLevel<=-15 && run.CatchTrial==true)
			{
				/*if we are doing back to back catch trials,
				 * and we are at the bottom of the range
				 * we may have just forced a catch trial
				 * but we don't want to force another one
				 * we want it to go on an play the stim
				 * then come back later and possibly force another one
				 **/
				return false;
			}

			//randomly determine if there will be a catch trial
			Random r = new Random(DateTime.Now.Millisecond);
			int x = r.Next(100);
			if(x<mainmenu.def.nCatchFreq||(run.nCurrentLevel<=-15 && ((int)run.Levels[run.Levels.Count-1])==-15)||nCatchCount==5)
			{
				//either randomly determined there would be a catch,
				//or the last two responses were at -15
				//or the user has responded positively 5 times in a row
				nCatchCount = 0;
				res.ShowCatchTrial();
				run.CatchTrial = true;
				timerCatch.Start();
				return true;
			}
			
			return false;
		}
		private void CheckReversals()
		{
			if(run.nStepType==2&&nReversals<ar.Reversals)//secondary step size, and nReversals less than ar.reversals
			{
				run.CalcLevels.Add(run.nCurrentLevel);
				nReversals++;
				res.ShowReversals("Secondary",nReversals);
			}
			else if(nReversals<ar.Reversals)//Primary step size, just done so we can see how many
			{
				nReversals++;
				res.ShowReversals("Primary",nReversals);
			}
		}


		private void timer_Tick(object sender, EventArgs e)
		{
			if(res.IsActive())
			{
				bButtons = false;
				timer.Stop();
				CheckButton("abcdef");
			}
			else //set all timers to false
				ModifyAllTimers(false);
			
		}
		private void timer2_Tick(object sender, EventArgs e)
		{	
			if(res.IsActive())
			{
				timer2.Enabled = false;
				timer2.Stop();
				PresentStim(run.nCurrentLevel);
			}
			else
				ModifyAllTimers(false);


		}
		private void timer3_Tick(object sender, EventArgs e)
		{	
			if(res.IsActive())
			{
				timer3.Stop();
				if(player.Signals[0].Status!=WaveBuffer.WaveStatus.Done)
					timer3.Start();//wait for stim to finish
				else
					CheckButton(strButton);//inter stim delay
			}
			else
				ModifyAllTimers(false);
				
		}
		private void ModifyAllTimers(bool bStatus)
		{
			timer.Enabled=bStatus;
			timer2.Enabled=bStatus;
			timer3.Enabled=bStatus;

		}
		private void timerCatch_Tick(object sender, EventArgs e)
		{
			timerCatch.Stop();
			bButtons = false;
			CheckButton("Catch");

		}
		
		private void ITimerOn_Tick(object sender, EventArgs e)
		{/*
			ITimerOn.Stop();
			foreach(Button b in mainmenu.def.Buttons)
				b.BackColor = this.BackColor;
			this.Update();
			run.nCurrentInterval++;
			if(run.nCurrentInterval<mainmenu.def.Buttons.Count-1)
				ITimerSpace.Start();
			else
			{
				IntervalPlay();
			}
*/
		}

		private void ITimerSpace_Tick(object sender, EventArgs e)
		{/*
			ITimerSpace.Stop();
			this.IntervalPlay();
			*/
		}
		


		public void Reset()
		{
			player.Stop();
			ar.Clear();
			run.Levels.Clear();
			run.CalcLevels.Clear();
			run.nStepType = 1;
			run.nStepSize = mainmenu.def.nInitialStep;
			run.nCurrentLevel = run.CurrentStim.nOrigin;
			run.nTrialCount = 0;
			run.StdErr = 5.0f;
			run.bZeroed = false;
			run.CatchTrial = false;
			this.timerCatch.Stop();
			timer.Stop();
			timer3.Stop();
			timer2.Start();//inter stim delay
		}
		public void Skip()
		{
			timerCatch.Stop();
			timer2.Stop();
			timer3.Stop();
			timer.Stop();
			run.CurrentStim.bSkipped = true;
			res.Skip(run.CurrentStim);
			iwriter.WriteLine("{0},{1},{2},{3},{4}",run.CurrentStim.nFreq,run.nCurrentLevel,strButton,run.CurrentStim.strDesiredResponse,"SKIPPED");
			Complete();
		}

		private bool ReadResultFile()
		{
			FileInfo f = new FileInfo(mainmenu.txtResultFile.Text);
			if(f.Exists==false)
				return false;
			try
			{
				run.PreviousStims = new ArrayList();
				StreamReader input = new StreamReader(mainmenu.txtResultFile.Text);
				string strLine = input.ReadLine();
				while(strLine.Trim()!="SUBJECT DATA")
					strLine = input.ReadLine();
				string strName = input.ReadLine();
				string strBirth = input.ReadLine();
				string strGender = input.ReadLine();
				string strGroup = input.ReadLine();
				string strId = input.ReadLine();
				//down to parameters.
				//all i really want to move is the summary?
				strLine = input.ReadLine().Trim();
				while(strLine!="RESULTS")
					strLine = input.ReadLine().Trim();
				//Ear: Method: Freq:
				strLine = input.ReadLine();

				string[] arrIn;
				char[] szSeps = {'\t',' '};
				char[] szSeps2 = {'\t',':','='};
				Stim s;
				string str;
				while(strLine!=null&&strLine!="SUMMARY")
				{
					//first line if entry
					arrIn = strLine.Split(szSeps2,6);
					s = new Stim();
					s.strEar = arrIn[1].Trim();
					str = arrIn[3];
					if(str=="70%")
						s.nMethod = 2;
					else if(str=="30%")
						s.nMethod = 3;
					else
						s.nMethod = 1;
					s.nFreq = int.Parse(arrIn[5]);
					strLine = input.ReadLine();//Response/tLevel
					int n=-1;
					//read trials
					strLine = input.ReadLine();
					while(n<0)
					{
						arrIn = strLine.Split(szSeps,2);
						n = int.Parse(arrIn[0]);
						if(n==1)
							s.Responses.Add(true);
						else
							s.Responses.Add(false);
						s.Levels.Add(int.Parse(arrIn[1]));
						strLine = input.ReadLine();
						n = strLine.IndexOf("Threshold");
					}
					arrIn = strLine.Split(szSeps2,2);
					s.fThreshold = int.Parse(arrIn[1]);
					strLine = input.ReadLine();//should be blank
					strLine = input.ReadLine().Trim();//either next result, or SUMMARY line

					run.PreviousStims.Add(s);	
				}
				input.Close();
				FileInfo newf = new FileInfo(f.FullName + ".old");
				if(newf.Exists)
					newf.Delete();
				f.CopyTo(f.FullName+ ".old",true);
			}
			catch(Exception err)
			{
				MessageBox.Show(res,"Error processing append file: " + err.Message);
				return false;
			}
			return true;
		}
		private void WriteResults()
		{
			/*if(iwriter!=null)
			{
				iwriter.Flush();
				iwriter.Close();
			}*/
			try
			{
				if(run.PreviousStims!=null)
				{
					foreach(Stim a in run.PreviousStims)
					{
						bool bF = false;
						foreach(Stim b in run.Stims)
						{
							if(a.nFreq==b.nFreq)
							{
								if(a.strEar==b.strEar)
									bF = true;
							}
						}
						if(bF==false)
							run.Stims.Add(a);
					}
				}
				output = new StreamWriter(outfile.FullName,false);
				output.AutoFlush=true;
				output.WriteLine("HAL Results");
				output.WriteLine(string.Empty);
				output.WriteLine("SUBJECT DATA");
				output.WriteLine(mainmenu.txtName.Text);
				output.WriteLine(mainmenu.txtBirth.Text);
				output.WriteLine(mainmenu.cmbGender.Text);
				output.WriteLine(mainmenu.txtGroup.Text);
				output.WriteLine(mainmenu.txtId.Text);
					output.WriteLine(this.strInitFile);
				output.WriteLine(DateTime.Now.ToShortDateString());
				output.WriteLine(DateTime.Now.ToShortTimeString());
				output.WriteLine(string.Empty);
				output.WriteLine("Calibration: "+mainmenu.GetCal());
				output.WriteLine(string.Empty);

				output.WriteLine("TEST STIMULI");
				string str;
				/*if(run.PreviousStims!=null)
				{
					foreach(Stim s in run.PreviousStims)
					{
						run.Stims.Add(s);
					}
				}*/
				foreach(Stim s in run.Stims)
				{
					if(s.strEar.ToLower()=="left")
					{
						if(s.bMasking==false)
						{
							str = string.Format("{0},{1},{2}",
								s.strEar,s.nMethod,s.nFreq);
						}
						else
						{
							str = string.Format("{0},{1},{2},MASKED",
								s.strEar,s.nMethod,s.nFreq);
						}
						output.WriteLine(str);
					}
				}
				foreach(Stim s in run.Stims)
				{
					if(s.strEar.ToLower()=="right")
					{
						if(s.bMasking==false)
						{
							str = string.Format("{0},{1},{2}",
								s.strEar,s.nMethod,s.nFreq);
						}
						else
						{
							str = string.Format("{0},{1},{2},MASKED",
								s.strEar,s.nMethod,s.nFreq);
						}
						output.WriteLine(str);
					}
				}
				output.WriteLine(string.Empty);

				output.WriteLine("RESULTS");
				string strMethod = string.Empty;
				foreach(Stim s in this.run.Stims)
				{
					if(s.nMethod==1)
						strMethod = "50%";
					else if (s.nMethod==2)
						strMethod = " 70%";
					else if(s.nMethod==3)
						strMethod = " 30%";
					str = string.Format("Ear: {0}\t Method: {1}\t Frequency: {2}",
						s.strEar,strMethod,s.nFreq);
					output.WriteLine(str);
					output.WriteLine("Response\t Level");
					int k=0;
					foreach(bool n in s.Responses)
					{
						if(n==true)
						{
							if(k<s.Levels.Count)
							{
								str = string.Format("1\t {0}",
									s.Levels[k]);  
							}
							else 
								str = "1\t ??";
						}
						else
						{
							if(k<s.Levels.Count)
							{
								str = string.Format("0\t {0}",
									s.Levels[k]);
							}
							else 
								str = "1\t ??";

						}
						output.WriteLine(str);
						k++;
					}
					if(s.bSkipped==true)
						str = string.Format("SKIPPED!");
					else
						str = string.Format("Threshold = {0}",s.fThreshold.ToString("###"));
					output.WriteLine(str);
					output.WriteLine(string.Empty);
				}
				output.WriteLine("SUMMARY");
				output.WriteLine("Ear\tFreq\tThreshold\tMethod");
				foreach(Stim s in this.run.Stims)
				{
					if(s.bSkipped==true)
					{
						str = string.Format("{0}\t{1}\t{2}\t{3}",
							s.strEar,s.nFreq,"SKIPPED",s.nMethod);
					}
					else
					{
						str = string.Format("{0}\t{1}\t{2}\t{3}",
							s.strEar,s.nFreq,s.fThreshold.ToString("###"),s.nMethod);
					}
					output.WriteLine(str);
				}
				output.WriteLine("ENDSUMMARY");
				output.Flush();
				output.Close();
			}
			catch(Exception)
			{
				MessageBox.Show(res,"Error writing results file!");
			}
			Summary = true;
		}



		private bool CheckForMasking()
		{
			//Get previous
			if(run.PreviousStims!=null)
			{
				foreach(Stim a in run.PreviousStims)
				{
					bool bF = false;
					foreach(Stim b in run.Stims)
					{
						if(a.nFreq==b.nFreq)
						{
							if(a.strEar==b.strEar)
								bF = true;
						}
					}
					if(bF==false)
						run.Stims.Add(a);
				}
			}
			listMasking = new ArrayList();
			foreach(Stim l in run.Stims)
			{
				if(l.strEar == "left")
				{
					foreach(Stim r in run.Stims)
					{
						if(r.strEar == "right")
						{
							if(l.nFreq==r.nFreq)
							{
								if(Math.Abs(l.fThreshold-r.fThreshold)>=40)
								{
									if(l.fThreshold>r.fThreshold)
									{
										r.bMasking = true;
										listMasking.Add(r);
									}
									else
									{
										l.bMasking = true;
										listMasking.Add(l);
									}
								}//if Abs>=40
							}//if l = r
						}//if ear = right
					}//foreach stim r
				}//if ear == left
			}//for each stim l
			if(listMasking.Count==0)
				return false;
			else 
			{
				listNoise = new ArrayList();
				foreach(Stim s in listMasking)
				{
					NarrowBandNoise nb = new NarrowBandNoise();
					nb.Load(s.nFreq,s.strEar);
					listNoise.Add(nb);
				}
				run.nCurrentStim = 0;
				return true;
			}
		}
		
		private bool Masking()
		{
			//Do masking.  Stims in listMasking
			//generate / load narrow band noise
			//play noise at 10db below current threshold
			//retest
			//if threshold INCREASES increase noise, retest
			//else done

			Mode = MASKING;
			Stim s = run.CurrentStim;
			s.Responses = ar.Responses;
			s.Levels = run.Levels;
			SetMethod(s.nMethod);
			//average last three levels
			float f=0.0f;
			int m=3;
			if(run.CalcLevels.Count<3)
				m=run.CalcLevels.Count;
			for(int k=0;k<m;k++)
			{
				f+=(int)run.CalcLevels[run.CalcLevels.Count-1-k];
			}
			f/=m;
			if(f>run.CurrentStim.fThreshold+4)//increases by 5dB or more
			{
				run.CurrentStim.fThreshold = f;
			}
			else
			{
				//Get next Stim from Masking
				res.AddAudPoint(s,ar.standardError,nReversals);
				int n = listMasking.IndexOf(s);
				listMasking.RemoveAt(n);
				listMasking.Insert(n,s);
				run.nCurrentStim++;
				ar.Clear();
				int x=0;
				while(player.Signals[0].Status!=WaveLib.WaveBuffer.WaveStatus.Done&&x<5)
				{
					System.Threading.Thread.Sleep(100);
					x++;
				}
				if(listMasking.Count<=run.nCurrentStim)
					return false;
				run.CurrentStim = (Stim) listMasking[run.nCurrentStim];
			}
			
			ar.Clear();
			run.Levels = new ArrayList();
			run.CalcLevels.Clear();
			run.nStepType = 1;
			run.nStepSize = mainmenu.def.nInitialStep;
			run.nCurrentLevel = run.CurrentStim.nOrigin;
			run.nTrialCount = 0;
			run.StdErr = 5.0f;
			run.bZeroed = false;
			run.CatchTrial = false;
			player.Signals[0] = run.CurrentStim.buffer;
			ar.SetMethod(run.CurrentStim.nMethod);

			//Generate / Load noise and start it
			NarrowBandNoise nb = null;
			for(int i=0;i<listNoise.Count;i++)
			{
				nb = (NarrowBandNoise)listNoise[i];
				if(nb.nFreq == run.CurrentStim.nFreq)
					break;
			}
			//nb.Load(run.CurrentStim.nFreq,mat,run.CurrentStim.strEar);
			if(nb==null)
			{
				MessageBox.Show("Error loading narrow band noise!");
				return false;
			}
			
			player.Signals[1] = nb.buf;
			int nLevel = (int)run.CurrentStim.fThreshold-10;
			if(nLevel<=0)
				nLevel = 5;
			else if(nLevel>mainmenu.CalDb)
				nLevel = (int) mainmenu.CalDb;
			double V2 = mainmenu.GetV2(nLevel,1000,"both");
			if(V2>mainmenu.MaxAmplitude)
				V2 = mainmenu.MaxAmplitude;
			player.Signals[1].SetLevel((int)V2);
			player.Signals[1].Play();
			player.Play();
			return true;
		}

		public void TestMasking()
		{
			player.Stop();
			Mode = MASKING;
			MessageBox.Show("Starting Masking");
			listMasking = new ArrayList();
			foreach(Stim s in run.Stims)
			{
				//s.fThreshold = 80;
				s.bMasking = true;
				listMasking.Add(s);
			}
			listNoise = new ArrayList();
			foreach(Stim s in listMasking)
			{
				NarrowBandNoise nb = new NarrowBandNoise();
				nb.Load(s.nFreq,s.strEar);
				listNoise.Add(nb);
			}
			Masking();
			MessageBox.Show("GO");
			timer2.Start();
		}

		private void b_KeyPress(object sender, KeyPressEventArgs e)
		{
			switch(e.KeyChar)
			{
				case 'r':
				case 'R':
					this.Reset();
					break;
				case 's':
				case 'S':
					this.Skip();
					break;
				case 'p':
				case 'P':
					EventArgs ev = new EventArgs();
					res.btnPause_Click(sender, ev);
					break;
				case 'c':
				case 'C':
					res.Continue();
					break;
			}
		}
	
	}
}
