using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using QCRPG.Framework;
using QCRPG.TSL.BT.Packets;

namespace QCRPG.TSL.BT;

public class SCODataQueue : QCRPG.Framework.IObserver<PacketBase>
{
	private enum SCODataState
	{
		IDLE,
		RECEIVING_DATA
	}

	private const double AMPLITUDE_RESOLUTION = 327.0;

	private SpecificPacketNotifier specificPacketNotifier;

	private Queue<SynchronousData> scoDataQueue = new Queue<SynchronousData>();

	private BTAPI btAPI;

	public bool abort;

	private Logger logger;

	private int currentAvailable;

	private bool enableTx;

	private ManualResetEvent parentShutdownEvent;

	private List<uint> connectionHandles = new List<uint>();

	private string wavFileLocation = "";

	private int currentSCOConnectionHandler;

	private Thread sendScoThread;

	private bool sendSco;

	private bool startTx;

	private List<SynchronousData> scoTxPacketList;

	private int scoTxPacketIndex;

	private uint scoDataLength;

	private volatile bool scoStarted;

	private uint frequency = 1000u;

	private uint amplitude = 100u;

	private uint sampleSize = 16u;

	private uint sampleRate = 8000u;

	private uint durationSec = 30u;

	private string tempFileLocation;

	private Thread saveReceivedScoToFileThread;

	private ManualResetEvent saveScoToFileEvent = new ManualResetEvent(initialState: false);

	private FileStream scoReceivedFileStream;

	private Queue<SynchronousData> scoReceivedQueue;

	private BinaryWriter bw;

	private SCODataState scoState;

	private string remoteBDAddr = "";

	public int CurrentAvailable => currentAvailable;

	public bool EnableTx
	{
		set
		{
			enableTx = value;
		}
	}

	public int CurrentSCOConnectionHandler
	{
		get
		{
			return currentSCOConnectionHandler;
		}
		set
		{
			currentSCOConnectionHandler = value;
		}
	}

	public string RemoteBDAddress
	{
		set
		{
			remoteBDAddr = value;
		}
	}

	public SCODataQueue(int connectionHandle, SpecificPacketNotifier specificPacketNotifier, BTAPI btAPI, Logger logger, ManualResetEvent parentShutdownEvent = null)
	{
		currentSCOConnectionHandler = connectionHandle;
		this.btAPI = btAPI;
		this.parentShutdownEvent = parentShutdownEvent;
		this.logger = logger;
		this.specificPacketNotifier = specificPacketNotifier;
		btAPI.RegisterReceiveSCODataPacketObserver(this);
		if (!Directory.Exists("C:\\Qualcomm\\QCRPG\\Logs"))
		{
			if (!Directory.Exists("C:\\Temp"))
			{
				Directory.CreateDirectory("C:\\Temp");
				Thread.Sleep(2000);
			}
			tempFileLocation = "C:\\Temp\\SCOoverHCI_" + logger.DutName + DateTime.Now.ToString("ddMMyyHHmmssfff") + ".bin";
		}
		else
		{
			tempFileLocation = "C:\\Qualcomm\\QCRPG\\Logs\\SCOoverHCI_" + logger.DutName + DateTime.Now.ToString("ddMMyyHHmmssfff") + ".bin";
		}
		scoReceivedQueue = new Queue<SynchronousData>();
		saveReceivedScoToFileThread = new Thread(FileWriteThread);
		saveReceivedScoToFileThread.Name = "SCODataQueue-SaveReceivedSco";
		saveReceivedScoToFileThread.IsBackground = true;
		saveReceivedScoToFileThread.Start();
		scoState = SCODataState.IDLE;
	}

	public SCODataQueue(int connectionHandle, SpecificPacketNotifier specificPacketNotifier, BTAPI btAPI, Logger logger, string wavFileLocation, ManualResetEvent parentShutdownEvent = null)
		: this(connectionHandle, specificPacketNotifier, btAPI, logger, parentShutdownEvent)
	{
		this.wavFileLocation = wavFileLocation;
	}

