// ============================================================================
//  RETTUNGSDIENST-ERWEITERUNG  -  Untersuchen, Befund, Funkmeldungen
// ============================================================================
//
//  Ablauf im Spiel:
//   1. Notarzt oder RTW-Besatzung untersuchen den Patienten ("Untersuchen").
//      Danach ist der Befund bekannt und wird per Funkmeldung angezeigt.
//   2. Der Befund entscheidet, wer behandeln darf:
//        leicht verletzt   -> RTW-Besatzung oder Notarzt
//        schwer verletzt   -> nur Notarzt
//        Lebensgefahr      -> Notarzt, und eine RTW-Besatzung muss
//                             zur Unterstützung beim Patienten sein
//   3. Ohne Untersuchung ist keine Behandlung möglich.
//
//  Benötigte Teile:
//   - Diese Datei                  (Scripts/Game/Command/)
//   - Ein Haken in heal.script     (siehe Kommentar "Rettungsdienst-Erweiterung")
//   - Der Befehl "EMS_Untersuchen" in den Prototypen von Notarzt und
//     Rettungssanitätern (bei den Sanitätern zusätzlich "Heal").
//     Einheiten OHNE "EMS_Untersuchen" behandeln weiter wie im Originalspiel.
//
//  Wie der Befund am Patienten gespeichert wird:
//   Der Patient bekommt einen unsichtbaren "Merker-Befehl" zugewiesen
//   (EMS_Befund_Leicht / _Schwer / _Kritisch). So wird UserData nicht
//   belegt, das viele andere Mods schon für eigene Zwecke verwenden.
//
//  Befehle in dieser Datei:
//   EMS_Untersuchen     Button für Notarzt und RTW-Besatzung
//   EMS_DiagnoseStellen intern: wird am Ende der Untersuchung ausgeführt
//   EMS_Behandeln       intern: neue Behandlungsregeln, wird von heal.script aufgerufen
//   EMS_BefundLoeschen  intern: entfernt den Befund wieder (verletzte Einsatzkräfte)
//   EMS_Befund_*        intern: Merker am Patienten, ohne eigene Funktion
// ============================================================================


// ----------------------------------------------------------------------------
//  EINSTELLUNGEN  -  hier kann alles angepasst werden
// ----------------------------------------------------------------------------

// Wie lange eine Untersuchung dauert (Sekunden)
const float EMS_UNTERSUCHUNG_DAUER = 6.0f;

// Schweregrad anhand der verbleibenden Lebensenergie des Patienten (0 - 1000):
//   ab EMS_GRENZE_LEICHT          -> leicht verletzt
//   ab EMS_GRENZE_SCHWER          -> schwer verletzt
//   darunter                      -> Lebensgefahr
const float EMS_GRENZE_LEICHT = 600.0f;
const float EMS_GRENZE_SCHWER = 250.0f;

// Lebensgefahr: Muss eine RTW-Besatzung beim Patienten sein, bevor der
// Notarzt behandeln kann? (true = ja, false = Notarzt reicht allein)
const bool  EMS_KRITISCH_BRAUCHT_RTW = true;

// In welchem Umkreis um den Patienten die RTW-Besatzung stehen muss
const float EMS_UNTERSTUETZUNG_RADIUS = 600.0f;

// Wie oft der wartende Notarzt prüft, ob die RTW-Besatzung da ist (Sekunden)
const float EMS_WARTEN_PRUEFINTERVALL = 2.0f;

// Absender der Funkmeldungen
const char EMS_NAME_NOTARZT[] = "Notarzt";
const char EMS_NAME_RTW[]     = "RTW-Besatzung";


// ----------------------------------------------------------------------------
//  INTERNE NAMEN  -  nur ändern, wenn auch heal.script angepasst wird
// ----------------------------------------------------------------------------

const char EMS_CMD_UNTERSUCHEN[]     = "EMS_Untersuchen";
const char EMS_CMD_DIAGNOSE[]        = "EMS_DiagnoseStellen";
const char EMS_CMD_BEHANDELN[]       = "EMS_Behandeln";
const char EMS_CMD_BEFUND_LOESCHEN[] = "EMS_BefundLoeschen";
const char EMS_MERKER_LEICHT[]       = "EMS_Befund_Leicht";
const char EMS_MERKER_SCHWER[]       = "EMS_Befund_Schwer";
const char EMS_MERKER_KRITISCH[]     = "EMS_Befund_Kritisch";

