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 Antrieb111
{
	// 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("Modifizierter Reihenschlussantrieb mit einem speziell für die 111 designten Antriebs-Steuergerät." + Antriebssystem111.BedienungSpezielleWerteBeschreibung, "Antrieb Baureihe 111");
	}
	// 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 1; }
	// 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 "BR 111 Standard";
		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 Antriebssystem111(index));
		}
		catch(System.Exception ex)
		{
			MessageBox.Show("Antrieb konnte icht initialisiert werden:\r\n" + ex.ToString(), "Antrieb Baureihe 111");
			return new NativeMethods.FahrzeugAntriebCatcher(null); //Damit Zusi nicht völlig abstürzt.
		}
	}
}
#if NETCOREAPP
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class Antriebssystem111 : NativeMethods.IFahrzeugAntrieb
{
	public static AntriebsmodellElektrischReihenschluss LoadAntrieb()
	{
		var fzgName = "Antrieb111.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 Antriebssystem111(int index)
	{
		this.VarianteAktuell = index;
		this.Antriebsdaten = LoadAntrieb();
		this.Schuetzsteuerung = new Schuetzsteuerung(this.Antriebsdaten.Nachlaufsteuerung);
		this.HauptschalterSteuerung = new HauptschalterSteuerung(this);
		this.RichtungsschalterSteuerung = new RichtungsschalterSteuerung();
		this.Lueftersteuerung = new Lueftersteuerung(this);
		this.Fahrsteuerung = new Fahrsteuerung(this);
	}
	protected int VarianteAktuell {get; set;}
	private float m_ZugkraftAktuell;
	private bool m_MotorWerteDirty = true;
	protected float Spannungsfaktor {get; private set;}
	private AntriebsmodellElektrischReihenschluss.MotorenWerte m_MotorWerte;
	protected float ZugkraftAktuell { get { return m_ZugkraftAktuell; } set { m_ZugkraftAktuell = value; m_MotorWerteDirty = true; } }
	/*protected*/ public AntriebsmodellElektrischReihenschluss.MotorenWerte MotorenWerte
	{
		get
		{
			if (m_MotorWerteDirty)
				m_MotorWerte = Antriebsdaten.GetWerteReihenschluss(ZugkraftAktuell, GetMaxAntriebskraft(Speed), 
				      Speed, Spannungsfaktor * 15000);
			m_MotorWerteDirty = false;
			return m_MotorWerte;
		}
	}
	public Schuetzsteuerung Schuetzsteuerung {get; private set;}
	public HauptschalterSteuerung HauptschalterSteuerung {get; private set;}
	public RichtungsschalterSteuerung RichtungsschalterSteuerung {get; private set;}
	public Lueftersteuerung Lueftersteuerung {get; private set;}
	public Fahrsteuerung Fahrsteuerung {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 virtual byte Antriebstyp { get {return 1;} }
	
	public const int IdLueferFahrmotorStufe = 1;
	public const int IdHilfsfahrschalterFrei = 2;
	public static string BedienungSpezielleWerteBeschreibung
	{
		get 
		{
			return "Spezielle IDs:\r\n" + 
							"Lüfer Fahrmotor Stufe: " + IdLueferFahrmotorStufe + "\r\n" +
							"Hilfsfahrschalter Frei: " + IdHilfsfahrschalterFrei + "\r\n" +
							"Schalten des Hilfsfahrschalters über Gangwahlschalter";
		}
	}
	// 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)
	{
		var gw = new Steuergeraet111.GrenzwertueberwachungEin();
		gw.ImotIst = MotorenWerte.IMotor;
		gw.IoberIst = MotorenWerte.IOber;
		gw.Fist = ZugkraftAktuell;
		gw.UmotIst = MotorenWerte.UMotor;
		gw.FsollZ = fstProtokoll.Value.ZugkraftSteller.Parameter * 50000 * 4;
		gw.FsollAFB = float.MaxValue;
		gw.Faktiv = fstProtokoll.Value.ZugkraftSteller.Funktion == KombiSchalterFunktion.ZugkraftX;
		gw.FahrschalterAuf = fstProtokoll.Value.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeAufX || (Fahrsteuerung.HilfsfahrschalterHilfsschuetz && (fstProtokoll.Value.Ganghebel.Funktion == KombiSchalterFunktion.GangWahlAuf));
		gw.FahrschalterAb = fstProtokoll.Value.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeAbX || (Fahrsteuerung.HilfsfahrschalterHilfsschuetz && (fstProtokoll.Value.Ganghebel.Funktion == KombiSchalterFunktion.GangWahlAb));
		gw.Stoerschalter = false;
		gw.Schleuderschutzkipptaster = false;
		gw.KsAnfahrUeberwachung = false;
		gw.MotorstromdiffSchleudern = fstProtokoll.Value.SchleuderschutzElektronischWirksam;
		
		var ausgabe = Steuergeraet111.GrenzwertueberwachungAus.Generate(gw);
		Schuetzsteuerung.AbSchuetz = ausgabe.AbSchuetz; // || (fstProtokoll.Value.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeSchnellAus);
		Schuetzsteuerung.AufSchuetz = ausgabe.AufSchuetz;
		//Schleuderschutzbremse
		//LMAnfahrueberwachung
		//LMFunktionsbereit
		fstProtokoll.Value.SetFtdIntern(IdLueferFahrmotorStufe, Lueftersteuerung.LueferFahrmotorStufe);

		//Schuetzsteuerung.Bedienung;
		HauptschalterSteuerung.Bedienung(fstProtokoll);
		RichtungsschalterSteuerung.Bedienung(fstProtokoll);
		Lueftersteuerung.Bedienung(fstProtokoll);
		Fahrsteuerung.Bedienung(fstProtokoll);
	}
	
	// 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;
		Spannungsfaktor = 
		(mehrfachtraktionsdaten.Value.StreckenStromsystem == Antriebsdaten.Volt) ?
		mehrfachtraktionsdaten.Value.FahrlSpannungsfaktor : 0;


		Schuetzsteuerung.Berechnung(deltaT);
		HauptschalterSteuerung.Berechnung(mehrfachtraktionsdaten);
		RichtungsschalterSteuerung.Berechnung(mehrfachtraktionsdaten);
		Lueftersteuerung.Berechnung(mehrfachtraktionsdaten);
		Fahrsteuerung.Berechnung(mehrfachtraktionsdaten);
		
		if (Hauptschalterzustand == 0 || HSGesperrt)
			ZugkraftAktuell = Antriebsdaten.Kennfeld.GetStufenWert(Schuetzsteuerung.Stufe) * 
				Antriebsdaten.Kennfeld.KraftAblesen(System.Math.Abs(Speed)) * 
				Spannungsfaktor * Spannungsfaktor * //ToDo: Spannungsfaktor²?
				Fahrsteuerung.LeistungsfaktorTrennschuetzRichtungswender; 
		else
			ZugkraftAktuell = 0;
	}
	
	// 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)
	{
		Speed = sp;
		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 {return HauptschalterSteuerung.Hauptschalterzustand;} set{HauptschalterSteuerung.Hauptschalterzustand = value;} }
	
	// 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 MotorenWerte.IOber;} }
	public float Motorstrom { get {return MotorenWerte.IMotor;} }
	public float Motorspannung { get {return MotorenWerte.UMotor;} }
	public int Achsen { get {return Antriebsdaten.Antriebsachsen;} }
	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 Schuetzsteuerung.Stufe;} }
	// C-Druck für Druckluftergänzungsbremse o.ä. soll Druckluft-Bremse überstimmen
	// -1 keine Vorgabe
	public float ZylSolldruck { get {return -1;} }
	public bool FahrstufeIst0 { get {return Schuetzsteuerung.Stufe == 0;} }
	// muss true zurückgeben, wenn aus Sicht des Antriebsmodells ein
	// Nullstellungszwang aktiv ist
	public bool NullstellungszwangAktiv { get {return false;} }
	// muss true zurückgeben, wenn aus Sicht des Antriebsmodells eine
	// Hauptschalterauslösung nötig ist wegen unpassender Fahrleitungsspannung
	// Spannungsfaktor ist 1, wenn die Fahrleitungsspannung genau dem Sollwert entspricht
	public bool HasHauptschalterausloesungWgOLEnde(NativeMethods.Fahrleitungstyp fahrlTyp, float spannungsfaktor) 
	{ return false; }
	// muss true zurückgeben, wenn aus Sicht des Antriebsmodells eine
	// Hauptschalterauslösung wg. Oberstromüberschreitung nötig ist.
	// für diesen Antrieb muss sich die dll selbst um die Auswirkungen kümmern
	// Die Funktion teilt das hier nur z.B. den anderen Einheiten einer Mehrfachtraktion mit
	public bool HasHauptschalterausloesungWgOberstrom { get {return HauptschalterSteuerung.HasHauptschalterausloesungWgOberstrom;} }
	//bool Hauptschalterstatus { get; } => Oben schon der Setter

	// Zustand angeben für Luftpresser-Steuerung
	public bool HauptschalterDrin { get {return HauptschalterSteuerung.Hauptschalterzustand == 0;} }
	public bool DieselmotorLaeuft { get {return false;} }
}
public static class ProtokollFstExtMethods
{
	public static bool IsSchaltstufenStellerZero(this NativeMethods.ProtokollFst fstProtokoll)
	{
		return ((fstProtokoll.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeSchnellAus) || 
				((fstProtokoll.SchaltstufenSteller.Funktion == KombiSchalterFunktion.FahrstufeX) && (fstProtokoll.SchaltstufenSteller.Parameter == 0)));
	}
	public static bool IsZugkraftStellerZero(this NativeMethods.ProtokollFst fstProtokoll)
	{
		return ((fstProtokoll.ZugkraftSteller.Funktion == KombiSchalterFunktion.ZugkraftSchnellAus) || 
				((fstProtokoll.ZugkraftSteller.Funktion == KombiSchalterFunktion.ZugkraftX) && (fstProtokoll.ZugkraftSteller.Parameter == 0)));
	}
}

#if NETCOREAPP
#else
}
#endif
