#include "Bmi.h"

//   #include <stdio.h>
//   #include <iostream>
//   using namespace std;

/**
*constructs a new BMI
*/
Bmi::Bmi(int index, QWidget *parent):Measurable(index,parent)
 {
	allDependenciesSetp=false;
	this->bmi = true;
	this->object = "Bmi";
 }

/**
*Sets the neccesary information to calculate BMI.
*@param height must be a bodypart measuring the height in cm!
*@param weihgt must be a bodypart measuring the weight in kg!
*/
void Bmi::setDependecies(BodyPart *height, BodyPart *weight){
this->height = height;
this->weight = weight;
allDependenciesSetp=true;
}

/**
*Sets the neccesary information to calculate BMI.
*@param height must be a bodypart measuring the height in cm!
*/
void Bmi::setHeight(BodyPart *height){
this->height = height;
heightSet=true;
if(heightSet && weightSet){allDependenciesSetp=true;}
}

/**
*UnSets the neccesary information to calculate BMI.
*@effect: allDependenciesSet() == false
*/
void Bmi::unSetHeight(){
heightSet=false;
allDependenciesSetp=false;
}

/**
*UnSets the neccesary information to calculate BMI.
*@effect: allDependenciesSet() == false
*/
void Bmi::unSetWeight(){
weightSet=false;
allDependenciesSetp=false;
}


/**
*Sets the neccesary information to calculate BMI.
*@param weight must be a bodypart measuring the weight in kg!
*/
void Bmi::setWeight(BodyPart *weight){
this->weight = weight;
weightSet=true;
if(heightSet && weightSet){allDependenciesSetp=true;}

}


/**
*a string version containing all the necesary information about this Bmi oject
*Heightname ! weightName !
*\note If height and weight are not specified, it returns somethings that is NOT usefull ... So check isLegal() first
*/
QString Bmi::toString(){
QString out;
if(allDependenciesSet()){
out = height->objectName();
out.append("!");
out.append(weight->objectName());
out.append("!");}

return out;
}