// Schweregrade
const int EMS_BEFUND_KEINER   = 0;   // noch nicht untersucht
const int EMS_BEFUND_LEICHT   = 1;
const int EMS_BEFUND_SCHWER   = 2;
const int EMS_BEFUND_KRITISCH = 3;

// childID für EMS_Behandeln: wartender Notarzt prüft erneut (ohne neue Meldung)
const int EMS_ERNEUT_PRUEFEN = 1;


// ----------------------------------------------------------------------------
//  HILFSFUNKTIONEN
// ----------------------------------------------------------------------------
// Die Engine findet Funktionen aus einem Befehl heraus nur, wenn sie in
// einem Namensraum stehen. Aufruf daher immer mit EMS::Name(...).
namespace EMS
{

// Liefert den gespeicherten Befund eines Patienten (EMS_BEFUND_...)
int GetBefund(Person *patient)
{
	if (patient->HasCommand(EMS_MERKER_KRITISCH)) return EMS_BEFUND_KRITISCH;
	if (patient->HasCommand(EMS_MERKER_SCHWER))   return EMS_BEFUND_SCHWER;
	if (patient->HasCommand(EMS_MERKER_LEICHT))   return EMS_BEFUND_LEICHT;
	return EMS_BEFUND_KEINER;
}

// Speichert einen Befund am Patienten (ersetzt einen eventuell alten)
void SetBefund(Person *patient, int befund)
{
	patient->RemoveCommand(EMS_MERKER_LEICHT);
	patient->RemoveCommand(EMS_MERKER_SCHWER);
	patient->RemoveCommand(EMS_MERKER_KRITISCH);

	if (befund == EMS_BEFUND_LEICHT)   patient->AssignCommand(EMS_MERKER_LEICHT);
	if (befund == EMS_BEFUND_SCHWER)   patient->AssignCommand(EMS_MERKER_SCHWER);
	if (befund == EMS_BEFUND_KRITISCH) patient->AssignCommand(EMS_MERKER_KRITISCH);
}

// Ermittelt den Schweregrad aus der verbleibenden Lebensenergie
int BefundErmitteln(Person *patient)
{
	float leben = patient->GetLife();
	if (leben >= EMS_GRENZE_LEICHT) return EMS_BEFUND_LEICHT;
	if (leben >= EMS_GRENZE_SCHWER) return EMS_BEFUND_SCHWER;
	return EMS_BEFUND_KRITISCH;
}

// Ist der Patient überhaupt ein Fall für den Rettungsdienst?
bool IstPatient(Person *p)
{
	if (!p->IsValid() || p->IsDead() || p->IsCarried() || p->IsDrowning())
		return false;
	if (p->GetRole() == ROLE_ANIMAL || p->IsRescueDog())
		return false;
	if (p->GetEnteredCarID() != -1)
		return false;
	return p->IsInjured() || p->IsWoundedSquad();
}

// Name der Einheit für Funkmeldungen
void Absender(Person *helfer, char *text, int laenge)
{
	if (helfer->IsParamedicTeam())
		snprintf(text, laenge, "%s", EMS_NAME_RTW);
	else
		snprintf(text, laenge, "%s", EMS_NAME_NOTARZT);
}

// Schweregrad als Text
void SchweregradText(int befund, char *text, int laenge)
{
	if (befund == EMS_BEFUND_LEICHT)        snprintf(text, laenge, "leicht verletzt");
	else if (befund == EMS_BEFUND_SCHWER)   snprintf(text, laenge, "schwer verletzt");
	else                                    snprintf(text, laenge, "in Lebensgefahr");
}

// Diagnose als Text - abhängig von Verletzungsursache und Schweregrad.
// Welche Variante ein Patient bekommt, hängt von seiner ID ab. So bleibt
// die Diagnose bei jedem Anzeigen gleich, ohne sie speichern zu müssen.
void DiagnoseText(Person *patient, int befund, char *text, int laenge)
{
	int variante = patient->GetID() % 3;
	int ursache = patient->GetInjuryReason();

	if (ursache == INJUREREASON_FIRE)
	{
		if (befund == EMS_BEFUND_LEICHT)       snprintf(text, laenge, "Verbrennungen 1. Grades, leichte Rauchgasinhalation");
		else if (befund == EMS_BEFUND_SCHWER)  snprintf(text, laenge, "Verbrennungen 2. Grades, Rauchgasvergiftung");
		else                                   snprintf(text, laenge, "großflächige Verbrennungen 3. Grades");
		return;
	}
	if (ursache == INJUREREASON_SHOT)
	{
		if (befund == EMS_BEFUND_LEICHT)       snprintf(text, laenge, "Streifschuss am Arm");
		else if (befund == EMS_BEFUND_SCHWER)  snprintf(text, laenge, "Schussverletzung am Bein, starke Blutung");
		else                                   snprintf(text, laenge, "Schussverletzung im Oberkörper, Schock");
		return;
	}
	if (ursache == INJUREREASON_DROWN)
	{
		if (befund == EMS_BEFUND_KRITISCH)     snprintf(text, laenge, "Beinahe-Ertrinken, Atemstillstand");
		else                                   snprintf(text, laenge, "Unterkühlung, Wasser in der Lunge");
		return;
	}
	if (ursache == INJUREREASON_CONTAM_CHEM || ursache == INJUREREASON_CONTAM_BIO || ursache == INJUREREASON_CONTAM_ATOM)
	{
		if (befund == EMS_BEFUND_LEICHT)       snprintf(text, laenge, "Kontamination, Reizung von Haut und Atemwegen");
		else                                   snprintf(text, laenge, "Kontamination mit Vergiftungserscheinungen");
		return;
	}

	// Alle übrigen Ursachen (Unfälle, Stürze, Schlägereien, ...)
	if (befund == EMS_BEFUND_LEICHT)
	{
		if (variante == 0)      snprintf(text, laenge, "Prellungen und Schürfwunden");
		else if (variante == 1) snprintf(text, laenge, "Verdacht auf Unterarmbruch");
		else                    snprintf(text, laenge, "Platzwunde am Kopf");
	}
	else if (befund == EMS_BEFUND_SCHWER)
	{
		if (variante == 0)      snprintf(text, laenge, "offener Unterschenkelbruch");
		else if (variante == 1) snprintf(text, laenge, "Schädel-Hirn-Trauma");
		else                    snprintf(text, laenge, "Verdacht auf innere Blutungen");
	}
	else
	{
		if (variante == 0)      snprintf(text, laenge, "Polytrauma, nicht ansprechbar");
		else if (variante == 1) snprintf(text, laenge, "Kreislaufstillstand");
		else                    snprintf(text, laenge, "schweres Schädel-Hirn-Trauma, bewusstlos");
	}
}

// Zeigt eine Funkmeldung an:  "<Absender>: <Text>"
void Funkmeldung(Person *helfer, char *meldung)
{
	char absender[64];
	char text[512];
	Absender(helfer, absender, 64);
	snprintf(text, 512, "%s: %s", absender, meldung);
	Game::ShowHelpTextWindow(text);
}

// Funkmeldung mit dem Befund und was als Nächstes zu tun ist
void MeldeBefund(Person *helfer, Person *patient, int befund)
{
	char diagnose[256];
	char schwere[64];
	char text[512];
	DiagnoseText(patient, befund, diagnose, 256);
	SchweregradText(befund, schwere, 64);

	bool istRTW = helfer->IsParamedicTeam();

	if (befund == EMS_BEFUND_LEICHT)
		snprintf(text, 512, "Befund: %s, Patient %s. Kein Notarzt erforderlich.", diagnose, schwere);
	else if (befund == EMS_BEFUND_SCHWER && istRTW)
		snprintf(text, 512, "Befund: %s, Patient %s. Notarzt nachfordern!", diagnose, schwere);
	else if (befund == EMS_BEFUND_SCHWER)
		snprintf(text, 512, "Befund: %s, Patient %s. Übernehme die Versorgung.", diagnose, schwere);
	else if (istRTW)
		snprintf(text, 512, "Befund: %s, Patient %s! Notarzt sofort nachfordern!", diagnose, schwere);
	else if (EMS_KRITISCH_BRAUCHT_RTW)
		snprintf(text, 512, "Befund: %s, Patient %s! Benötige Unterstützung durch eine RTW-Besatzung.", diagnose, schwere);
	else
		snprintf(text, 512, "Befund: %s, Patient %s!", diagnose, schwere);

	Funkmeldung(helfer, text);
}

// Steht eine freie RTW-Besatzung in der Nähe des Patienten?
bool IstRTWInDerNaehe(Person *patient)
{
	GameObjectList personen = patient->GetObjectsInRange(EMS_UNTERSTUETZUNG_RADIUS, ACTOR_PERSON);
	for (int i = 0; i < personen.GetNumObjects(); i++)
	{
		Person p(personen.GetObject(i));
		if (p.IsValid() && p.IsParamedicTeam() && !p.IsCarryingPerson())
			return true;
	}
	return false;
}

}   // Ende namespace EMS


