using System;
using System.Collections;
using AdaptiveResponse;
using WaveLib;
using System.IO;
using System.Drawing;

namespace HearingAidLab
{
	/// <summary>
	/// Summary description for Definitions.
	public class Definitions : object
	{
		public string strStimDirectory;					//Path to the stim files
		//Primary Stims
		public ArrayList PrimaryStims;					//Array of primary stimuli
		//Secondary Stims
		public ArrayList SecondaryStims;				//array of secondary stims
		// Paired Stims
		public ArrayList PairedStims;

		//Buttons
		public ArrayList Buttons;						//array with Button objects
		public int nButtons;							//number of buttons
		
		//Feedback
		public ArrayList Feedback;						//list of feed back files
		public string strFeedbackDir;					//path to the feedback files
		public string strGameDir;						//path for the current game files
		public Point FeedbackLoc;
		public Point FeedbackSize;

		//def & Result file
		public string strInitFile;						//.def file
		public string strResultFile;					//where results will be written

		//Results
		public ArrayList listRes;						//array with the results params

		//Masker
		public Stim Masker;								//Single stim to be used as a masker

		//Constants
		public ArrayList Methods;						//Methods, 30,50,70 to be used

		public bool bSingleScreen;						//show on single screen
		public bool bBandwidth;							//increase or decrease on correct, 
		public bool bRandom;							//Primary order is random
		public bool bCatchTrial;						//do catch trials
		public bool bBackToBackCatch;					//Allow multiple catch trials in a row
		public bool bInterval;							//do multiple interval forced choice
		public bool bWaitForNextButton;					//have a next button and wait for it before proceeding to next stim
		public bool bSecondaryContinuous;				//are secondary stims continuous
		public bool bCue;								//Play a cue before the Stim?
		public bool bCL;								//Is this a CL .def file?
		public bool bAnchoredTrials;					//CL  Use same endpoints for all trials at same freq
		public bool bMixSecondary;						// Mix secondary with primary or play each seperately												//if Achored AND Repetitions>1, use same data points for ALL trials
		public bool bIndependentTrials;					//CL  Measure Min Max each time
		public bool bPlayLRStereo;						//have seperate file for Left and right and play two together as stereo

		public int nAdaptType;							//0=None, 1 = Primary, 2 = Secondary, 3 = Primary, but treat all stims as 1 (adapt across stims)
		public int nPrimaryFilterOrder;					//Primary stim filter order - not currently used
		public int nSecondaryFilterOrder;				//Secondary stim filter order - not currently used
		public int nStepSize;							//if doing primary stims for a range of volumes, the step size to go from A to B in
		public int nRange;								//if doing primary stims for a range, how big is the total range - starts at stim default, goes up in stepsize to default+range
		public int nCatchLength;						//length of a catch trial in ms
		public int nCatchFreq;							//frequency of catch trials (10 = 10%, 50 = 50%)
		public int nPrimaryStep;						//Primary Step Size for adaptives
		public int nSecondaryStep;						//Secondary Step Size for adaptives
		public int nInitialStep;						//How big a step to go up until you get the first positive from the subject
		public int nReps;								//# of repititions for each primary - how many times to play it, find the threshold, whatever
		public int nTimer;								//How long the subject has to respond, in ms
		public int nDelay;								//ISI - how long between stimuli
		public int nJitter;								//Jitter the ISI - max to randomly add to the isi
		public int nGameDelay;							//update the game every how many responses?
		public int nGameDelayType;						//0 = Store corrects and step game that many times, 1 = or just do 1 step every x trials
		public int nStopReversals;						//How many reversals before we stop?
		public int nChangeReversals;					//How many reversals before we go to secondary step size?
		public int nIntervalLen;						//The length of an Interval when doing MIFC, in ms
		public int nIntervalSpace;						//Time, in ms, between Intervals, when doing MIFC
		public int nNumberOfTrials;						//Maximum number of trials (overall)
		public int nMaxLevel;							//Max allowed output level
		public int nStopMode;							//Stop on Std Error (0) or # of Reversals (1), or both (2)
		public int nCLButtons;							//number of buttons on a Categorical Loudness run
		public int nSpecialPatNextButton;				//Light the Next Button after every other trial
		public int nChangeFeedback;						//change feedback after this many responses, 0 for never
		public double dwStdErrVal;						//standard error to stop on

