using System;
using System.IO;
using System.Collections;

namespace SteganoWave
{
	/// <summary>
	/// Class That HelpYou To Get Only Data Array Of WAV File That you will pass to it.
	/// </summary>
	public class Cls_OpenWavFile
	{
		private BinaryReader RBin_ReadWavFile;
		private short[] ShortsArr_Data;
		public long FileLenght;
		public ArrayList AoutoCorrArr = new ArrayList();
		public int FrameLength = 0;
		private const int BytesPerSample = 2;
		private WaveFormat format = new WaveFormat(11025, BytesPerSample*8, 2);
		public byte[] DataShorts
		{
			get{ return ShortsArr_Data; }
			set
			{
				this.ShortsArr_Data = value;
			}
		}
		
		public Cls_OpenWavFile(string Path)
		{
			bool CaseAT = true ,CaseTA = true;
			string Str_BufferContent;
			byte[] TempArrByteBuffer = new byte[2];
			Stream Str = File.Open(Path,FileMode.Open);
			RBin_ReadWavFile = new BinaryReader(Str);
			while(CaseAT && CaseTA)
			{
				TempArrByteBuffer = RBin_ReadWavFile.ReadBytes(2);
				Str_BufferContent = System.Text.Encoding.ASCII.GetString(TempArrByteBuffer);
				if(Str_BufferContent == "at")
					CaseAT = false;
				else if(Str_BufferContent == "ta")
					CaseTA = false;

			}
			if(!CaseTA)
			{
				FileLenght = (long) RBin_ReadWavFile.ReadInt32();
				BytesArr_DataBytes = RBin_ReadWavFile.ReadBytes((int)FileLenght);

			}
			else
			{
				RBin_ReadWavFile.ReadBytes(1);
				//Str_BufferContent = GetStringFromByteArr(RBin_ReadWavFile.ReadBytes(4));
				//FileLenght = long.Parse(Str_BufferContent);
				FileLenght = (long) RBin_ReadWavFile.ReadInt32();
				BytesArr_DataBytes = RBin_ReadWavFile.ReadBytes((int)FileLenght);

				
				
			}
			Str.Close();

		}
		public Cls_OpenWavFile()
		{
		}
		private string GetStringFromByteArr(byte[] BytesArr)
		{
			string temp ="";
			foreach(byte By in BytesArr)
				temp += By.ToString();
			return temp;
		}

		public long[] Energy(byte[] RecordedBytesArr)
		{
			int NumberOfFrames = 0;
			long Offset =0;
			int Rem =0, i;
			long Box = 0;
			long[] EnergyArray;
			if(RecordedBytesArr.Length % FrameLength == 0)
				NumberOfFrames = RecordedBytesArr.Length / FrameLength;

			else
			{
				Rem = RecordedBytesArr.Length % FrameLength;
				NumberOfFrames = (RecordedBytesArr.Length / FrameLength) + 1;
			}
			EnergyArray =new long[NumberOfFrames];

			for( i=0;i<NumberOfFrames-1;i++)
			{
				Box = 0;
				for(long j= Offset;j<Offset+FrameLength;j++)
					Box += (long)(int.Parse(RecordedBytesArr[j].ToString())*int.Parse(RecordedBytesArr[j].ToString()));	
				
				EnergyArray[i] = Box;
				Offset += FrameLength;
			}
			if(Rem ==0)
			{
				Box = 0;
				for(long j= Offset;j<Offset+FrameLength;j++)
					Box += (long)(int.Parse(RecordedBytesArr[j].ToString())*int.Parse(RecordedBytesArr[j].ToString()));	
			
				EnergyArray[i] = Box;}
			else
			{
				Box = 0;
				for(long j= Offset;j<Offset+Rem;j++)
				{
					Box += (long)(int.Parse(RecordedBytesArr[j].ToString())*int.Parse(RecordedBytesArr[j].ToString()));	
				}
				EnergyArray[i] = Box;
			}
			return EnergyArray;
		}	
		public long[] Energy()
		{
			int NumberOfFrames = 0;
			long Offset =0;
			int Rem =0, i;
			long Box = 0;
			
			if(BytesArr_DataBytes.Length % FrameLength == 0)
				NumberOfFrames = BytesArr_DataBytes.Length / FrameLength;

			else
			{
				Rem = BytesArr_DataBytes.Length % FrameLength;
				NumberOfFrames = (BytesArr_DataBytes.Length / FrameLength) + 1;
			}
			long[] EnergyArray =new long[NumberOfFrames];

			for( i=0;i<NumberOfFrames-1;i++)
			{
				Box = 0;
				for(long j= Offset;j<Offset+FrameLength;j++)
					Box += (long)(int.Parse(BytesArr_DataBytes[j].ToString())*int.Parse(BytesArr_DataBytes[j].ToString()));	
				
				EnergyArray[i] = Box;
				Offset += FrameLength;
			}
			if(Rem ==0)
			{
				Box = 0;
				for(long j= Offset;j<Offset+FrameLength;j++)
					Box += (long)(int.Parse(BytesArr_DataBytes[j].ToString())*int.Parse(BytesArr_DataBytes[j].ToString()));	
			
				EnergyArray[i] = Box;}
			else
			{
				Box = 0;
				for(long j= Offset;j<Offset+Rem;j++)
				{
					Box += (long)(int.Parse(BytesArr_DataBytes[j].ToString())*int.Parse(BytesArr_DataBytes[j].ToString()));	
				}
				EnergyArray[i] = Box;
			}
			return EnergyArray;
		}	
		
