#ifndef ALLINONESENSOR_H
#define ALLINONESENSOR_H

#include "Settings.h"
#include <string>
#include "../WiFiSensor.h"
#include "../AccelerometerSensor.h"
#include "../GyroscopeSensor.h"
#include "../BarometerSensor.h"
#include <Indoor/sensors/offline/OfflineAndroid.h>

class AllInOneSensor :
		public WiFiSensor, public AccelerometerSensor, public GyroscopeSensor, public BarometerSensor,
		public OfflineAndroidListener {

private:

	std::string file;
	bool running = false;
	std::thread thread;

public:

	AllInOneSensor(const std::string& file) : file(file) {
		;
	}

	void start() {
		if (running) {return;}
		running = true;
		thread = std::thread(&AllInOneSensor::run, this);
	}

	void stop() {
		if (!running) {return;}
		running = false;
		thread.join();
	}

	bool isRunning() const override {
		return running;
	}

protected:

	virtual void onGyroscope(const Timestamp _ts, const GyroscopeData data) override {
		const Timestamp ts = relativeTS(_ts);
		handbrake(ts);
		GyroscopeSensor::informListeners(ts, data);
	}

	virtual void onAccelerometer(const Timestamp _ts, const AccelerometerData data) override {
		const Timestamp ts = relativeTS(_ts);
		handbrake(ts);
		AccelerometerSensor::informListeners(ts, data);
	}

	virtual void onGravity(const Timestamp ts, const AccelerometerData data) override {
		(void) ts;
		(void) data;
	}

	virtual void onWiFi(const Timestamp _ts, const WiFiMeasurements data) override {
		const Timestamp ts = relativeTS(_ts);
		handbrake(ts);
		WiFiMeasurements copy = data;
		for (WiFiMeasurement& m : copy.entries) {m.ts = ts;}	// make each timestmap also relative
		WiFiSensor::informListeners(ts, copy);
	}

	virtual void onBarometer(const Timestamp _ts, const BarometerData data) override {
		const Timestamp ts = relativeTS(_ts);
		handbrake(ts);
		BarometerSensor::informListeners(ts, data);
	}

private:

	Timestamp baseTS;
	Timestamp relativeTS(const Timestamp ts) {
		if (baseTS.isZero()) {baseTS = ts;}
		return ts - baseTS;
	}

	/** handbrake for the offline-parser to ensure realtime events */
	Timestamp startSensorTS;
	Timestamp startSystemTS;

	void handbrake(const Timestamp ts) {
		if (startSensorTS.isZero()) {startSensorTS = ts;}
		if (startSystemTS.isZero()) {startSystemTS = Timestamp::fromUnixTime();}
		const Timestamp runtimeOfflineData = ts - startSensorTS;;
		const Timestamp runtimeSystemTime = (Timestamp::fromUnixTime() - startSystemTS) * Settings::offlineSensorSpeedup;
		const Timestamp diff = (runtimeOfflineData - runtimeSystemTime);
		if (diff > Timestamp::fromMS(0)) {
			std::this_thread::sleep_for(std::chrono::milliseconds(diff.ms()));
		}
	}

private:

	/**
	 * file-parsing runs in a background thread
	 * the parsing is manually slowed down via handbrake()
	 * which blocks during the event callbacks
	 */
	void run() {
		OfflineAndroid parser;
		parser.parse(file, this);
	}

};

#endif // ALLINONESENSOR_H
