#ifndef ACTIVITYDETECTION_H
#define ACTIVITYDETECTION_H

#include <string>
#include "Activities.h"
#include <vector>

#include <Indoor/math/MovingAVG.h>
#include <Indoor/math/MovingMedian.h>
#include <Indoor/grid/walk/GridWalk.h>

#include <KLib/math/statistics/Statistics.h>


#include <Indoor/math/Distributions.h>

/**
 * classification of pedestrian activities using sensor inputs.
 * sensor inputs are passed into this element
 * recognized acitivities are the output
 *
 */
class ActivityDetection {

private:

	/** the currently detected activity */
	Activity current = Activity::UNKNOWN;

	K::Statistics<float> mag;
	//K::Statistics<float> hpa;
	std::vector<float> hpa;

	MovingMedian<float> hpaAvg;

public:

	ActivityDetection() : hpaAvg(3) {;}

	/** add accelerometer values */
	void addAccel(float accel[3]) {

		float magnitude = std::sqrt(accel[0]*accel[0] + accel[1]*accel[1] + accel[2]*accel[2]);
		mag.add(magnitude - 9.81f);

	}

	/** add barometer values */
	void addBaro(float hpa) {
		//hpaAvg.add(hpa);
		//float smoothed = hpaAvg.get();
		//this->hpa.add(smoothed);
		this->hpa.push_back(hpa);
		if (this->hpa.size() > 40) {analyze();}
	}

	struct ActClass {
		Activity act;
		Distribution::Normal<float> barometer;
		Distribution::Normal<float> magnitude;
		ActClass(const Activity act, float muMag, float muBaro, float varMag, float varBaro) :
			act(act), barometer(muBaro, std::sqrt(varBaro)), magnitude(muMag, std::sqrt(varMag)) {
			;
		}
		float getProbability(const float hpaRange, const float varMag) const {
			return	barometer.getProbability(hpaRange) *
					magnitude.getProbability(varMag);
		}
	};

	void analyze() {

//		std::vector<ActClass> classes = {
//			ActClass(Activity::STANDING, 0.042118, 0.01, 0.002322, 0.001),
//			ActClass(Activity::STAIRS, 3.561667, 0.035, 1.392631, 0.002),		// stair up
//			ActClass(Activity::STAIRS, 5.529414, -0.035, 3.416315, 0.002),		// stair down
//			ActClass(Activity::WALKING, 1.669506, 0.01, 0.273880, 0.001),
//		};

//		auto comp = [&] (const ActClass& a, const ActClass& b) {
//			return a.getProbability(hpa.getRange(), mag.getStdDev()) < b.getProbability(hpa.getRange(), mag.getStdDev());
//		};

//		auto it = std::max_element(classes.begin(), classes.end(), comp);

//		current = (*it).act;


		if (mag.getStdDev() < 0.3) {
			current = Activity::STANDING;
		} else if (mag.getStdDev() < 3) {
			current = Activity::WALKING;
		} else {
			float hpaD = hpa.front() - hpa.back();
			if (hpaD > 0.01) {
				current = Activity::STAIRS_UP;
			} else if (hpaD < -0.01) {
				current = Activity::STAIRS_DOWN;
			} else {
				current = Activity::WALKING;
			}
		}

//		current = (Activity) idx;
//		current = Activity::UNKNOWN;

		mag.reset();
		hpa.clear();

	}

	/** get the currently detected activity */
	Activity getCurrentActivity() const {
		return current;
	}

	std::string toString() const {
		switch (current) {
			case Activity::UNKNOWN:		return "unknown";
			case Activity::STANDING:	return "standing";
			case Activity::WALKING:		return "walking";
			case Activity::STAIRS_UP:	return "stairs up";
			case Activity::STAIRS_DOWN:	return "stairs down";
			case Activity::ELEVATOR:	return "elevator";
		}
		throw "should not happen";
	}

};

#endif // ACTIVITYDETECTION_H
