using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using ZusiCLIProject.EntryPoints.ReihenschlussHelper;
using ZusiCLIProject.FileLibrary.Zusi3;
using KombiSchalterFunktion = ZusiCLIProject.FileLibrary.Zusi3.NativeMethods.KombiSchalterFunktion;

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

public class Demodll_AntriebLoadable
{
	// die Funktion sollte eine Messagebox starten, die als kurze Doku zur Funktion
	// der dll für den Fahrzeugbauer liefert
	public static void InfoEx(System.IntPtr callerOrNull, System.Reflection.Assembly assenbly, int id) 
	{
		MessageBox.Show("Demo-Antrieb Loadable", "Demo-Antrieb Loadable");
	}
	// die dll kann mehrere Varianten enthalten, es muss mindestens 1 zurückgegeben werden
	public static int AnzahlVariantenEx(System.IntPtr callerOrNull, System.Reflection.Assembly assenbly, int id) { return 2; }
	// Für jede Variante soll ein kurzer String als Beschreibung zurückgegeben werden
	// Die strings werden im Fahrzeugeditor in die Combobox gefüllt
	// index beginnt bei 0 für die 1. Variante
	public static string VariantenNameEx(System.IntPtr callerOrNull, System.Reflection.Assembly assenbly, int id, int index) 
	{
		if (index == 0)
			return "Test-Antrieb Fahrstufe";
		else if (index == 1)
			return "Test-Antrieb Zugkraft";
		else
			return "error";
	}
	// Diese Funktion wird beim Start der Fahrt einmal aufgerufen, um der dll
	// mitzuteilen, welche der Varianten berechnet werden soll
	// index beginnt bei 0 für die 1. Variante
#if NETCOREAPP
	[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
	public static NativeMethods.IFahrzeugAntrieb VarianteAktivierenEx(System.IntPtr callerOrNull, System.Reflection.Assembly assenbly, int id, int index) 
	{ 
		try
		{
			return new NativeMethods.FahrzeugAntriebCatcher(new FahrzeugAntriebAntrieb(index));
		}
		catch(System.Exception ex)
		{
			MessageBox.Show("Demo-Antrieb Loadable", "Demo-Antrieb konnte icht initialisiert werden:\r\n" + ex.ToString());
			return new NativeMethods.FahrzeugAntriebCatcher(null); //Damit Zusi nicht völlig abstürzt.
		}
	}
}
#if NETCOREAPP
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public abstract class FahrzeugAntriebBasisfunktion : NativeMethods.IFahrzeugAntrieb
{
	public static AntriebsmodellElektrischReihenschluss LoadAntrieb()
	{
		var fzgName = "DemoAntriebLoadable.fzg";
		var fileCandidate = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
		if (!System.IO.File.Exists(System.IO.Path.Combine(fileCandidate, fzgName))) //wenn sich diese DLL im /lib-Unterordner befindet, wollen wir die .fzg-Datei auch finden.
			fileCandidate = System.IO.Path.Combine(fileCandidate, "..");
		return AntriebsmodellElektrischReihenschluss.GetAntriebeByFileName(System.IO.Path.Combine(fileCandidate, fzgName)).Single();
	}
	public FahrzeugAntriebBasisfunktion(int index)
	{
		this.VarianteAktuell = index;
		this.Antriebsdaten = LoadAntrieb();
		this.Nachlaufsteuerung = new Nachlaufsteuerung(this.Antriebsdaten.Nachlaufsteuerung);
	}
	protected int VarianteAktuell {get; set;}
	protected float ZugkraftAktuell {get; set;}
	public Nachlaufsteuerung Nachlaufsteuerung {get; private set;}
	public AntriebsmodellElektrischReihenschluss Antriebsdaten {get; private set;}
	protected float Speed {get; set;}
	
	// Antriebstyp gemäß TCP-Doku 0002 000A 008E 0001 0025 0001
	public abstract byte Antriebstyp { get; }
	
	// ruft der Sim einmal pro Rechenschritt für das selbstgefahrene Fahrzeug und
	// für Fahrzeuge im Mehrfachtraktionsmodus im selbstgefahrenen Zug auf,
	// um die Führerstandsbedienung zu übertragen
	public virtual void Bedienung(NativeMethods.AntriebsRenderModus antriebsRenderModus, NativeMethods.ProtokollFstRef fstProtokoll, float sp)
	{
	}
	
	// ruft der Sim einmal pro Rechenschritt auf, dort sollen die Berechnungen erfolgen
	// wird nur für das selbstgefahrene Fahrzeug und für Fahrzeuge im
	// Mehrfachtraktionsmodus im selbstgefahrenen Zug aufgerufen
	public virtual void Berechnung(double deltaT, NativeMethods.AntriebsRenderModus antriebsRenderModus, NativeMethods.MehrfachtraktionsdatenRef mehrfachtraktionsdaten, bool HSGesperrt) 
	{ 
		Speed = mehrfachtraktionsdaten.Value.SpAkt;
		Nachlaufsteuerung.Berechnen(deltaT);
	}
	
	// das wird für Fahrzeuge einmal pro Rechenschritt aufgerufen, die im eigenen
	// Zug im Modus "eigener Tf" fahren und für alle Fahrzeuge im Autopilotmodus
	// es muss hier eine vereinfachte, schnelle Berechnung durchgeführt werden
	// komplexe Rechenmanöver können die Performance beeinflussen, da sehe viele Züge
	// mit diesem Modus im fahrplan unterwegs sein können
	public void VorspanntraktionsdatenSetzen(float sp, float sollLeistungNormiert)
	{
		ZugkraftAktuell = sollLeistungNormiert * Antriebsdaten.Kennfeld.KraftAblesen(System.Math.Abs(sp));
	}
	
	// wird vom Sim aufgerufen, wenn ein Nullstellungszwang durch das Antriebssystem
	// erfolgen muss
	public void Nullstellungszwang() {}
	
	// Setter wird vom Sim aufgerufen, wenn sich der Modus des HS ändern soll
	// modus:  0: HS ein, 1: HS aus, 2: Spannung frisch an, 3: HS frisch ein
	public byte Hauptschalterzustand { get; set; }
	
	// die bei der angegebenen Geschwindigkeit max. mögliche Zugkraft
	public float GetMaxAntriebskraft(float sp) { return Antriebsdaten.Kennfeld.KraftAblesen(System.Math.Abs(sp)); }

	// div. Fahrgrößen
	public float Antriebskraft { get {return ZugkraftAktuell;} }
	public float AntriebskraftSollNormiert { get {return 0;} }
	public float Oberstrom { get {return 0;} }
	public float Motorstrom { get {return 0;} }
	public float Motorspannung { get {return 0;} }
	public int Achsen { get {return 0;} }
	public float SollZugkraft { get {return 0;} }
	public float Motordrehzahl { get {return 0;} }
	public float Motormoment { get {return 0;} }
	public float Motorlast { get {return 0;} }
	
	public float Fahrstufe { get {return Nachlaufsteuerung.Stufe;} }
	public float ZylSolldruck { get {return -1;} }
	public bool FahrstufeIst0 { get {return true;} }
	public bool NullstellungszwangAktiv { get {return false;} }
	public bool HasHauptschalterausloesungWgOLEnde(NativeMethods.Fahrleitungstyp fahrlTyp, float spannungsfaktor) { return false;}
	public bool HasHauptschalterausloesungWgOberstrom { get {return false;} }
	//bool Hauptschalterstatus { get; } => Oben schon der Setter
	public bool HauptschalterDrin { get {return true;} }
	public bool DieselmotorLaeuft { get {return true;} }
}
#if NETCOREAPP
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class FahrzeugAntriebAntrieb : FahrzeugAntriebBasisfunktion
{
	public FahrzeugAntriebAntrieb(int index) : base(index) {}
	public override byte Antriebstyp { get {return 1;} }
	public override void Bedienung(NativeMethods.AntriebsRenderModus antriebsRenderModus, NativeMethods.ProtokollFstRef fstProtokoll, float sp)
	{
		base.Bedienung(antriebsRenderModus, fstProtokoll, sp);
		if ((fstProtokoll.Value.Rischa.Funktion == KombiSchalterFunktion.RischaR) || (fstProtokoll.Value.Rischa.Funktion == KombiSchalterFunktion.RischaV))
		{
			if (VarianteAktuell == 0)
			{
				if (fstProtokoll.Value.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeX)
					Nachlaufsteuerung.Zielstufe = (int)System.Math.Round(fstProtokoll.Value.SchaltstufenSteller.Parameter * Nachlaufsteuerung.Daten.AnzahlSchaltstufen);
			}
			else if (VarianteAktuell == 1)
			{
				if (fstProtokoll.Value.ZugkraftSteller.Funktion == KombiSchalterFunktion.ZugkraftX)
					Nachlaufsteuerung.Zielstufe = (int)System.Math.Round(fstProtokoll.Value.ZugkraftSteller.Parameter * Nachlaufsteuerung.Daten.AnzahlSchaltstufen);
			}
		}
		else
			Nachlaufsteuerung.Zielstufe = 0;

		if      (fstProtokoll.Value.Hauptschalter.Funktion == KombiSchalterFunktion.HauptschalterAus)
			Hauptschalterzustand = 1;
		else if (fstProtokoll.Value.Hauptschalter.Funktion == KombiSchalterFunktion.HauptschalterEin)
		{
			// nur HS einlegen, wenn das Schaltwerk abgelaufen ist und der Fahrschalter in 0 steht
			if (VarianteAktuell == 0)
			{
				if ((Nachlaufsteuerung.Stufe == 0) && ((fstProtokoll.Value.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeSchnellAus) || 
				((fstProtokoll.Value.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeX) && (fstProtokoll.Value.SchaltstufenSteller.Parameter == 0))))
					Hauptschalterzustand = 0;
			}
			else if (VarianteAktuell == 1)
			{
				if ((Nachlaufsteuerung.Stufe == 0) && ((fstProtokoll.Value.ZugkraftSteller.Funktion == KombiSchalterFunktion.ZugkraftSchnellAus) || 
				((fstProtokoll.Value.ZugkraftSteller.Funktion == KombiSchalterFunktion.ZugkraftX) && (fstProtokoll.Value.ZugkraftSteller.Parameter == 0))))
					Hauptschalterzustand = 0;
			}
		}

		// der LM auf fahrzeugintern19 soll beispielhaft leuchten wenn das System Zugkraft erzeugt
		fstProtokoll.Value.SetFtdIntern(19, (Antriebskraft != 0) ? 1 : 0);

		// den Sound auf fahrzeugintern20 ansteuern (irgendein sinnfreies Beispiel)
		fstProtokoll.Value.SetFtdIntern(20, (float)System.Math.Sqrt(sp));
	}
	public override void Berechnung(double deltaT, NativeMethods.AntriebsRenderModus antriebsRenderModus, NativeMethods.MehrfachtraktionsdatenRef mehrfachtraktionsdaten, bool HSGesperrt) 
	{
		base.Berechnung(deltaT, antriebsRenderModus, mehrfachtraktionsdaten, HSGesperrt);
		if (Hauptschalterzustand == 0)
			ZugkraftAktuell = Antriebsdaten.Kennfeld.GetStufenWert(Nachlaufsteuerung.Stufe) * Antriebsdaten.Kennfeld.KraftAblesen(System.Math.Abs(Speed));
		else
			ZugkraftAktuell = 0;

		// Hauptschalter soll beispielhaft über 50 km/h rausfliegen
		if (Speed>50/3.6)
			Hauptschalterzustand = 1;
	}
}
#if NETCOREAPP
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class FahrzeugAntriebBremse : FahrzeugAntriebBasisfunktion
{
	public FahrzeugAntriebBremse(int index) : base(index) {}
	public override byte Antriebstyp { get {return 0;} }
	public override void Bedienung(NativeMethods.AntriebsRenderModus antriebsRenderModus, NativeMethods.ProtokollFstRef fstProtokoll, float sp)
	{
		base.Bedienung(antriebsRenderModus, fstProtokoll, sp);
	}
	public override void Berechnung(double deltaT, NativeMethods.AntriebsRenderModus antriebsRenderModus, NativeMethods.MehrfachtraktionsdatenRef mehrfachtraktionsdaten, bool HSGesperrt) 
	{
		base.Berechnung(deltaT, antriebsRenderModus, mehrfachtraktionsdaten, HSGesperrt);
	}
}
public class Nachlaufsteuerung
{
	public Nachlaufsteuerung(AntriebsmodellElektrischReihenschluss.NachlaufsteuerungTyp daten)
	{
		Daten = daten;
	}
	public void Berechnen(double deltaT) 
	{
		if      (m_StufeAktuellFloat<Zielstufe)
		{
			m_StufeAktuellFloat=System.Math.Min(m_StufeAktuellFloat + deltaT*Daten.GeschwAuf, Zielstufe);
		}
		else if (m_StufeAktuellFloat>Zielstufe)
		{
			m_StufeAktuellFloat=System.Math.Max(m_StufeAktuellFloat - deltaT*Daten.GeschwAb, Zielstufe);
		}
	}
	public int Stufe {get {return (int)System.Math.Round(m_StufeAktuellFloat);} }
	public int Zielstufe {get; set;}
	public AntriebsmodellElektrischReihenschluss.NachlaufsteuerungTyp Daten { get; private set; }
	private double m_StufeAktuellFloat = 0;
}

#if NETCOREAPP
#else
}
#endif
