using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace QC.QMSCT;

public class NetworkUnitDetector : IUnitDetector, IDisposable
{
	public struct ThreadData
	{
		public Thread thread;

		public int panelNumber;

		public string userData;

		public NetworkInterface packetDevice;

		public IPAddress ipAddress;

		public IPEndPoint endpoint;

		public UdpClient udpClient;
	}

	private Dictionary<int, string> expectedPhysicalAddress = new Dictionary<int, string>();

	private string expectedHardwareId = "";

	private Dictionary<int, int[]> expectedPorts = new Dictionary<int, int[]>();

	private Dictionary<string, NetworkInterface> livePacketDeviceDictionary = new Dictionary<string, NetworkInterface>();

	private List<ThreadData> networkThreads = new List<ThreadData>();

	private DetectionInstance detectionInstance = DetectionInstance.DETECT_INST_NET_MODE;

	private UDEDetectionMode udeDetectionMode;

	private DetectionEventMode eventMode = DetectionEventMode.DETECT_MODE_TIMER;

	public List<string> PortKeyWord => new List<string>();

	public bool PortKeyWordCaseSensitive
	{
		set
		{
		}
	}

	public DetectionInstance SetDetectionInstance
	{
		set
		{
			detectionInstance = value;
		}
	}

	public UDEDetectionMode SetUDEDetectionMode
	{
		set
		{
			udeDetectionMode = value;
		}
	}

	public DetectionEventMode SetEventMode
	{
		set
		{
			eventMode = value;
		}
	}

	public Dictionary<int, int[]> ExpectedListOfPorts
	{
		get
		{
			return expectedPorts;
		}
		set
		{
			expectedPorts = value;
		}
	}

	public Dictionary<int, string> ExpectedListOfPhysicalAddress
	{
		get
		{
			return expectedPhysicalAddress;
		}
		set
		{
			expectedPhysicalAddress = value;
		}
	}

	public string ExpectedHardwareId
	{
		get
		{
			return expectedHardwareId;
		}
		set
		{
			expectedHardwareId = value;
		}
	}

	public event ActivePositionsChangedGlobal OnChange;

	public event ActivePositionsChangedGlobalGV OnChangeGV;

	public void Stop()
	{
		foreach (ThreadData networkThread in networkThreads)
		{
			if (networkThread.thread.IsAlive)
			{
				networkThread.thread.Abort();
			}
			networkThread.udpClient.Close();
		}
		livePacketDeviceDictionary.Clear();
	}