	private void SendScoThread()
	{
		while (true)
		{
			if (!sendSco || !startTx)
			{
				continue;
			}
			sendSco = false;
			if (scoTxPacketList != null && scoTxPacketList.Count > 0)
			{
				btAPI.Send(scoTxPacketList[scoTxPacketIndex++]);
				if (scoTxPacketIndex > scoTxPacketList.Count - 1)
				{
					StopScoOverHciTx();
				}
			}
			else
			{
				logger.Enqueue(new LogMessage(LogMessageType.Error, "Queue is empty"));
			}
		}
	}

	public void StartScoOverHciTx(uint frequency, uint amplitude, uint sampleSize, uint sampleRate, uint durationSec)
	{
		scoTxPacketList = null;
		this.frequency = frequency;
		this.amplitude = amplitude;
		this.sampleSize = sampleSize;
		this.sampleRate = sampleRate;
		this.durationSec = durationSec;
		Thread thread = new Thread(GenerateTxData);
		thread.Name = "SCODataQueue-DataGenerator";
		thread.IsBackground = true;
		thread.Start();
		sendSco = false;
		scoTxPacketIndex = 0;
		sendScoThread = new Thread(SendScoThread);
		sendScoThread.Name = "SCODataQueue-SendSco";
		sendScoThread.IsBackground = true;
		sendScoThread.Start();
	}

	public void StopScoOverHciTx()
	{
		startTx = false;
		btAPI.SetDecodePacket(decodePacket: true, packetResponse: true);
	}

	private void GenerateTxData()
	{
		int num = 0;
		while (scoDataLength == 0)
		{
			Thread.Sleep(10);
		}
		uint num2 = durationSec * sampleRate;
		short[] array = new short[num2];
		num = 0;
		int num3 = 0;
		ushort num4 = (ushort)(amplitude * 100);
		if (num4 > 100)
		{
			num4 = 100;
		}
		num4 = (ushort)(327.0 * (double)(int)num4);
		while (num < array.Length - 1)
		{
			double num5 = Math.PI * 2.0 * (double)frequency / (double)sampleRate;
			short num6 = (short)((double)(int)num4 * Math.Sin(num5 * (double)num3));
			array[num++] = num6;
			num3++;
		}
		AssembleScoPacketData(array);
		startTx = true;
	}

	private void AssembleScoPacketData(short[] data)
	{
		if (data.Length == 0)
		{
			return;
		}
		byte[] array = new byte[2];
		scoTxPacketList = new List<SynchronousData>();
		int num = 0;
		while (num < data.Length)
		{
			SynchronousData synchronousData = new SynchronousData();
			for (int i = 0; i < scoDataLength / 2; i++)
			{
				array = BitConverter.GetBytes(data[num]);
				array = BitConverter.GetBytes(data[num]);
				synchronousData._UserFields.DataList.Add(array[0]);
				synchronousData._UserFields.DataList.Add(array[1]);
				num++;
			}
			synchronousData._UserFields.DataLength = (uint)synchronousData._UserFields.DataList.Count;
			synchronousData._UserFields.Handle = (uint)currentSCOConnectionHandler;
			scoTxPacketList.Add(synchronousData);
		}
	}