		//Categorical Loudness ISO improvements
		public int CLButtonsLayout;				// 0= default(as is ) layout, 1= ISO layout
		public bool CLUseAdaptive;				// use adaptive scaling to loudness, default=true;

		//for frequency shaping
		public bool bFrequencyShaping;					//Are we doing FreqShaping?
		public int nFreqShapeSecondary;				//Are we doing the secondary?  0 = null, 1 = yes, 2 = no
		public float[] LAtten;							//left channel atten db
		public float[] LFreqs;							//left channel freqs
		public float[] RAtten;							//right channel atten db
		public float[] RFreqs;							//left channel freqs
		public string strSecondaryOrder;				//Secondary stims order: random, randomtrial, block, randominterval

		public AdaptiveTypes PrimaryAdapt;				//what are we adapting for the primary, Level,lowpass,hipass,bandpass,bandstop ONLY LEVEL HAS BEEN TESTED

	
			
		public Definitions()
		{
            strStimDirectory = string.Empty;

			PrimaryStims = new ArrayList();
			SecondaryStims = new ArrayList();
			PairedStims = new ArrayList(); 

			Buttons = new ArrayList();
			Feedback = new ArrayList();

			strInitFile = "Init.def";
			strResultFile = "Result.txt";

			listRes = new ArrayList();
/*			listRes.Add("Ear");
			listRes.Add("StimCat");
			listRes.Add("AltCat");*/

			Masker = null;

			bWaitForNextButton = false;
			bInterval = false;
			bSingleScreen = false;
			bBandwidth = false;
			bRandom = true;
			bCatchTrial = false;
			bBackToBackCatch = false;
			bSecondaryContinuous = false;
			bCue = false;
			bFrequencyShaping = false;
			nFreqShapeSecondary = 0;
			bCL = false;
			bAnchoredTrials = false;
			bMixSecondary = false;
			bPlayLRStereo = false;

			nAdaptType = 0;
			nIntervalLen = 2000;
			nIntervalSpace = 250;
			nMaxLevel = 120;
			nNumberOfTrials = -1;
			nStopMode = 0;
			nStopReversals = 6;
			nChangeReversals = 5;
			nStepSize = 0;
			nRange = 0;
			nButtons = 2;
			nPrimaryStep = 10;
			nSecondaryStep = 5;
			nInitialStep = 20;
			nCatchLength = 500;
			nCatchFreq = 5;
			nReps = 1;
			nTimer = 600000;
			nDelay = 1000;
			nJitter = 0;
			nGameDelay = 1;
			nGameDelayType = 0;
			nCLButtons = 11;
			nSpecialPatNextButton = 0;
			dwStdErrVal = 1.8;
			nChangeFeedback=1;

			PrimaryAdapt = AdaptiveTypes.None;

			strFeedbackDir = string.Empty;
			strGameDir = string.Empty;
			strSecondaryOrder = "Block";
			FeedbackLoc = new Point(100,10);
			FeedbackSize = new Point(600,500);
			Methods = new ArrayList();
			
			CLButtonsLayout = 0 ;//0= default layout
			CLUseAdaptive = true;
		}
		public void Clear()
		{
			strStimDirectory = string.Empty;

			PrimaryStims = new ArrayList();
			SecondaryStims = new ArrayList();

			Buttons = new ArrayList();
			Feedback = new ArrayList();

			strInitFile = "Init.def";
			strResultFile = "Result.txt";

			listRes = new ArrayList();
			/*			listRes.Add("Ear");
						listRes.Add("StimCat");
						listRes.Add("AltCat");*/

			Masker = null;

			bWaitForNextButton = false;
			bInterval = false;
			bSingleScreen = false;
			bBandwidth = false;
			bRandom = true;
			bCatchTrial = false;
			bBackToBackCatch = false;
			bSecondaryContinuous = false;
			bCue = false;
			bFrequencyShaping = false;
			nFreqShapeSecondary = 0;
			bCL = false;
			bMixSecondary = true;

			nAdaptType = 0;
			nIntervalLen = 2000;
			nIntervalSpace = 250;
			nMaxLevel = 120;
			nNumberOfTrials = -1;
			nStopMode = 0;
			nStopReversals = 6;
			nChangeReversals = 5;
			nStepSize = 0;
			nRange = 0;
			nButtons = 2;
			nPrimaryStep = 10;
			nSecondaryStep = 5;
			nInitialStep = 20;
			nCatchLength = 500;
			nCatchFreq = 5;
			nReps = 1;
			nTimer = 600000;
			nDelay = 1000;
			nJitter = 0;
			nGameDelay = 1;
			nGameDelayType = 0;
			nCLButtons = 11;
			nSpecialPatNextButton = 0;
			dwStdErrVal = 1.8;

			PrimaryAdapt = AdaptiveTypes.None;

			strFeedbackDir = string.Empty;
			strGameDir = string.Empty;
			strSecondaryOrder = "Block";
			
			Methods = new ArrayList();

			CLButtonsLayout = 0 ;//0= default layout
			CLUseAdaptive = true;
		}
	}

