using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using QC.QTMDotNetKernelInterface;

namespace QC.QTMToolKit.Network;

[QTMTestClass]
public class SocketCommunicator
{
	private Dictionary<string, TcpClient> tcpClientDictionary = new Dictionary<string, TcpClient>();

	private TcpClient currentTcpClient;

	private NetworkStream currentStream;

	private string currentSocketKey = "";

	private AutoResetEvent readCompleted = new AutoResetEvent(initialState: false);

	private BackgroundWorker backgroundWorker;

	private List<byte> readByteList = new List<byte>();

	private Exception lastException;

	public SocketCommunicator()
	{
		backgroundWorker = new BackgroundWorker();
		backgroundWorker.WorkerSupportsCancellation = true;
		backgroundWorker.DoWork += backgroundWorker_DoWork;
		backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
	}

	[QTMTest(CanAddToTestPlan = true)]
	public void CreateSocket(int localPortNumber, string remoteHostAddress, int remotePortNumber)
	{
		string socketKey = GetSocketKey(localPortNumber, remoteHostAddress, remotePortNumber);
		if (tcpClientDictionary.ContainsKey(socketKey))
		{
			throw new Exception("Socket connection already exists. Call SetCurrentSocket to use the specified socket.");
		}
		string hostName = Dns.GetHostName();
		IPAddress[] hostAddresses = Dns.GetHostAddresses(hostName);
		if (hostAddresses.Length <= 0)
		{
			throw new Exception("No local IP address found");
		}
		IPEndPoint localEP = new IPEndPoint(hostAddresses[0], localPortNumber);
		currentTcpClient = new TcpClient(localEP);
		IPAddress address = IPAddress.Parse(remoteHostAddress);
		currentTcpClient.Connect(address, remotePortNumber);
		currentStream = currentTcpClient.GetStream();
		currentSocketKey = socketKey;
		tcpClientDictionary[socketKey] = currentTcpClient;
	}

	[QTMTest(CanAddToTestPlan = true)]
	public void SetCurrentSocket(int localPortNumber, string remoteHostAddress, int remotePortNumber)
	{
		string socketKey = GetSocketKey(localPortNumber, remoteHostAddress, remotePortNumber);
		if (tcpClientDictionary.ContainsKey(socketKey))
		{
			currentTcpClient = tcpClientDictionary[socketKey];
			currentStream = currentTcpClient.GetStream();
			currentSocketKey = socketKey;
			return;
		}
		throw new Exception("Specified Socket connection does not exist. Call CreateSocket first.");
	}

	[QTMTest(CanAddToTestPlan = true)]
	public string ReadFromCurrentSocket(int numberOfBytesToBeRead, int timeout)
	{
		if (currentTcpClient != null)
		{
			if (!currentTcpClient.Connected)
			{
				throw new Exception("No Socket Connection not established");
			}
			currentStream.ReadTimeout = timeout;
			lastException = null;
			readCompleted.Reset();
			readByteList.Clear();
			backgroundWorker.RunWorkerAsync(numberOfBytesToBeRead);
			if (!readCompleted.WaitOne(timeout))
			{
				backgroundWorker.CancelAsync();
				while (backgroundWorker.IsBusy)
				{
				}
				throw new Exception("Read time out occured");
			}
			if (lastException != null)
			{
				throw lastException;
			}
			string text = BitConverter.ToString(readByteList.ToArray());
			return text.Replace('-', ',');
		}
		throw new Exception("Socket not initialized properly");
	}

	[QTMTest(CanAddToTestPlan = true)]
	public void WriteBytesToCurrentSocket(string csvString, int timeout)
	{
		if (currentTcpClient != null)
		{
			if (!currentTcpClient.Connected)
			{
				throw new Exception("No Socket Connection not established");
			}
			string[] array = csvString.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			byte[] array2 = new byte[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = byte.Parse(array[i], NumberStyles.HexNumber);
			}
			currentStream.WriteTimeout = timeout;
			currentStream.Write(array2, 0, array2.Length);
			return;
		}
		throw new Exception("Socket not initialized properly");
	}

	[QTMTest(CanAddToTestPlan = true)]
	public void CloseCurrentSocket()
	{
		if (currentStream != null)
		{
			currentStream.Close();
		}
		if (currentTcpClient != null)
		{
			currentTcpClient.Close();
		}
		currentStream = null;
		currentTcpClient = null;
		if (tcpClientDictionary.ContainsKey(currentSocketKey))
		{
			tcpClientDictionary.Remove(currentSocketKey);
			return;
		}
		throw new Exception("Cannot close socket. Socket does not exist.");
	}

	private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
	{
		int num = (int)e.Argument;
		int num2 = 1024;
		byte[] array = new byte[num2];
		if (num > 0)
		{
			while (!backgroundWorker.CancellationPending && readByteList.Count < num)
			{
				currentStream.Read(array, 0, num2);
				readByteList.AddRange(array);
			}
			return;
		}
		while (!backgroundWorker.CancellationPending && !currentStream.DataAvailable)
		{
		}
		while (!backgroundWorker.CancellationPending && currentStream.DataAvailable)
		{
			currentStream.Read(array, 0, num2);
			readByteList.AddRange(array);
		}
	}

	private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
	{
		lastException = e.Error;
		readCompleted.Set();
	}

	private string GetSocketKey(int localPortNumber, string remoteHostAddress, int remotePortNumber)
	{
		return $"{localPortNumber}:{remoteHostAddress}:{remotePortNumber.ToString()}";
	}
}
