import m from 'mithril';

export interface Status {
	batteryVoltage: number; // in Volts
	motorCurrent: number; // in Amperes
	speed: number; // in meters per second
	tripDistance: number; // in meters
	tripTotalTime: number; // in seconds
	tripMovingTime: number; // in seconds
	tripAscendingElevation: number; // in meters
	tripMotorEnergy: number; // in Watt-hour
	temperature: number; // in Celcius degrees
	altitude: number; // in meters above sea level
};

interface ApiStatus {
	v: number; // voltage (mV)
	c: number; // current (mA)
	s: number; // speed (mm/s)
	td: number; // trip distance (m)
	ttt: number; // trip total time (s)
	tmt: number; // trip moving time (s)
	tae: number; // trip ascending elevation (m)
	tme: number; // trip motor energy (Wh)
	temp: number; // temperature (tenth of °C)
	alt: number; // altitude (mm)
}

export class MonitorApi {
	private mockServer: boolean;
	private lastStatus: Status = null;
	private static api: MonitorApi = null;
	
	private lastFetchTime = 0;
	private lastFetchAltitude = -100000;
	
	private mockedTripDistance = 0;
	private mockedTripTotalTime = 0;
	private mockedTripMovingTime = 0;
	private mockedTripAscendingElevation = 0;
	private mockedTripMotorEnergy = 0;
	
	constructor() {
		this.mockServer = window.location.protocol == "file:";
	}
	
	static get() {
		if(MonitorApi.api == null)
			MonitorApi.api = new MonitorApi();
		return MonitorApi.api;
	}
	
	getStatus() { return this.lastStatus; }
	
	async fetchStatus(): Promise<Status> {
		let apiStatus: ApiStatus;
		
		if(this.mockServer) {
			await new Promise(resolve => setTimeout(resolve, 200));
			
			let t = Date.now() / 1000.0;
			if(this.lastFetchTime == 0) this.lastFetchTime = t;
			let dt = t - this.lastFetchTime;
			this.lastFetchTime = t;
			
			let speed = (Math.cos(t*0.3)+1.0)*0.5 * 14; // in meters per second
			if(speed < 0.25) speed = 0;
			
			this.mockedTripDistance += speed * dt;
			this.mockedTripTotalTime += dt;
			if(speed > 0)
				this.mockedTripMovingTime += dt;
			
			let altitude = (Math.cos(t*0.0001)+1.0)*0.5 * 4500 - 200 + (Math.cos(t*0.03))*0.5 * 60; // in meters
			if(this.lastFetchAltitude == -100000) this.lastFetchAltitude = altitude;
			let altitudeChange = altitude - this.lastFetchAltitude;
			this.lastFetchAltitude = altitude;
			
			if(altitudeChange > 0)
				this.mockedTripAscendingElevation += altitudeChange;
				
			let voltage = (Math.cos(t*0.4)+1.0)*0.5 * 6 + 36;
			let current = (Math.cos(t*0.7)+1.0)*0.5 * 30;
			let newEnergy = voltage * current * dt / 3600;
			this.mockedTripMotorEnergy += newEnergy;
			
			apiStatus = {
				v: Math.round(voltage * 1000),
				c: Math.round(current * 1000),
				s: Math.round(speed * 1000),
				td: Math.round(this.mockedTripDistance),
				ttt: Math.round(this.mockedTripTotalTime),
				tmt: Math.round(this.mockedTripMovingTime),
				tae: Math.round(this.mockedTripAscendingElevation*10.0),
				tme: Math.round(this.mockedTripMotorEnergy*10.0),
				temp: Math.round((Math.cos(t*0.2)+1.0)*0.5 * 400 - 100),
				alt: Math.round(altitude * 1000)
			};
		} else {
			apiStatus = await new Promise<ApiStatus>((resolve, error) => {
				let request = new XMLHttpRequest();
				request.onreadystatechange = () => {
					if(request.readyState == 4) {
						if(request.status == 200) {
							resolve(JSON.parse(request.response));
						}
						else {
							error();
						}
					}
				};
				request.open('GET', '/api/status', true);
				request.send();
			});
		}
		
		this.lastStatus = {
			batteryVoltage: apiStatus.v / 1000,
			motorCurrent: apiStatus.c / 1000,
			speed: apiStatus.s / 1000,
			tripDistance: apiStatus.td,
			tripTotalTime: apiStatus.ttt,
			tripMovingTime: apiStatus.tmt,
			tripAscendingElevation: apiStatus.tae / 10,
			tripMotorEnergy: apiStatus.tme / 10,
			temperature: apiStatus.temp / 10,
			altitude: apiStatus.alt / 1000
		};
		
		return this.lastStatus;
	}
}