	public class ChirpCal
	{
		public ArrayList Cal;
		public ArrayList Response;
		public double dLevel;
		public int nMic;
		public int nMaxInt;
		public double dMaxOut;
		public double dMaxIn;

		public double Gain;
		public double Vim;
		public double Vref;
		public double Ref;
		public double io;
		public double im;

		public string strFile;

		public ChirpCal()
		{
			strFile = string.Empty;
			dLevel = 2000000.0;
			nMic = 1;
			nMaxInt = 8388607;
			dMaxOut = 4.875;
			dMaxIn = 10.20;

			Gain = 0.0;
			Vim = 0.0;
			Vref = 0.0;
			Ref = 0.0;
			io = 0.0;
			im = 0.0;

			Response = new ArrayList();
			Cal = new ArrayList();
		}

		public void Init()
		{
			if(nMic==0)
				Gain = 1;
			else
				Gain = nMic;
			Vim = dMaxIn*0.707;
			if(Gain==20)
				Vref = 0.000010; //20 = 10uV
			else if(Gain==40)
				Vref = 0.000100; //40 = 100uV
			else
				Vref = 0.000001; //0 = 1uV
			Ref = 20*Math.Log10(Vim/Vref);
			io = dLevel;
			im = nMaxInt;
		}

		public int CalcInt(double db, int Freq)
		{
			/*int ff = Freq/2 -1;
				if(ff>Cal.Count)
					ff = Cal.Count-1;*/
			//Frequecy Range is 0-16K, over 4000 Samples
			//it was 8000 samples, but that gets cut in half because of the
			//fft.
			//to find a frequency:
			// 16K / 4096 Samples = 3.90625 Hz / Sample
			// DesiredFreq / 3.90625 = Sample that is that freq
			// 1000 / 3.90625 = 256, sample 256 is the desired sample
			//int ff = (int) (Freq/3.90625);
			//int ff = (int) (Freq/4);

			//I'm really not sure about this.  It might be right, it might not.
			int ff = Freq;
				
			try
			{
				double SPLmax = Ref + (double) Cal[ff];// - Gain;//used to be -gain, 
				//changed trying to figure out why it doesn't work in +20 mode
				int r = (int) (im * Math.Pow(10,((db-SPLmax)/20)));
				return r;
			}
			catch(Exception err)
			{
				System.Windows.Forms.MessageBox.Show(err.Message + "  " + Freq.ToString());
				return 0;
			}
		}
		public double GetMax(int Freq)
		{
			double SPLmax = Ref + (double) Cal[Freq];
			return SPLmax;
		}
	}
	public class Stim : object
	{
		public string strText;							//the text declaration of the signal, from the def file
		public string strStimDirectory;					//directory to stim file
		public WaveLib.WaveBuffer buffer;				//memory buffer holding the signal
		public bool bWav;								//is it a wav
		public bool bLoop;								//loop it?
		public string strEar;							//which ear to play it in
		public int nMethod;								//adaptive method to use
		public int nFreq;								//frequency or center freq
		public int nLength;								//length of the stim - if pulsed, length of each pulse
		public int nRise;								//rise time, in ms
		public int nFall;								//fall time, in ms
		public int nOctave;								//if we're working with fractions of an octave, the denominator
		public int nInterval;							//if it has an interval, how long is it
		public string strType;							//tone, nbn, wave, the stim type
		public string strDesiredResponse;				//what response you want.  the 'stimcat' field from declaration
		public string strInfo;							//where the 'altcat' data goes
		public bool bMasking;							//was masking used
		public float fThreshold;						//the calculated threshold, if adaptive
		public string strFile;							//the filename associated with the stim, if any
		public ArrayList Responses;						//Each response collected
		public ArrayList Levels;						//The Levels that the stim was played at
		public WaveLib.AmplificationTypes AmpType = AmplificationTypes.Peak;//amplification type to use on the signal
		public int nOrigin;								//what db to play the signal at (or originate the signal at, if adaptive)
		public bool bSkipped;							//user hit the skip button while this stim was being tested
		public int nPhase;								//Phase, in degrees, at t0
		public int nInitialPhase;
		//Use for complex stims
		public int nStart;								//start time, used for complex signals
		public int nStop;								//stop time, relative to overall complex stim
		public bool bAdapt;								//are we adapting this stim?
		public bool bIsComplexMember;					//is this a part of a complex stim
		public Stim()
		{
			strText = string.Empty;
			buffer = new WaveBuffer();
			bWav = false;
			strEar = string.Empty;
			nMethod = 0;
			nFreq = 0;
			nLength = 0;
			nRise = 0;
			nFall = 0;
			nInterval = 0;
			strType = string.Empty;
			strDesiredResponse = "1";
			bMasking = false;
			//nMaskingValue = 0;
			fThreshold = 0.0f;
			strFile = string.Empty;
			bLoop = false;
			nOrigin = 40;
			strInfo = string.Empty;
			Responses = new ArrayList();
			Levels = new ArrayList();
			bSkipped = false;
			nPhase = 0;
			nInitialPhase = 0;
			nStart = 0;
			nStop = 0;
			bAdapt = false;
			bIsComplexMember = false;
		}