	public void Start()
	{
		networkThreads = new List<ThreadData>();
		NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
		foreach (NetworkInterface networkInterface in allNetworkInterfaces)
		{
			livePacketDeviceDictionary.Add(networkInterface.Description + "|" + networkInterface.Name, networkInterface);
		}
		char[] separator = new char[1] { '|' };
		for (int j = 0; j < expectedPhysicalAddress.Keys.Count; j++)
		{
			if (!expectedPhysicalAddress.TryGetValue(expectedPhysicalAddress.Keys.ElementAt(j), out var value))
			{
				continue;
			}
			string[] array = value.Split(separator);
			if (array.Length < 2 || !livePacketDeviceDictionary.TryGetValue(array[0] + "|" + array[1], out var value2))
			{
				continue;
			}
			try
			{
				int panelNumber = expectedPhysicalAddress.Keys.ElementAt(j);
				string userData = ((array.Length == 3) ? array[2] : "");
				IPAddress iPAddress = (from ip in value2.GetIPProperties().UnicastAddresses
					where ip.Address.AddressFamily == AddressFamily.InterNetwork
					select ip.Address).SingleOrDefault();
				IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, 2389);
				UdpClient udpClient = new UdpClient(iPEndPoint);
				ThreadData tData = default(ThreadData);
				Thread thread = new Thread((ThreadStart)delegate
				{
					unitDetectorThread(tData);
				});
				tData.thread = thread;
				tData.packetDevice = value2;
				tData.ipAddress = iPAddress;
				tData.endpoint = iPEndPoint;
				tData.udpClient = udpClient;
				tData.panelNumber = panelNumber;
				tData.userData = userData;
				networkThreads.Add(tData);
				thread.Start();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception: " + ex.Data);
			}
		}
	}

	private void unitDetectorThread(ThreadData tData)
	{
		UdpClient udpClient = tData.udpClient;
		NetworkInterface packetDevice = tData.packetDevice;
		string text = tData.userData;
		int panelNumber = tData.panelNumber;
		while (true)
		{
			using (udpClient)
			{
				bool flag = true;
				IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
				do
				{
					try
					{
						Console.WriteLine("udpClient.Receive ... (Sync)");
						byte[] bytes = udpClient.Receive(ref remoteEP);
						string @string = Encoding.ASCII.GetString(bytes);
						Console.WriteLine("returnData = " + @string.ToString());
						flag = false;
					}
					catch (Exception ex)
					{
						Console.WriteLine("Issue with recieving udp packets: " + ex.Data);
					}
				}
				while (flag);
				UdpClient udpClient2 = new UdpClient();
				IPInterfaceProperties iPProperties = packetDevice.GetIPProperties();
				UnicastIPAddressInformationCollection unicastAddresses = iPProperties.UnicastAddresses;
				string text2 = "";
				foreach (UnicastIPAddressInformation item in unicastAddresses)
				{
					if (item.Address.AddressFamily == AddressFamily.InterNetwork)
					{
						text2 += item.Address;
					}
				}
				IPAddress iPAddress = IPAddress.Parse(text2);
				IPAddress address = remoteEP.Address;
				IPEndPoint iPEndPoint = new IPEndPoint(address, 2388);
				IPEndPoint localEP = new IPEndPoint(iPAddress, 0);
				udpClient2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true);
				udpClient2.Client.Bind(localEP);
				string s = iPAddress.ToString() + ":" + 2388;
				byte[] bytes2 = Encoding.ASCII.GetBytes(s);
				IPEndPoint localEP2 = new IPEndPoint(iPAddress, 2387);
				TcpListener tcpListener = new TcpListener(localEP2);
				tcpListener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true);
				tcpListener.Server.ReceiveTimeout = 1000;
				tcpListener.Start();
				udpClient2.Send(bytes2, bytes2.Length, iPEndPoint);
				Thread.Sleep(1000);
				bool flag2 = false;
				Socket socket = null;
				try
				{
					if (!tcpListener.Pending())
					{
						continue;
					}
					socket = tcpListener.AcceptSocket();
					socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, optionValue: false);
					socket.SendTimeout = 500;
					socket.ReceiveTimeout = 1000;
					Encoding.UTF8.GetBytes("Alive");
					int num;
					int portno;
					do
					{
						byte[] buffer = new byte[256];
						num = socket.Receive(buffer);
						string value = Regex.Match(text, "\\d+").Value;
						portno = (string.IsNullOrEmpty(value) ? 1 : int.Parse(value));
						text = (string.IsNullOrEmpty(text) ? iPEndPoint.Address.ToString() : text);
						if (!flag2)
						{
							this.OnChangeGV(panelNumber, connected: true, portno, text);
							flag2 = true;
						}
					}
					while (num != 0);
					this.OnChangeGV(panelNumber, connected: false, portno, text);
					socket.Disconnect(reuseSocket: true);
					tcpListener.Stop();
				}
				catch (InvalidOperationException)
				{
					tcpListener.Stop();
				}
				catch (SocketException ex3)
				{
					if (ex3.NativeErrorCode.Equals(10035))
					{
						Console.WriteLine("Still Connected, but the Send would block");
					}
					this.OnChange(panelNumber, connected: false, 1);
					socket?.Disconnect(reuseSocket: true);
					tcpListener.Stop();
				}
				catch (Exception)
				{
					Console.WriteLine("Issue with connection");
				}
			}
		}
	}

	public void Dispose()
	{
		Stop();
	}
}