		private int Sign(int Value)
		{
			if(Value > 0)
				return 1;
			else
				return -1;
		}
		public int[]  ZeroCrosing(byte[] RecordedBytesArr)
		{
			int[] EffectedData = SingSignal(RecordedBytesArr);
			int NumberOfFrames = 0;
			long Offset =0;
			int Rem =0, i;
			int Box = 0;
			int[] ZeroArray;
			if(EffectedData.Length % FrameLength == 0)
				NumberOfFrames = EffectedData.Length / FrameLength;

			else
			{
				Rem = EffectedData.Length % FrameLength;
				NumberOfFrames = (EffectedData.Length / FrameLength) + 1;
			}
			ZeroArray =new int[NumberOfFrames];

			for( i=0;i<NumberOfFrames-2;i++)
			{
				Box = 0;
				for(long j= Offset;j<Offset+FrameLength;j++)
					Box += Math.Abs(
						Sign(int.Parse(EffectedData[j].ToString())) - 
						Sign(int.Parse(EffectedData[j+1].ToString())));	
				
				ZeroArray[i] = Box/2;
				Offset += FrameLength;
			}
			if(Rem ==0)
			{
				Box = 0;
				for(long j= Offset;j<Offset+FrameLength;j++)
					Box += Math.Abs(
						Sign(int.Parse(EffectedData[j].ToString())) - 
						Sign(int.Parse(EffectedData[j+1].ToString())));	
			
				ZeroArray[i] = Box/2;}
			else
			{
				Box = 0;
				for(long j= Offset;j<Offset+Rem;j++)
				{
					Box += Math.Abs(
						Sign(int.Parse(EffectedData[j].ToString())) - 
						Sign(int.Parse(EffectedData[j+1].ToString())));	
				}
				ZeroArray[i] = Box /2;
			}
			return ZeroArray;
		}
		private void FindMinMax(out long Min,out long Max,long[] Data)
		{
			Min = Data[0];
			Max = Data[0];
			for(int i=0;i<Data.Length;i++)
			{
				if(Data[i]<=Min)
					Min = Data[i];
				if(Data[i]>=Max)
					Max = Data[i];
			}
		}
		private void FindMinMax(out int Min,out int Max,int[] Data)
		{
			Min = Data[0];
			Max = Data[0];
			for(int i=0;i<Data.Length;i++)
			{
				if(Data[i]<=Min)
					Min = Data[i];
				if(Data[i]>=Max)
					Max = Data[i];
			}
		}
		private void FindMinMax(out long Max1,out int Max2,long[] Data1,int[] Data2)
		{
			Max1 = Data1[0];
			Max2 = Data2[0];
			for(int i=0;i<Data1.Length;i++)
			{
				if(Data1[i]>=Max1)
					Max1 = Data1[i];
				if(Data2[i]>=Max2)
					Max2 = Data2[i];
			}
		}
		public long[] Normalize(long[] DataToBeNonrmlized,double from2,double to2)
		{	
			long Min,Max;
			FindMinMax(out Min,out Max,DataToBeNonrmlized);
			double from1 = (double)Min, to1 = (double)Max;
			double temp = 0;
			long[] NormlizedLongArr = new long[DataToBeNonrmlized.Length];
			for(int i=0;i<DataToBeNonrmlized.Length;i++)
			{
				temp=((double)(DataToBeNonrmlized[i]-from1)/(double)(to1-from1))*(double)(to2-from2) + from2;
				NormlizedLongArr[i] = (long)temp;
			}
			return NormlizedLongArr;
		}
		public void DrawWave(byte[] Data)
		{
			WaveForm WForm = new WaveForm();
			WForm.ByteData = Data;
			WForm.Paint +=new System.Windows.Forms.PaintEventHandler(WForm.WForm_PaintBytes);
			WForm.Show();
		}
		public void DrawWave(byte[] Data,System.Windows.Forms.PictureBox Pic)
		{
			WaveForm WForm = new WaveForm();
			WForm.ByteData = Data;
			WForm.Paint +=new System.Windows.Forms.PaintEventHandler(WForm.WForm_PaintBytes);
			Pic.Paint +=new System.Windows.Forms.PaintEventHandler(WForm.WForm_PaintBytes);
			WForm.Show();
		}