// ============================================================================
//  BUTTON: Patient untersuchen  (Notarzt und RTW-Besatzung)
// ============================================================================
object EMS_Untersuchen : CommandScript
{
	EMS_Untersuchen()
	{
		SetIcon("checkvictim");        // Original-Icon "Opfer untersuchen"
		SetCursor("checkvictim");
		SetValidTargets(ACTOR_PERSON);
		SetRestrictions(RESTRICT_NOTARRESTED);
		SetPossibleCallers(ACTOR_PERSON);
		SetPossibleExists(CPE_INJURED_PERSON | CPE_WOUNDED_SQUAD);  // Button nur, wenn es Verletzte gibt
		SetPriority(310);              // etwas wichtiger als "Behandeln" (300)
		SetSelfClickActivation(true);
	}

	bool CheckPossible(GameObject *Caller)
	{
		Person helfer(Caller);
		if (helfer.IsParamedicTeam() && helfer.IsCarryingPerson())
			return false;
		return true;
	}

	bool CheckTarget(GameObject *Caller, Actor *Target, int childID)
	{
		if (!Caller->IsValid() || !Target->IsValid() || Target->GetID() == Caller->GetID())
			return false;
		if (Target->GetType() != ACTOR_PERSON)
			return false;

		Person helfer(Caller);
		if (helfer.IsParamedicTeam() && helfer.IsCarryingPerson())
			return false;

		Person patient(Target);
		return EMS::IstPatient(&patient) && !patient.IsHealing();
	}

