using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ZusiCLIProject.FileLibrary.Zusi3;
using KombiSchalterFunktion = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.KombiSchalterFunktion;
using AntriebsRenderModus = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.AntriebsRenderModus;
using ProtokollFstRef = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.ProtokollFstRef;
using MehrfachtraktionsdatenRef = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.MehrfachtraktionsdatenRef;
using Fahrleitungstyp = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.Fahrleitungstyp;
using ProtokollFstFieldAddresses = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.ProtokollFstFieldAddresses;
using MehrfachtraktionsdatenAddresses = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.MehrfachtraktionsdatenAddresses;
using System.Xml.Linq;

#if NETCOREAPP
namespace ZusiCLIProject.EntryPoints;
#else
namespace ZusiCLIProject.EntryPoints
{
#endif

#if NETCOREAPP
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class FahrzeugAntriebServer : NativeMethods.IFahrzeugAntrieb
{
	public System.IO.Stream Stream {get; set;}
	private byte[] Read(int length)
	{
		byte[] buffer = new byte[length];
		int bytesRead = 0;
		while (bytesRead < length)
		{
			bytesRead += Stream.Read(buffer, bytesRead, length - bytesRead);
		}
		return buffer;
	}
	private void ReadStaticData()
	{
		var data = Read(StaticDataArrayLng);
		int offset = 0;
		StaticDataFromArray(ref offset, data);
	}

	public byte Antriebstyp { get; private set; }
	
	public void Bedienung(AntriebsRenderModus antriebsRenderModus, ProtokollFstRef fstProtokoll, float sp)
	{
		Stream.Write(System.BitConverter.GetBytes((int)1), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((int)antriebsRenderModus), 0, 4);
		var fstProtokollData = new byte[(int)ProtokollFstFieldAddresses.Z_MaxLength];
		System.Runtime.InteropServices.Marshal.Copy(fstProtokoll.Address, fstProtokollData, 0, fstProtokollData.Length);
		Stream.Write(fstProtokollData, 0, fstProtokollData.Length);
		Stream.Write(System.BitConverter.GetBytes((float)sp), 0, 4);
		fstProtokollData = Read((int)ProtokollFstFieldAddresses.Z_MaxLength);
		System.Runtime.InteropServices.Marshal.Copy(fstProtokollData, 0, fstProtokoll.Address, fstProtokollData.Length);
		ReadStaticData();
	}
	public void Berechnung(double deltaT, AntriebsRenderModus antriebsRenderModus, MehrfachtraktionsdatenRef mehrfachtraktionsdaten, bool HSGesperrt)
	{
		Stream.Write(System.BitConverter.GetBytes((int)2), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((double)deltaT), 0, 8);
		Stream.Write(System.BitConverter.GetBytes((int)antriebsRenderModus), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((int)(HSGesperrt ? 1 : 0)), 0, 4);
		var mehrfachtraktionsdatenData = new byte[(int)MehrfachtraktionsdatenAddresses.Z_MaxLength];
		System.Runtime.InteropServices.Marshal.Copy(mehrfachtraktionsdaten.Address, mehrfachtraktionsdatenData, 0, mehrfachtraktionsdatenData.Length);
		Stream.Write(mehrfachtraktionsdatenData, 0, mehrfachtraktionsdatenData.Length);
		//mehrfachtraktionsdatenData = Read((int)MehrfachtraktionsdatenAddresses.Z_MaxLength);
		//System.Runtime.InteropServices.Marshal.Copy(mehrfachtraktionsdatenData, 0, mehrfachtraktionsdaten.Address, mehrfachtraktionsdatenData.Length);
		//ReadStaticData();
	}
	public void VorspanntraktionsdatenSetzen(float sp, float sollLeistungNormiert)
	{
		Stream.Write(System.BitConverter.GetBytes((int)3), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((float)sp), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((float)sollLeistungNormiert), 0, 4);
		//ReadStaticData();
	}
	public void Nullstellungszwang()
	{
		Stream.Write(System.BitConverter.GetBytes((int)4), 0, 4);
		ReadStaticData();
	}


	private byte HauptschalterzustandRead { get; set; }
	public byte Hauptschalterzustand 
	{ 
		get { return HauptschalterzustandRead; } 
		set
		{
			Stream.Write(System.BitConverter.GetBytes((int)5), 0, 4);
			Stream.Write(System.BitConverter.GetBytes((int)value), 0, 4);
			ReadStaticData();
		} 
	}

	
	
	public float GetMaxAntriebskraft(float sp)
	{
		Stream.Write(System.BitConverter.GetBytes((int)6), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((float)sp), 0, 4);
		var data = Read(4);
		return System.BitConverter.ToSingle(data, 0);
	}
	
	public float Antriebskraft { get; private set; }
	public float AntriebskraftSollNormiert { get; private set; }
	public float Oberstrom { get; private set; }
	public float Motorstrom { get; private set; }
	public float Motorspannung { get; private set; }
	public int Achsen { get; private set; }
	public float SollZugkraft  { get; private set; }
	public float Motordrehzahl  { get; private set; }
	public float Motormoment { get; private set; }
	public float Motorlast { get; private set; }
	public float Fahrstufe { get; private set; }
	public float ZylSolldruck { get; private set; }
	public bool FahrstufeIst0 { get; private set; }
	public bool NullstellungszwangAktiv { get; private set; }
	
	public bool HasHauptschalterausloesungWgOLEnde(Fahrleitungstyp fahrlTyp, float spannungsfaktor)
	{
		Stream.Write(System.BitConverter.GetBytes((int)7), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((int)fahrlTyp), 0, 4);
		Stream.Write(System.BitConverter.GetBytes((float)spannungsfaktor), 0, 4);
		var data = Read(4);
		//ReadStaticData();
		return System.BitConverter.ToInt32(data, 0) != 0;
	}
	
	public bool HasHauptschalterausloesungWgOberstrom { get; private set; }
	public bool HauptschalterDrin { get; private set; }
	public bool DieselmotorLaeuft { get; private set; }


	private const int StaticDataArrayLng = 18 * 4;
	private void StaticDataFromArray(ref int offset, byte[] data)
	{
		HauptschalterzustandRead = (byte)System.BitConverter.ToInt32(data, offset); offset += 4;
		Antriebskraft = System.BitConverter.ToSingle(data, offset); offset += 4;
		AntriebskraftSollNormiert = System.BitConverter.ToSingle(data, offset); offset += 4;
		Oberstrom = System.BitConverter.ToSingle(data, offset); offset += 4;
		Motorstrom = System.BitConverter.ToSingle(data, offset); offset += 4;
		Motorspannung = System.BitConverter.ToSingle(data, offset); offset += 4;
		Achsen = System.BitConverter.ToInt32(data, offset); offset += 4;
		SollZugkraft = System.BitConverter.ToSingle(data, offset); offset += 4;
		Motordrehzahl = System.BitConverter.ToSingle(data, offset); offset += 4;
		Motormoment = System.BitConverter.ToSingle(data, offset); offset += 4;
		Motorlast = System.BitConverter.ToSingle(data, offset); offset += 4;
		Fahrstufe = System.BitConverter.ToSingle(data, offset); offset += 4;
		ZylSolldruck = System.BitConverter.ToSingle(data, offset); offset += 4;
		FahrstufeIst0 = System.BitConverter.ToInt32(data, offset) != 0; offset += 4;
		NullstellungszwangAktiv = System.BitConverter.ToInt32(data, offset) != 0; offset += 4;
		HasHauptschalterausloesungWgOberstrom = System.BitConverter.ToInt32(data, offset) != 0; offset += 4;
		HauptschalterDrin = System.BitConverter.ToInt32(data, offset) != 0; offset += 4;
		DieselmotorLaeuft = System.BitConverter.ToInt32(data, offset) != 0; offset += 4;
	}
	public void ResetStaticData() //Damit Fahrzeuge nicht "durchgehen", wenn die Verbindung trippt.
	{
		HauptschalterzustandRead = 0;
		Antriebskraft = 0;
		AntriebskraftSollNormiert = 0;
		Oberstrom = 0;
		Motorstrom = 0;
		Motorspannung = 0;
		//Achsen = 0;
		SollZugkraft = 0;
		Motordrehzahl = 0;
		Motormoment = 0;
		Motorlast = 0;
		Fahrstufe = 0;
		ZylSolldruck = -1;
		FahrstufeIst0 = true;
		NullstellungszwangAktiv = false;
		HasHauptschalterausloesungWgOberstrom = false;
		HauptschalterDrin = false;
		DieselmotorLaeuft = false;
	}
}
#if NETCOREAPP
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class FahrzeugAntriebClient
{
	public FahrzeugAntriebClient()
	{
		bedienungOrBerechnungPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Math.Max(
			(int)ProtokollFstFieldAddresses.Z_MaxLength, (int)MehrfachtraktionsdatenAddresses.Z_MaxLength));
	}
	~FahrzeugAntriebClient()
	{
		System.Runtime.InteropServices.Marshal.FreeCoTaskMem(bedienungOrBerechnungPtr);
		bedienungOrBerechnungPtr = IntPtr.Zero;
	}
	public NativeMethods.IFahrzeugAntrieb Parent {get; set;}
	public System.IO.Stream Stream {get; set; }
	private byte[] Read(int length) 
	{
		byte[] buffer = new byte[length];
		int bytesRead = 0;
		while (bytesRead < length)
		{
			bytesRead += Stream.Read(buffer, bytesRead, length - bytesRead);
		}
		return buffer;
	}

