// /--------------------\
// | Plugin for openBVE |
// |       ATS-SN       |
// \--------------------/

#include "plugin.h"
#include "stdio.h"
#include "direct.h"
#include <windows.h>
#include <math.h>


// ---------------
// - train specs -
// ---------------

int trainCars = 0;
int trainPowerNotches = 0;
int trainCancelNotch = 0;
int trainEmergencyNotch = 0;
bool trainDoorsOpen = false;
bool trainInitialized = false;


// -------------------
// - driver's handle -
// -------------------

int trainPowerNotch = 0;
int trainBrakeNotch = 0;
int trainReverserPos = 0;


// --------
// - time -
// --------

int lastTime = 0;


// -------
// - ATS -
// -------

const int ATS_SN_DISABLED = -1;
const int ATS_SN_NORMAL = 0;
const int ATS_SN_CHIME = 1;
const int ATS_SN_ALARM = 2;
const int ATS_SN_EMERGENCY = 3;
const int ATS_SN_INITIALIZE = 4;

int atsSnState = ATS_SN_NORMAL;
int atsSnCountdown = 0;


// ---------
// - panel -
// ---------

const int PANEL_ATS_DENGEN = 0;
const int PANEL_ATS = 1;
const int PANEL_ATS_DOUSA = 2;
const int PANEL_LAMP_BUREEKI = 30;
const int PANEL_LAMP_RIKKOU = 31;


// ---------
// - sound -
// ---------

const int SOUND_CHIME = 0;
const int SOUND_ALARM = 1;
const int SOUND_DING = 2;
const int SOUND_EB = 3;

bool playDing = false;


// -----------
// - exports -
// -----------