	void PushActions(GameObject *Caller, Actor *Target, int childID)
	{
		Person helfer(Caller);
		Person patient(Target);

		// Schon untersucht? Dann nur den Befund erneut melden.
		int befund = EMS::GetBefund(&patient);
		if (befund != EMS_BEFUND_KEINER)
		{
			EMS::MeldeBefund(&helfer, &patient, befund);
			return;
		}

		// Zum Patienten gehen und untersuchen
		Caller->PushActionMove(ACTION_NEWLIST, Target, TARGET_TREATMENT);
		Caller->PushActionTurnTo(ACTION_APPEND, Target);

		// Das Sanitäter-Team mit Trage hat keine passende Animation
		if (!helfer.IsParamedicTeam())
			Caller->PushActionSwitchAnim(ACTION_APPEND, "treatinjured1");

		Caller->PushActionWait(ACTION_APPEND, EMS_UNTERSUCHUNG_DAUER);

		if (!helfer.IsParamedicTeam())
			Caller->PushActionSwitchAnim(ACTION_APPEND, "idle");

		// Ergebnis feststellen und melden
		Caller->PushActionExecuteCommand(ACTION_APPEND, EMS_CMD_DIAGNOSE, Target, 0, false);
	}
};


// ============================================================================
//  INTERN: Diagnose stellen  (Ende der Untersuchung)
// ============================================================================
object EMS_DiagnoseStellen : CommandScript
{
	EMS_DiagnoseStellen() {}

	bool CheckPossible(GameObject *Caller) { return true; }
	bool CheckTarget(GameObject *Caller, Actor *Target, int childID) { return true; }

	void PushActions(GameObject *Caller, Actor *Target, int childID)
	{
		Person helfer(Caller);
		Person patient(Target);
		if (!EMS::IstPatient(&patient))
			return;     // Patient ist inzwischen verstorben oder abtransportiert

		// Hat eine andere Einheit gleichzeitig untersucht, gilt deren Befund
		int befund = EMS::GetBefund(&patient);
		if (befund == EMS_BEFUND_KEINER)
		{
			befund = EMS::BefundErmitteln(&patient);
			EMS::SetBefund(&patient, befund);
		}
		EMS::MeldeBefund(&helfer, &patient, befund);
	}
};


// ============================================================================
//  INTERN: Behandeln nach den neuen Regeln  (aufgerufen von heal.script)
// ============================================================================
object EMS_Behandeln : CommandScript
{
	EMS_Behandeln() {}

	bool CheckPossible(GameObject *Caller) { return true; }
	bool CheckTarget(GameObject *Caller, Actor *Target, int childID) { return true; }