/**
*Returns the bmi or -1 when no data is available
*/
int Bmi::getValue(StringDate date){
bool a = height->hasMeasurement(date.getQDate()) && weight->hasMeasurement(date.getQDate());
bool b = height->hasMeasurement(date.getQDate());
bool c = weight->hasMeasurement(date.getQDate());

if(a){
	float h = height->getMeasurement(date.getQDate())->getValue(); //cm
	h = h/100; //in meters
	float w = weight->getMeasurement(date.getQDate())->getValue(); //kg
	return (w)/(h*h);
	}
if(b && weight->getMeasurements().size()>0){
	float h = height->getMeasurement(date.getQDate())->getValue(); //cm
	h = h/100; //in meters
	BodyMeasurement *earlymeasurement;
	BodyMeasurement *latemeasurement = weight->getMeasurements().at(0);
	int i=1;
	if(latemeasurement->getDate() > date) {
		///use the most recent value of weight
		float w = latemeasurement->getValue(); //kg
		return (w)/(h*h);
		}
	while(i<weight->getMeasurements().size() && latemeasurement->getDate() < date){
		earlymeasurement = latemeasurement;
		latemeasurement = weight->getMeasurements().at(i);
		i++;
		}
	int daystolatesample = -date.getQDate().daysTo(latemeasurement->getDate().getQDate());
	///the minus sign is because latesample is taken on an earlier date then the height. Because no later
	///sample is available
	int daystoearlysample = -date.getQDate().daysTo(earlymeasurement->getDate().getQDate());
	float alpha = daystolatesample/(daystolatesample+daystoearlysample);
	float w = (1-alpha)*latemeasurement->getValue() + alpha*earlymeasurement->getValue(); //kg
	return (w)/(h*h);
	}

if(c && height->getMeasurements().size()>0){
	BodyMeasurement *earlymeasurement;
	BodyMeasurement *latemeasurement = height->getMeasurements().at(0); //at(0) => de oudste
	int i=1;
	if(latemeasurement->getDate() > date) {
		///use the most recent value of weight
		float w = weight->getMeasurement(date.getQDate())->getValue(); 
		float h = latemeasurement->getValue(); 
		h = h/100; //in meters
		return (w)/(h*h);
		}
	while(i<height->getMeasurements().size() && latemeasurement->getDate() < date){
		earlymeasurement = latemeasurement;
		latemeasurement = height->getMeasurements().at(i);
		i++;
		}
	float w = weight->getMeasurement(date.getQDate())->getValue(); 
	int daystolatesample = date.getQDate().daysTo(latemeasurement->getDate().getQDate());
	int daystoearlysample = -date.getQDate().daysTo(earlymeasurement->getDate().getQDate());
	float alpha = daystolatesample/(daystolatesample+daystoearlysample);
	float h = ((1-alpha)*latemeasurement->getValue() + alpha*earlymeasurement->getValue()); //kg
	h = h/100; //in meters
	return (w)/(h*h);
	}

if( !c && !c && height->getMeasurements().size()>0 && weight->getMeasurements().size()>0){
	BodyMeasurement *earlymeasurement;
	BodyMeasurement *latemeasurement = weight->getMeasurements().at(0);
	int i=1;
	while(i<weight->getMeasurements().size() && latemeasurement->getDate() < date){
		earlymeasurement = latemeasurement;
		latemeasurement = weight->getMeasurements().at(i);
		i++;
		}
	int daystolatesample = date.getQDate().daysTo(latemeasurement->getDate().getQDate());
	int daystoearlysample = -date.getQDate().daysTo(earlymeasurement->getDate().getQDate());
	float alpha = daystolatesample/(daystolatesample+daystoearlysample);
	float w = ((1-alpha)*latemeasurement->getValue() + alpha*earlymeasurement->getValue()); //kg


	latemeasurement = height->getMeasurements().at(0);
	i=1;
	while(i<height->getMeasurements().size() && latemeasurement->getDate() < date){
		earlymeasurement = latemeasurement;
		latemeasurement = height->getMeasurements().at(i);
		i++;
		}
	daystolatesample = date.getQDate().daysTo(latemeasurement->getDate().getQDate());
	daystoearlysample = -date.getQDate().daysTo(earlymeasurement->getDate().getQDate());
	alpha = daystolatesample/(daystolatesample+daystoearlysample);
	float h = ((1-alpha)*latemeasurement->getValue() + alpha*earlymeasurement->getValue()); //kg
	h = h/100; //in meters
	return (w)/(h*h);
	}

return -1;
}

/**
*This function returns a vector with datapoints oredered from old to new.
*X: nb of days from today
*Y: value of Bmi
*/
QVector<QPointF> Bmi::getVector(){
QVector<QPointF> vector;
if(allDependenciesSet()){
	QMap<int,QPointF> map;
	map = addPointsForVector(map, getHeight());
	map = addPointsForVector(map, getWeight());
	vector = QVector<QPointF>::fromList(map.values());
	return vector;
	}
else{
	return vector;
	}
}

/**
*Returns a map with daysfromtoday as key and the QPointF(daysfromtoday,bmivalue) as value
*the bmi values are added to map on the dates of values of measurements of part
*/
QMap<int,QPointF>  Bmi::addPointsForVector(QMap<int,QPointF> map, BodyPart* part){
for (int i=0;i<part->getMeasurements().size();i++){
	QDate dateOfMeasurement = part->getMeasurements().at(i)->getDate().getQDate();
	QPointF pointf;
	int key = dateOfMeasurement.daysTo(QDate::currentDate());
	pointf.setX(key);
	pointf.setY(getValue(StringDate(dateOfMeasurement)));
	map.insert(key,pointf);
	}
return map;
}

/**
*Returns the height object, or maybe null when allDependenciesSet isn't true
*/
BodyPart *Bmi::getHeight(){
return this->height;
}

/**
*Returns the weight object, or maybe null when allDependenciesSet isn't true
*/
BodyPart *Bmi::getWeight(){
return this->weight;
}


/**
*Returns true when all dependencies for the automeasurement: bmi are setDependecies
*True when the BMI value and tostring value is legal and can be used
*/
bool Bmi::allDependenciesSet(){
return this->allDependenciesSetp;
}

/**
*@effect new.getIndex() == index
*/
void Bmi::setIndex(int index){
this->index = index;
}

/**
*Returns the index (ID)
*/
int Bmi::getIndex(){
return this->index;
}