extern "C" {

	// called when the plugin is loaded
	ATS_API void WINAPI Load () { }

	// called when the plugin is unloaded
	ATS_API void WINAPI Dispose () {
	}

	// called to query the API version
	ATS_API int WINAPI GetPluginVersion () {
		return ATS_VERSION;
	}

	// called to inform the plugin about the specs of the train
	ATS_API void WINAPI SetVehicleSpec (ATS_VEHICLESPEC vehicleSpec) {
		trainCars = vehicleSpec.Cars;
		trainCancelNotch = vehicleSpec.AtsNotch;
		trainEmergencyNotch = vehicleSpec.BrakeNotches + 1;
	}

	// called to inform the plugin about the starting mode of the security system
	ATS_API void WINAPI Initialize (int brake) {
		if (!trainInitialized) {
			if (brake == ATS_INIT_OFF_EMG) {
				atsSnState = ATS_SN_DISABLED;
			} else {
				atsSnState = ATS_SN_NORMAL;
			}
			trainInitialized = true;
		}
		atsSnCountdown = 0;
	}

	// called every frame
	ATS_API ATS_HANDLES WINAPI Elapse (ATS_VEHICLESTATE vehicleState, int* panel, int* sound) {
		// --- panel ---
		for (int i = 0; i < 256; i++) {
			panel[i] = 0;
		}
		// --- sound ---
		for (int i = 0; i < 256; i++) {
			if (sound[i] == ATS_SOUND_PLAYLOOPING) {
				sound[i] = ATS_SOUND_STOP;
			} else {
				sound[i] = ATS_SOUND_CONTINUE;
			}
		}
		// --- train ---
		ATS_HANDLES handles;
		handles.Reverser = trainReverserPos;
		handles.Power = trainPowerNotch;
		handles.Brake = trainBrakeNotch;
		handles.ConstantSpeed = ATS_CONSTANTSPEED_CONTINUE;
		// --- ATS-SN ---
		if (atsSnState != ATS_SN_DISABLED) {
			if (atsSnState == ATS_SN_ALARM) {
				atsSnCountdown -= vehicleState.Time - lastTime;
				if (atsSnCountdown <= 0) {
					atsSnState = ATS_SN_EMERGENCY;
				}
			} else if (atsSnState == ATS_SN_EMERGENCY) {
				handles.Brake = trainEmergencyNotch;
			} else if (atsSnState == ATS_SN_INITIALIZE) {
				atsSnCountdown -= vehicleState.Time - lastTime;
				if (atsSnCountdown <= 0) {
					atsSnState = ATS_SN_CHIME;
				} else {
					handles.Brake = trainEmergencyNotch;
				}
			}
			// --- sound ---
			if (atsSnState == ATS_SN_CHIME) {
				sound[SOUND_CHIME] = ATS_SOUND_PLAYLOOPING;
			} else if (atsSnState == ATS_SN_ALARM || atsSnState == ATS_SN_EMERGENCY || atsSnState == ATS_SN_INITIALIZE) {
				sound[SOUND_ALARM] = ATS_SOUND_PLAYLOOPING;
			}
		} else {
			if (trainPowerNotch >= 2) {
				sound[SOUND_ALARM] = ATS_SOUND_PLAYLOOPING;
			}
		}
		// --- controls ---
		if (atsSnState != ATS_SN_DISABLED && trainDoorsOpen) {
			handles.Power = 0;
		}
		if (handles.Brake != 0) {
			handles.Power = 0;
		}
		// --- panel ---
		panel[PANEL_ATS_DENGEN] = (atsSnState != ATS_SN_DISABLED) ? 1 : 0;
		panel[PANEL_ATS] = (atsSnState == ATS_SN_NORMAL || atsSnState == ATS_SN_CHIME) ? 1 : 0;
		panel[PANEL_ATS_DOUSA] = (atsSnState == ATS_SN_ALARM || (atsSnState == ATS_SN_EMERGENCY && vehicleState.Time % 1200 < 600) || atsSnState == ATS_SN_INITIALIZE) ? 1 : 0;
		panel[PANEL_LAMP_BUREEKI] = handles.Brake >= trainCancelNotch && handles.Brake < trainEmergencyNotch || handles.Reverser != 0 && trainCancelNotch == 2 && handles.Brake == 1 ? 1 : 0;
		panel[PANEL_LAMP_RIKKOU] = handles.Reverser != 0 && (handles.Power != 0 && handles.Brake == 0 || trainCancelNotch == 2 && handles.Brake == 1) ? 1 : 0;
		// --- finalize ---
		lastTime = vehicleState.Time;
		return handles;
	}

	// called when the power notch is changed
	ATS_API void WINAPI SetPower (int notch) {
		trainPowerNotch = notch;
	}

	// called when the brake notch is changed
	ATS_API void WINAPI SetBrake (int notch) {
		trainBrakeNotch = notch;
	}

	// called when the reverser is changed
	ATS_API void WINAPI SetReverser (int pos) {
		trainReverserPos = pos;
	}

	// called when a plugin key is pressed
	ATS_API void WINAPI KeyDown (int atsKeyCode) {
		switch (atsKeyCode) {
			case ATS_KEY_S:
				// acknowledge alarm
				if (atsSnState == ATS_SN_ALARM && trainPowerNotch == 0 && trainBrakeNotch >= trainCancelNotch) {
					atsSnState = ATS_SN_CHIME;
				}
				break;
			case ATS_KEY_A1:
				// cancel chime
				if (atsSnState == ATS_SN_CHIME) {
					atsSnState = ATS_SN_NORMAL;
				}
				break;
			case ATS_KEY_B1:
				// reset safety system
				if (atsSnState == ATS_SN_EMERGENCY && trainReverserPos == 0 && trainPowerNotch == 0 && trainBrakeNotch == trainEmergencyNotch) {
					atsSnState = ATS_SN_CHIME;
				}
				break;
			case ATS_KEY_D:
				// enable/disable ATS-SN
				if (atsSnState == ATS_SN_DISABLED) {
					atsSnState = ATS_SN_INITIALIZE;
					atsSnCountdown = 1000;
				} else {
					if (atsSnState != ATS_SN_EMERGENCY && atsSnState != ATS_SN_INITIALIZE) {
						atsSnState = ATS_SN_DISABLED;
					}
				}
				break;
		}
	}

	// called when a plugin key is released
	ATS_API void WINAPI KeyUp (int atsKeyCode) {
	}

	// called when a horn is played (or stopped)
	ATS_API void WINAPI HornBlow (int hornType) {
	}

	// called when the doors start opening
	ATS_API void WINAPI DoorOpen () {
		trainDoorsOpen = true;
	}

	// called when the doors have closed
	ATS_API void WINAPI DoorClose () {
		trainDoorsOpen = false;
	}

	// called when the state of the upcoming signal changes
	ATS_API void WINAPI SetSignal (int signal) {
	}

	// called when a beacon is passed
	ATS_API void WINAPI SetBeaconData (ATS_BEACONDATA beaconData) {
		if (beaconData.Type == 0) {
			// --- S-type ---
			if (beaconData.Signal == 0 || beaconData.Signal >= 100) {
				if (atsSnState == ATS_SN_NORMAL || atsSnState == ATS_SN_CHIME) {
					atsSnState = ATS_SN_ALARM;
					atsSnCountdown = 5000;
				}
			}
		} else if (beaconData.Type == 1) {
			// --- SN-type ---
			if (beaconData.Signal == 0 || beaconData.Signal >= 100) {
				if (atsSnState == ATS_SN_NORMAL || atsSnState == ATS_SN_CHIME || atsSnState == ATS_SN_ALARM) {
					atsSnState = ATS_SN_EMERGENCY;
				}
			}
		} else if (beaconData.Type == 2) {
			// --- accidental departure ---
			if (beaconData.Signal == 0 || beaconData.Signal >= 100) {
				if (beaconData.Optional >= trainCars || beaconData.Optional == 0) {
					if (atsSnState == ATS_SN_NORMAL || atsSnState == ATS_SN_CHIME || atsSnState == ATS_SN_ALARM) {
						atsSnState = ATS_SN_EMERGENCY;
					}
				}
			}
		}
	}

}