	void PushActions(GameObject *Caller, Actor *Target, int childID)
	{
		Person helfer(Caller);
		Person patient(Target);
		if (!EMS::IstPatient(&patient))
			return;

		bool erneutePruefung = (childID == EMS_ERNEUT_PRUEFEN);
		int befund = EMS::GetBefund(&patient);

		// Regel 1: Ohne Untersuchung keine Behandlung
		if (befund == EMS_BEFUND_KEINER)
		{
			EMS::Funkmeldung(&helfer, "Patient muss zuerst untersucht werden.");
			return;
		}

		// Regel 2: Die RTW-Besatzung darf nur leicht Verletzte selbst versorgen
		if (helfer.IsParamedicTeam() && befund != EMS_BEFUND_LEICHT)
		{
			EMS::Funkmeldung(&helfer, "Versorgung nur durch den Notarzt möglich. Notarzt nachfordern!");
			return;
		}

		// Regel 3: Bei Lebensgefahr braucht der Notarzt Unterstützung.
		// Er geht schon zum Patienten und wartet dort, bis eine RTW-Besatzung
		// in der Nähe ist. Dann beginnt die Behandlung von selbst.
		if (befund == EMS_BEFUND_KRITISCH && EMS_KRITISCH_BRAUCHT_RTW
			&& !helfer.IsParamedicTeam() && !EMS::IstRTWInDerNaehe(&patient))
		{
			if (!erneutePruefung)
			{
				EMS::Funkmeldung(&helfer, "Beginne mit Erstmaßnahmen. RTW-Besatzung zur Unterstützung benötigt!");
				Caller->PushActionMove(ACTION_NEWLIST, Target, TARGET_TREATMENT);
				Caller->PushActionTurnTo(ACTION_APPEND, Target);
				Caller->PushActionSwitchAnim(ACTION_APPEND, "treatinjured1");
			}
			// In kurzen Abständen erneut prüfen. Ein neuer Befehl des
			// Spielers an den Notarzt beendet das Warten automatisch.
			Caller->PushActionWait(ACTION_APPEND, EMS_WARTEN_PRUEFINTERVALL);
			Caller->PushActionExecuteCommand(ACTION_APPEND, EMS_CMD_BEHANDELN, Target, EMS_ERNEUT_PRUEFEN, false);
			return;
		}

		// Alles in Ordnung: behandeln wie im Originalspiel
		if (erneutePruefung)
			EMS::Funkmeldung(&helfer, "RTW-Besatzung ist eingetroffen. Beginne mit der Behandlung.");

		if (erneutePruefung)
			Caller->PushActionMove(ACTION_APPEND, Target, TARGET_TREATMENT);
		else
			Caller->PushActionMove(ACTION_NEWLIST, Target, TARGET_TREATMENT);
		Caller->PushActionTurnTo(ACTION_APPEND, Target);
		Caller->PushActionHeal(ACTION_APPEND, Target);

		// Verletzte Einsatzkräfte stehen nach der Behandlung wieder auf und
		// können erneut verletzt werden - dann soll neu untersucht werden.
		if (patient.IsWoundedSquad())
			Caller->PushActionExecuteCommand(ACTION_APPEND, EMS_CMD_BEFUND_LOESCHEN, Target, 0, false);
	}
};


// ============================================================================
//  INTERN: Befund entfernen
// ============================================================================
object EMS_BefundLoeschen : CommandScript
{
	EMS_BefundLoeschen() {}

	bool CheckPossible(GameObject *Caller) { return true; }
	bool CheckTarget(GameObject *Caller, Actor *Target, int childID) { return true; }

	void PushActions(GameObject *Caller, Actor *Target, int childID)
	{
		Person patient(Target);
		EMS::SetBefund(&patient, EMS_BEFUND_KEINER);
	}
};


// ============================================================================
//  INTERN: Merker-Befehle für den Befund (unsichtbar, ohne Funktion)
// ============================================================================
object EMS_Befund_Leicht : CommandScript
{
	EMS_Befund_Leicht() {}
	bool CheckPossible(GameObject *Caller) { return false; }
	bool CheckTarget(GameObject *Caller, Actor *Target, int childID) { return false; }
	void PushActions(GameObject *Caller, Actor *Target, int childID) {}
};

object EMS_Befund_Schwer : CommandScript
{
	EMS_Befund_Schwer() {}
	bool CheckPossible(GameObject *Caller) { return false; }
	bool CheckTarget(GameObject *Caller, Actor *Target, int childID) { return false; }
	void PushActions(GameObject *Caller, Actor *Target, int childID) {}
};

object EMS_Befund_Kritisch : CommandScript
{
	EMS_Befund_Kritisch() {}
	bool CheckPossible(GameObject *Caller) { return false; }
	bool CheckTarget(GameObject *Caller, Actor *Target, int childID) { return false; }
	void PushActions(GameObject *Caller, Actor *Target, int childID) {}
};
