using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using ZusiCLIProject.FileLibrary.Zusi3;

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

[System.Xml.Serialization.XmlTypeAttribute("Zusi")]
public class AntriebRumpfZusi
{
	[System.Xml.Serialization.XmlElementAttribute("Fahrzeug")]
	public AntriebRumpfFahrzeug Fahrzeug {set; get;}
}
public class AntriebRumpfFahrzeug
{
	[System.Xml.Serialization.XmlElementAttribute("AntriebsmodellElektrischReihenschluss")]
	public AntriebsmodellElektrischReihenschluss[] AntriebsmodellElektrischReihenschluss {set; get; }
	[System.Xml.Serialization.XmlElementAttribute("AntriebsmodellElektrischDrehstrom")]
	public AntriebsmodellElektrischDrehstrom[] AntriebsmodellElektrischDrehstrom { set; get; } 
}
public class AntriebsmodellElektrischDrehstrom : AntriebsmodellElektrischReihenschluss //Platzhalter-Typ für Drehstrom, da momentan mit Reihenschluss identisch
{ }
public class AntriebsmodellElektrischReihenschluss
{
	public class KennfeldTyp
	{
		public class PktTyp
		{
			[System.Xml.Serialization.XmlAttributeAttribute()]
			public float PktX {set; get;}
			[System.Xml.Serialization.XmlAttributeAttribute()]
			public float PktY {set; get;}
			public override string ToString() {return string.Format("Pkt({0}, {1})", PktX, PktY);}
		}
		[System.Xml.Serialization.XmlElementAttribute("Pkt")]
		public PktTyp[] Pkt {set; get;}
		public class StufeTyp
		{
			[System.Xml.Serialization.XmlAttributeAttribute("StufenWert")]
			public float StufenWert {set; get;}
			public override string ToString() {return string.Format("Stufe({0})", StufenWert);}
		}
		[System.Xml.Serialization.XmlElementAttribute("Stufe")]
		public StufeTyp[] Stufe {set; get;}
		//[System.Xml.Serialization.XmlArrayAttribute("Stufe"),
		//System.Xml.Serialization.XmlArrayItem("StufenWert")]
		//public float[] Stufe {set; get;}
		public float GetStufenWert(int stufe)
		{
			if (stufe == 0)
				return 0;
			return Stufe[stufe - 1].StufenWert;
		}
		private string ArrToStr<T>(T[] d)
		{
			if (d == null)
				return "null";
			else if (d.Length == 0)
				return "0";
			else if (d.Length == 1)
				return d[0].ToString();
			else if (d.Length == 2)
				return d[0].ToString() + ", " + d[1].ToString();
			else
				return d[0].ToString() + " ..("+d.Length+").. " + d[d.Length - 1].ToString();
		}
		public override string ToString() 
		{
			return string.Format("Kennfeld({0}, {1})", ArrToStr(Pkt), ArrToStr(Stufe));
		}
		public float KraftAblesen(float geschw)
		{
			var pt1 = new PktTyp();
			foreach(var pt2 in Pkt)
			{
				if ((geschw >= pt1.PktX) && (geschw < pt2.PktX))
					return pt1.PktY + (pt2.PktY - pt1.PktY) * (geschw - pt1.PktX) / (pt2.PktX - pt1.PktX);
				pt1 = pt2;
			}
			return 0;
		}
	}
	[System.Xml.Serialization.XmlElementAttribute()]
	public KennfeldTyp Kennfeld {set; get;}
	public class DrehstrommotorTyp
	{
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorMinFrequenz {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorMaxFrequenz {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorAnfahrspannung {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float spUmschalt {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorMaxSpannung {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorkHysterese {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorkWirbelstrom {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float Motorkonstante {set; get;}
	}
	public class ReihenschlussmotorTyp
	{
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorX {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorRaRf {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorMaxSpannung {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorkHysterese {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float MotorkWirbelstrom {set; get;}
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float Motorkonstante {set; get;}
	}
	public class NachlaufsteuerungTyp
	{
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public int AnzahlSchaltstufen { set; get; }
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float GeschwAuf { set; get; }
		[System.Xml.Serialization.XmlAttributeAttribute()]
		public float GeschwAb { set; get; }
	}
	[System.Xml.Serialization.XmlElementAttribute()]
	public NachlaufsteuerungTyp Nachlaufsteuerung { set; get;}
	[System.Xml.Serialization.XmlElementAttribute()]
	public ReihenschlussmotorTyp Reihenschlussmotor {set; get;}
	[System.Xml.Serialization.XmlElementAttribute()]
	public DrehstrommotorTyp Drehstrommotor {set; get;}
	[System.Xml.Serialization.XmlIgnoreAttribute()]
	public NativeMethods.Fahrleitungstyp Volt {set; get;}
	[System.ComponentModel.EditorBrowsableAttribute(
	System.ComponentModel.EditorBrowsableState.Never), 
	System.Xml.Serialization.XmlAttributeAttribute("Volt")]
	public int VoltInternal { set { Volt = (NativeMethods.Fahrleitungstyp)value; } get { return (int)Volt; } }
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float etaTrafo {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float MaxOberstrom {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public bool GrenzwertUeberwacht {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public bool OhneHS {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float HauptschalterVerzoegerungEin {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float HauptschalterVerzoegerungZugkraft {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float Leistung {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public int Antriebsachsen {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float Reibmasse {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float GueteKraftverteilung {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float GrenzdruckHL {set; get;}
	[System.Xml.Serialization.XmlAttributeAttribute()]
	public float GrenzdruckHBL {set; get;}
	
	public struct MotorenWerte
	{
		public float UMotor {get; set;}
		public float Freq {get; set;} //macht nur für Drehstrom Sinn, wird aber auch für Reiheschluss angegeben.
		public float IAnker {get; set;}
		public double Phi {get; set;}
		public float CosPhi {get; set;}
		public float PvWirbel {get; set;}
		public float PvHysterese {get; set;}
		public float IMotor {get; set;}
		public float IOber {get; set;}
		public float etaGesamt {get; set;}
	}
	public MotorenWerte GetWerteReihenschluss(float f, float fmax, float v, float Ufahrdraht)
	{
		var value = new MotorenWerte();
		value.UMotor = (float)(Reihenschlussmotor.MotorMaxSpannung * System.Math.Sqrt(f/fmax));
		if (this.Volt == NativeMethods.Fahrleitungstyp.F15kV16Hz)
			value.Freq = 16.0f + (1.0f / 3.0f); //Verwende die alte Nennfrequenz, weil hauptsächlich alte Loks.
		else
			value.Freq = this.Volt.GetFrequenz();
		value.IAnker = (float)System.Math.Sqrt(/*2.0 * System.Math.PI * */ f / Reihenschlussmotor.Motorkonstante);
		value.Phi = System.Math.Atan((2.0 * System.Math.PI * Reihenschlussmotor.MotorX) / 
		(2 * System.Math.PI * Reihenschlussmotor.MotorRaRf + 
		Reihenschlussmotor.Motorkonstante * v));
		value.CosPhi = (float)System.Math.Cos(value.Phi);
		value.PvWirbel = Reihenschlussmotor.MotorkWirbelstrom * v * v * value.IAnker;
		value.PvHysterese = Reihenschlussmotor.MotorkHysterese * v * value.IAnker * value.IAnker;
		value.IMotor = value.IAnker + (value.PvWirbel + value.PvHysterese) / (value.UMotor * value.CosPhi);
		//float uFahrleitung = this.Volt.GetSpannung();
		value.IOber = (value.UMotor * value.IAnker * value.CosPhi + value.PvWirbel + value.PvHysterese) / 
			(/*uFahrleitung*/ Ufahrdraht * this.etaTrafo * value.CosPhi);
		value.etaGesamt = f * v / (Ufahrdraht * value.IOber);
		return value;
	}
	public MotorenWerte GetWerteDrehstrom(float f, float fmax, float v, float Ufahrdraht)
	{
		var value = new MotorenWerte();
		//Die "Standardkurve" so weit wie möglich Reverse Engeneert:
		float uAnfahr = (float)(Drehstrommotor.MotorAnfahrspannung * System.Math.Pow(50, - v / Drehstrommotor.spUmschalt));
		float uLinear = Drehstrommotor.MotorMaxSpannung * v / Drehstrommotor.spUmschalt;
		float uAnwendbar = System.Math.Max(Drehstrommotor.MotorMaxSpannung, uAnfahr + uLinear);
		value.UMotor = (float)(uAnwendbar * System.Math.Sqrt(f/fmax));
		value.Freq = Drehstrommotor.MotorMinFrequenz + (
		(Drehstrommotor.MotorMaxFrequenz - Drehstrommotor.MotorMinFrequenz) * v / Drehstrommotor.spUmschalt);
		value.IAnker = f * value.Freq / (value.UMotor * Drehstrommotor.Motorkonstante);
		//value.Phi = System.Math.Atan((2.0 * System.Math.PI * X) / 
		//(2 * System.Math.PI * Drehstrommotor.MotorRaRf + 
		//Drehstrommotor.Motorkonstante * v));
		value.Phi = 0; //ToDo: Eigentlich müsste es eine Sollvorgabe hierfür geben...
		value.CosPhi = (float)System.Math.Cos(value.Phi);
		value.PvWirbel = Drehstrommotor.MotorkWirbelstrom * v * v * value.IAnker;
		value.PvHysterese = Drehstrommotor.MotorkHysterese * v * value.IAnker * value.IAnker;
		value.IMotor = value.IAnker + (value.PvWirbel + value.PvHysterese) / (value.UMotor * value.CosPhi);
		value.IOber = (value.UMotor * value.IAnker * value.CosPhi + value.PvWirbel + value.PvHysterese) / 
			(value.UMotor * this.etaTrafo * value.CosPhi);
		value.etaGesamt = f * v / (Ufahrdraht * value.IOber);
		return value;
	}

	private static System.Xml.Serialization.XmlSerializer m_serializer;
	public static AntriebsmodellElektrischReihenschluss[] GetAntriebeByFileName(string fileName)
	{
		if (m_serializer == null)
			m_serializer = new System.Xml.Serialization.XmlSerializer(typeof(ZusiCLIProject.EntryPoints.ReihenschlussHelper.AntriebRumpfZusi));
		var file = (ZusiCLIProject.EntryPoints.ReihenschlussHelper.AntriebRumpfZusi) 
			m_serializer.Deserialize(System.IO.File.OpenRead(fileName));
		return file.Fahrzeug.AntriebsmodellElektrischReihenschluss;
	}
}

public static class FahrleitungstypExtMethods
{
	public static float GetSpannung(this NativeMethods.Fahrleitungstyp fahrleitungstyp)
	{
		if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.Ohne)
			return 0;
		//else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.Unbestimmt)
		//	return 0;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F15kV16Hz)
			return 15000;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F25kV50Hz)
			return 25000;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F1500VDC)
			return 1500;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F1200VDCHamburg)
			return 1200;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F3kVDC)
			return 3000;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F750VDCBerlin)
			return 750;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.Bordsystem)
			return 1000; //Hier erst mal die Heizspannung als Referenz verwendet
		else
			throw new NotImplementedException();
	}
	public static float GetFrequenz(this NativeMethods.Fahrleitungstyp fahrleitungstyp)
	{
		if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.Ohne)
			return 0;
		//else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.Unbestimmt)
		//	return 0;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F15kV16Hz)
			return 16.7f;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F25kV50Hz)
			return 50;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F1500VDC)
			return 0;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F1200VDCHamburg)
			return 0;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F3kVDC)
			return 0;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.F750VDCBerlin)
			return 0;
		else if (fahrleitungstyp == NativeMethods.Fahrleitungstyp.Bordsystem)
			return 16.7f; //Hier erst mal die Heizspannung als Referenz verwendet
		else
			throw new NotImplementedException();
	}
}








#if NETCOREAPP
#else
}
#endif