		public Stim Clone()
		{
			Stim s = new Stim();
			s.buffer = buffer;
			s.bWav = bWav;
			s.bLoop = bLoop;
			s.strEar = strEar;
			s.nMethod = nMethod;
			s.nFreq = nFreq;
			s.nLength = nLength;
			s.nRise = nRise;
			s.nFall = nFall;
			s.nOctave = nOctave;
			s.nInterval = nInterval;
			s.strType = strType;
			s.strDesiredResponse = strDesiredResponse;
			s.strInfo = strInfo;
			s.bMasking = bMasking;
			s.fThreshold = fThreshold;
			s.strFile = strFile;
			s.Responses = (ArrayList) Responses.Clone();
			s.Levels = (ArrayList) Levels.Clone();
			s.nOrigin = nOrigin;
			s.AmpType = AmpType;
			s.nPhase = nPhase;
			s.nStart = nStart;
			s.nStop = nStop;
			s.bAdapt = bAdapt;
			s.bIsComplexMember = bIsComplexMember;
			s.strStimDirectory = strStimDirectory;
			return s;
		}
	
		// Copy differs from clone in that Clone still persists reference to 'buffer'
		// while Copy makes a new 'buffer'. Effect is that 'copy' creates a new stim with
		// a different 'buffer' from this object.
		public Stim Copy()
		{
			Stim s = new Stim();
			s.buffer = new WaveBuffer();
			s.bWav = bWav;
			s.bLoop = bLoop;
			s.strEar = strEar;
			s.nMethod = nMethod;
			s.nFreq = nFreq;
			s.nLength = nLength;
			s.nRise = nRise;
			s.nFall = nFall;
			s.nOctave = nOctave;
			s.nInterval = nInterval;
			s.strType = strType;
			s.strDesiredResponse = strDesiredResponse;
			s.strInfo = strInfo;
			s.bMasking = bMasking;
			s.fThreshold = fThreshold;
			s.strFile = strFile;
			s.Responses = (ArrayList) Responses.Clone();
			s.Levels = (ArrayList) Levels.Clone();
			s.nOrigin = nOrigin;
			s.AmpType = AmpType;
			s.nPhase = nPhase;
			s.nStart = nStart;
			s.nStop = nStop;
			s.bAdapt = bAdapt;
			s.bIsComplexMember = bIsComplexMember;
			s.strStimDirectory = strStimDirectory;
			return s;
		} 
	}
	public class ComplexStim : Stim
	{
		public int nTotalLength;						//Length of the overall complex signal
		public int nNumberOfStims;					//Number of component stims
		public ArrayList listStims;				//list of stims
		public ComplexStim()
		{
			nTotalLength = 0;
			nNumberOfStims = 0;
			listStims = new ArrayList();
		}
		public void SetLevel(int nLevel,Form1 main)
		{
			/*******************************
			 * This is a little weird
			 * Level is handled on an ongoing basis 
			 * in both star and audiogram.
			 * Meaning, we start at 40 (or whatever) and adaptations change that by 
			 * + or - the step size.
			 * The difficulty here is that we have multiple stims, and each could have a
			 * different initial level.  So, I'm using the CurrentLevel params in Star
			 * to maintain a delta - that is, it starts at 0 and will be incremented or
			 * decremented by the step size amount.  That amount will then be added to
			 * the sub stims initial level to get the current level
			 * 
			 * I hope
			 * */
			for(int i=0;i<nNumberOfStims;i++)
			{
				Stim s = (Stim) listStims[i];
				if(s.bAdapt==true)
				{
					int n = s.nOrigin+nLevel;
					int V2 = main.GetV2(n,s.nFreq,s.strEar);
					s.buffer.SetLevel(V2);
				}
			}
		}
		public void Mix()
		{
			/**
			 * Mix the component signals together, 
			 * paying attention to ramping and on/off times
			 * */
			int bufferLength = nTotalLength/1000*44100*8;
			MemoryStream ms = new MemoryStream(bufferLength);//Seconds*44100SamplesPerSecond*8BytesPerSample
			BinaryWriter wr = new BinaryWriter(ms);
			ms.Seek(0,SeekOrigin.Begin);
			double f = 1/44100.0;
			double t = 0;
			int a;
			int l;
			int r;
			BinaryReader[] arr = new BinaryReader[nNumberOfStims];
			Stim[] stims = new Stim[nNumberOfStims];
			for(int i=0;i<nNumberOfStims;i++)
			{
				stims[i] = (Stim) listStims[i];
				arr[i] = stims[i].buffer.reader;
				stims[i].buffer.ms.Seek(0,SeekOrigin.Begin);
			}
			while(t<nTotalLength/1000.0)
			{
				l = 0;
				r = 0;
				for(int i=0;i<this.nNumberOfStims;i++)
				{
					if(t>=stims[i].nStart/1000.0&&t<=stims[i].nStop/1000.0)
					{
						a = arr[i].ReadInt32();
						l+=a;
						a = arr[i].ReadInt32();
						r+=a;
					}
				}
				wr.Write(l);
				wr.Write(r);
				t+=f;
			}
			buffer.ManualBuffer(ref ms);
		}
		public bool AtMax(int nLevel, int Max)
		{
			for(int i=0;i<nNumberOfStims;i++)
			{
				Stim s = (Stim) listStims[i];
				if(s.bAdapt==true)
				{
					int n = s.nOrigin+nLevel;
					if(n>Max)
						return true;
				}
			}
			return false;
		}
		public bool AtMin(int nLevel, int Min)
		{
			for(int i=0;i<nNumberOfStims;i++)
			{
				Stim s = (Stim) listStims[i];
				if(s.bAdapt==true)
				{
					int n = s.nOrigin+nLevel;
					if(n<Min)
						return true;
				}
			}
			return false;
		}
	}
	public class CLData
	{
		public Stim s;
		public float[] L = new float[50];
		public SortedList TestedLevels = new SortedList();
        public ArrayList PastLevels = new ArrayList();
		public float Max;
		public int MaxLoudness = 45;
		public float Min;
		public float[] arrMin = {0,0,0};
		public float[] arrMax = {0,0,0};
		public int index = 0;
		public CLData()
		{
			s = null;
			Max = -1;
			Min = -1;
		}
		public void SetMin()
		{
			//if any two are = , that is the min, else average them
			Min = GetVal(arrMin);
			index = 0;
		}
		public void SetMax()
		{
			Max = GetVal(arrMax);
			index = 0;
		}
		public float GetVal(float[] arr)
		{
			if(arr[0]==arr[1]||arr[0]==arr[2])
				return arr[0];
			else if(arr[1]==arr[2])
				return arr[1];
			else
			{
				double total = arr[0]+arr[1]+arr[2];
				total /= 3.0;
				int x = (int) (total/ 2.5);
				return x*2.5f;
			}
			
		}
	}
	public class Run : object
	{
		public Stim CurrentStim;
		public ArrayList Stims;
		public ArrayList PreviousStims;
		public AdaptiveTypes AdaptType;
		public int nTotalStims;
		public int nCurrentStim;