		public void DrawWave(long[] Data)
		{
			WaveForm WForm = new WaveForm();
			WForm.LongData = Data;
			WForm.Paint +=new System.Windows.Forms.PaintEventHandler(WForm.WForm_PaintLong);
			WForm.Show();
		}

		public void DrawWave(int[] Data)
		{
			WaveForm WForm = new WaveForm();
			WForm.IntegersData = Data;
			WForm.Paint +=new System.Windows.Forms.PaintEventHandler(WForm.WForm_PaintIntegers);
			WForm.Show();
		}

		public byte[] EndPointDetection(byte[] Data,double Ec,double Zc)
		{
			long[] EnergyArray = Energy(Data);	
			int[] ZeroCrossArr  = ZeroCrosing(Data);
			long EnergyMax = 0;
			int ZeroCrossMax = 0,Counter = 0;
			int EIndex =0,ZeroIndex = 0,StartFrame = 0,EndFrame = 0;
			FindMinMax(out EnergyMax,out ZeroCrossMax,EnergyArray,ZeroCrossArr);
			double temp = EnergyMax * Ec;
			EnergyMax =(long)(temp);
			temp = ZeroCrossMax * Zc;
			ZeroCrossMax = (int)(temp);
			while(EIndex == 0 && Counter<EnergyArray.Length )
			{
				if(EnergyArray[Counter] >= EnergyMax)
					EIndex = Counter;
				Counter++;
			}
			ZeroIndex = EIndex;
			Counter = 0;
			while(ZeroIndex == 0 && Counter<EnergyArray.Length )
			{
				if(ZeroCrossArr[Counter] >= Zc)
					ZeroIndex = Counter;
				Counter++;
			}
			StartFrame = ZeroIndex;
			//Ending Of Frame
			EIndex = EnergyArray.Length-1;
			Counter = EnergyArray.Length-1;
			while(EIndex == EnergyArray.Length-1 && Counter >= 0 )
			{
				if(EnergyArray[Counter] >= EnergyMax)
					EIndex = Counter;
				Counter --;
			}
			ZeroIndex = EIndex;
			Counter = EnergyArray.Length-1;
			while(ZeroIndex == EIndex && Counter >= EIndex )
			{
				if(ZeroCrossArr[Counter] >= Zc)
					ZeroIndex = Counter;
				Counter--;
			}
			EndFrame = ZeroIndex;
			int NewDataLength  = Data.Length -((StartFrame*FrameLength)+(Data.Length-(EndFrame*FrameLength)));
			byte[] ArrAfterEPD = new byte[NewDataLength];
			int NewSatr = StartFrame*FrameLength,NewEnd = EndFrame*FrameLength;
			for(int i=NewSatr;i< NewEnd;i++)
			{
				ArrAfterEPD[i-NewSatr] = Data[i];
			}
			return ArrAfterEPD;

		}
		