	public void CreateWavFileFromReceivedPackets(PacketBase packet)
	{
		btAPI.SetDecodePacket(decodePacket: true, packetResponse: true);
		currentSCOConnectionHandler = 0;
		scoStarted = false;
		saveScoToFileEvent.Reset();
		if (bw == null)
		{
			logger.Enqueue(new LogMessage(LogMessageType.Info, "No SCO data received."));
			return;
		}
		logger.Enqueue(new LogMessage(LogMessageType.Info, "SCO transfer completed. Saving to file"));
		scoState = SCODataState.IDLE;
		lock (bw)
		{
			scoReceivedFileStream.Close();
		}
		string tempFileName = Path.GetTempFileName();
		int num = (int)new FileInfo(tempFileLocation).Length;
		int num2 = 1;
		int num3 = 16;
		using (BinaryWriter binaryWriter = new BinaryWriter(File.OpenWrite(tempFileName)))
		{
			binaryWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
			int value = 36 + num;
			binaryWriter.Write(value);
			binaryWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
			binaryWriter.Write(Encoding.ASCII.GetBytes("fmt "));
			binaryWriter.Write(16);
			binaryWriter.Write((short)1);
			binaryWriter.Write((short)num2);
			binaryWriter.Write((int)sampleRate);
			binaryWriter.Write((int)(sampleRate * num2 * num3 / 8));
			binaryWriter.Write((short)(num2 * num3 / 8));
			binaryWriter.Write((short)num3);
			binaryWriter.Write(Encoding.ASCII.GetBytes("data"));
			binaryWriter.Write(num);
			using FileStream fileStream = File.OpenRead(tempFileLocation);
			fileStream.CopyTo(binaryWriter.BaseStream);
		}
		string text = "C:\\Qualcomm\\QCRPG\\SCOOverHCI\\SCOoverHCI_Rx_" + logger.DutName + "_Rem" + remoteBDAddr + "_" + DateTime.Now.ToString("MMddyyyy_HHmmss_fff") + ".wav";
		if (!string.IsNullOrEmpty(wavFileLocation))
		{
			text = Path.Combine(Path.GetDirectoryName(wavFileLocation), Path.GetFileNameWithoutExtension(wavFileLocation) + "_SCOoverHCI_Rx_" + logger.DutName + "_Rem" + remoteBDAddr + "_" + DateTime.Now.ToString("MMddyyyy_HHmmss_fff") + ".wav");
		}
		new FileInfo(text).Directory.Create();
		File.Move(tempFileName, text);
		logger.Enqueue(new LogMessage(LogMessageType.Info, "SCO transfer completed. File saved: " + text));
	}

	private void FileWriteThread()
	{
		ManualResetEvent[] array = new ManualResetEvent[1] { saveScoToFileEvent };
		SynchronousData synchronousData = new SynchronousData();
		while (true)
		{
			int num = WaitHandle.WaitAny(array);
			if (array[num] != saveScoToFileEvent || scoReceivedQueue.Count == 0)
			{
				continue;
			}
			lock (scoReceivedQueue)
			{
				synchronousData = scoReceivedQueue.Dequeue();
			}
			lock (bw)
			{
				try
				{
					for (int i = 0; i < synchronousData.UserFields.DataLength; i++)
					{
						bw.Write((byte)synchronousData.UserFields.DataList[i]);
					}
				}
				catch (ArgumentException)
				{
					logger.Enqueue(new LogMessage(LogMessageType.Error, "stream not writeable"));
				}
			}
		}
	}

	private bool IsDummyPacket(SynchronousData scoPacket)
	{
		for (int i = 0; i < scoPacket.UserFields.DataLength; i++)
		{
			if (scoPacket.UserFields.DataList[i] != 0 && scoPacket.UserFields.DataList[i] != 213)
			{
				return false;
			}
		}
		return true;
	}

	public void NotifyObserver(PacketBase packet)
	{
		if (!(packet is SynchronousData))
		{
			return;
		}
		if (scoDataLength == 0)
		{
			scoDataLength = ((SynchronousData)packet).UserFields.DataLength;
		}
		if (scoState == SCODataState.IDLE)
		{
			btAPI.SetDecodePacket(decodePacket: false, packetResponse: false, conciseLog: true);
			scoState = SCODataState.RECEIVING_DATA;
			scoReceivedQueue.Enqueue((SynchronousData)packet);
		}
		else if (scoState == SCODataState.RECEIVING_DATA)
		{
			if (!scoStarted)
			{
				logger.Enqueue(new LogMessage(LogMessageType.Info, "First non-dummy packet received"));
				scoReceivedFileStream = File.Open(tempFileLocation, FileMode.Create, FileAccess.Write, FileShare.Read);
				bw = new BinaryWriter(scoReceivedFileStream);
				saveScoToFileEvent.Set();
				scoStarted = true;
			}
			if (startTx)
			{
				sendSco = true;
			}
			scoReceivedQueue.Enqueue((SynchronousData)packet);
		}
	}

	public void EnableRx()
	{
		specificPacketNotifier.AddPacketNotification(typeof(DisconnectionComplete), CreateWavFileFromReceivedPackets);
		scoReceivedQueue.Clear();
	}
}