	private void StaticDataToStream()
	{
		Stream.Write(System.BitConverter.GetBytes((int) Parent.Hauptschalterzustand), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Antriebskraft), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.AntriebskraftSollNormiert), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Oberstrom), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Motorstrom), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Motorspannung), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Achsen), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.SollZugkraft), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Motordrehzahl), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Motormoment), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Motorlast), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.Fahrstufe), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.ZylSolldruck), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.FahrstufeIst0 ? 1 : 0), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.NullstellungszwangAktiv ? 1 : 0), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.HasHauptschalterausloesungWgOberstrom ? 1 : 0), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.HauptschalterDrin ? 1 : 0), 0, 4);
		Stream.Write(System.BitConverter.GetBytes(Parent.DieselmotorLaeuft ? 1 : 0), 0, 4);
	}
	private IntPtr bedienungOrBerechnungPtr;
	public void ReadingLoop() { while(true) ReadBlock(); }
	private void ReadBlock()
	{
		var data = Read(4);
		int typ = System.BitConverter.ToInt32(data, 0);
		if (typ == 1)
		{
			data = Read((int)ProtokollFstFieldAddresses.Z_MaxLength + 4 + 4);
			System.Runtime.InteropServices.Marshal.Copy(data, 4, bedienungOrBerechnungPtr, (int)ProtokollFstFieldAddresses.Z_MaxLength);
			ProtokollFstRef protokollFstRef = new ProtokollFstRef(bedienungOrBerechnungPtr);
			Parent.Bedienung((AntriebsRenderModus)System.BitConverter.ToInt32(data, 0), protokollFstRef, System.BitConverter.ToSingle(data, 
				(int)ProtokollFstFieldAddresses.Z_MaxLength + 4));
			protokollFstRef.DisposePtr();
			System.Runtime.InteropServices.Marshal.Copy(bedienungOrBerechnungPtr, data, 4, (int)ProtokollFstFieldAddresses.Z_MaxLength);
			Stream.Write(data, 4, (int)ProtokollFstFieldAddresses.Z_MaxLength);
			StaticDataToStream();
		}
		else if (typ == 2)
		{
			data = Read((int)MehrfachtraktionsdatenAddresses.Z_MaxLength + 8 + 4 + 4);
			System.Runtime.InteropServices.Marshal.Copy(data, 8 + 4 + 4, bedienungOrBerechnungPtr, (int)MehrfachtraktionsdatenAddresses.Z_MaxLength);
			MehrfachtraktionsdatenRef mehrfachtraktionsdatenRef = new MehrfachtraktionsdatenRef(bedienungOrBerechnungPtr);
			Parent.Berechnung(System.BitConverter.ToDouble(data, 0), (AntriebsRenderModus)System.BitConverter.ToInt32(data, 8), mehrfachtraktionsdatenRef, System.BitConverter.ToInt32(data, 8 + 4) != 0);
			mehrfachtraktionsdatenRef.DisposePtr();
			//System.Runtime.InteropServices.Marshal.Copy(bedienungOrBerechnungPtr, data, 8 + 4, (int)MehrfachtraktionsdatenAddresses.Z_MaxLength);
			//Stream.Write(data, 8 + 4, (int)MehrfachtraktionsdatenAddresses.Z_MaxLength);
			//StaticDataToStream();
		}
		else if (typ == 3)
		{
			data = Read(4 + 4);
			Parent.VorspanntraktionsdatenSetzen(System.BitConverter.ToSingle(data, 0), System.BitConverter.ToSingle(data, 4));
			//StaticDataToStream();
		}
		else if (typ == 4)
		{
			Parent.Nullstellungszwang();
			StaticDataToStream();
		}
		else if (typ == 5)
		{
			data = Read(4);
			Parent.Hauptschalterzustand = (byte)System.BitConverter.ToInt32(data, 0);
			StaticDataToStream();
		}
		else if (typ == 6)
		{
			data = Read(4);
			float val = Parent.GetMaxAntriebskraft(System.BitConverter.ToSingle(data, 0));
			Stream.Write(System.BitConverter.GetBytes((float)val), 0, 4);
		}
		else if (typ == 7)
		{
			data = Read(4 + 4);
			bool val = Parent.HasHauptschalterausloesungWgOLEnde((Fahrleitungstyp)System.BitConverter.ToInt32(data, 0), System.BitConverter.ToSingle(data, 4));
			Stream.Write(System.BitConverter.GetBytes((int)(val ? 1 : 0)), 0, 4);
			//StaticDataToStream();
		}
		else
			throw new ArgumentOutOfRangeException();

	}
	
}

#if NETCOREAPP
#else
}
#endif