		private long GetAoutoOfK(int K,int[] data,int StartIndex,int EndIndex)
		{
			long Box = 0;
			for(int i = StartIndex; i < EndIndex-K; i++)
			{
				Box += (data[i] * data[i+K]);
			}
			return Box;
		}
		private long GetAoutoOfK(int K,byte[] data,int StartIndex,int EndIndex)
		{
			long Box = 0;
			for(int i = StartIndex; i < EndIndex-K; i++)
			{
				Box += (data[i] * data[i+K]);
			}
			return Box;
		}
		private int GetFramePitch(byte[] Data1,int StartIndex,int EndIndex)
		{
			//Sign Data or not
			int[] Data = SingSignal(Data1);
			long[] AoutoCorr = new long[EndIndex-StartIndex];
			for(int i = StartIndex; i < EndIndex; i++)
			{
				AoutoCorr[i - StartIndex] = GetAoutoOfK(i-StartIndex,Data,StartIndex,EndIndex);
			}
			//Add AoutoCorrArr
			AoutoCorrArr.Add(AoutoCorr);
			long Max = 0,Min = 0;
			FindMinMax(out Min,out Max,AoutoCorr);
			Max = (long)(Max/3);
			for(int i = 0; i < AoutoCorr.Length; i++)
			{
				if(AoutoCorr[i] < Max)
					AoutoCorr[i] += Max;
				else
					AoutoCorr[i] -= Max;
			}
			long ApproxValue = AoutoCorr[0] /3;
			long temp = 0;
			int PitchIndex1 = 0,PitchIndex2 = 0;
			for(int i = 0; i < AoutoCorr.Length-1; i++)
			{
				temp = AoutoCorr[i]-AoutoCorr[i+1];
				if(temp < 0 )
				{	//Complete the other indeex
					PitchIndex1 = i+1;
					for(int j = PitchIndex1+1; j< AoutoCorr.Length-1; j++)
					{
						temp = AoutoCorr[j]-AoutoCorr[j+1];
						if(temp < 0 )
						{	//Complete the other indeex
							PitchIndex2 = j+1;
							break;
						}


					}
					break;
				}
			
			}
			
			return (int)PitchIndex2-PitchIndex1;

		}

		public int[] PitchDetection(byte[] Data,long EnergyThresh)
		{
			long[] DataEnergy = Energy(Data);
			int Count = Data.Length / FrameLength;
			int[] PitchsInd = new int[Count];
			for(int i = 0;i< Count;i++)
			{
				if(DataEnergy[i] >= EnergyThresh)
				{
					int start = i * FrameLength;
					PitchsInd[i] = GetFramePitch(Data,start,start+FrameLength);
				}
			}	
			return PitchsInd;
		}
		public void SaveData(string FileName,byte[] Data)
		{
			MemoryStream recordedData = new MemoryStream(Data,false);
			MemoryStream recordedStream = WaveStream.CreateStream(recordedData, format);
			Stream Str_ToWriteData =  File.Open(FileName,FileMode.Create);
			recordedStream.WriteTo(Str_ToWriteData);
			Str_ToWriteData.Close();
			recordedStream.Close();
		}
		public int[] SingSignal(byte[] Data)
		{
			int[] ReturnData = new int[Data.Length];
			for(int i=0;i<Data.Length;i++)
			{
				ReturnData[i] = Data[i] - 128;
			}
			return ReturnData;
		}
		public int[] UnSingSignal(byte[] Data)
		{
			int[] ReturnData = new int[Data.Length];
			for(int i=0;i<Data.Length;i++)
			{
				ReturnData[i] = Data[i] + 128;
			}
			return ReturnData;
		}
	}
}