		public string CurrentResponse;
		public string strAction;

		public int nCurrentLevel;
		public int nCurrentCutoff;//if necessary
		public int F2;

		public ArrayList Levels;
		public ArrayList CalcLevels;
		public ArrayList RandomOrder;
		
		public bool CatchTrial;
		public bool bCurrentResponse;
		public bool bZeroed;
		public int nCurrentGame;
		public int nTrialCount;
		
		public int nStepType;//0  = intial, 1 = primary, 2 = secondary
		public int nStepSize;
		public int nCalDb;
		public double v1;
		public double StdErr;

		public int nInterval;
		public int nCurrentInterval;
		public double V2;
		
		public int nHits;
		public int nCatches;
		public int nFalse;
		public int nMiss;
		public Run()
		{
			V2 = 0;
			PreviousStims = new ArrayList();
			nCurrentGame = 0;
			nTotalStims = 0;
			nCurrentStim = 0;
			bCurrentResponse = false;
			Levels = new ArrayList();
			CalcLevels = new ArrayList();
			nCurrentLevel = -100;
			bZeroed = false;
			CatchTrial = false;
			nTrialCount = 0;
			nStepType = 0;
			nStepSize = 20;
			nCalDb = 0;
			v1 = 0.0;
			StdErr = 0.0;
			F2=0;
			nInterval = -1;
			
			Stims = new ArrayList();
			
			
			CurrentStim = new Stim();

			nHits = 0;
			nCatches = 0;
			nFalse = 0;
			nMiss = 0;
		}
	}

	public class NarrowBandNoise
	{
		public WaveBuffer buf;
		public int nFreq;
		public string strEar;
		public int MaxAmplitude;
		public NarrowBandNoise()
		{
			buf = new WaveBuffer();
			nFreq = 0;
		}
		public bool Load(int Freq, string Ear)
		{
			if(Ear == "left")
				strEar = "right";
			else
				strEar = "left";

			nFreq = Freq;
			try
			{
				int nPole1 = (int) ( Freq/(Math.Pow(2,(1.0/(2*2)))));
				int nPole2 = (int) (Freq*(Math.Pow(2,(1.0/(2*2)))));
				MemoryStream ms;
				SigPro.GenerateFilteredNoise(out ms,1000,"bandpass",nPole1,nPole2,20,20);
				buf.ManualBuffer(ref ms);
			}
			catch(Exception /*err*/)
			{
				return false;
			}
			return true;

		}

	}
	
/*	public class ButtonTag : object
	{
		public string strCat;
		public string strPic;
		public ButtonTag()
		{
			strCat = string.Empty;
			strPic = string.Empty;
		}
	}*/
}

