///////////////////////////////////////////////////////////
// Author:			Stefan Liersch
// Contact:			stefan.liersch@googlemail.com,
//					stefan.liersch@ufz.de
//
// Date:			2008-03-17
// last modified:	2008-03-18
//
//---------------------------------------------------------
// DESCRIPTION
//---------------------------------------------------------
//
//---------------------------------------------------------
// ToDo
//---------------------------------------------------------
//
///////////////////////////////////////////////////////////

#include <math.h>

#include <fstream>

#include "soil_salinity_assessment.h"

//-----------------------------------------------------------------
// CONSTRUCTORS
//-----------------------------------------------------------------
CSalinity::CSalinity()
{
}
//-----------------------------------------------------------------

CSalinity::CSalinity(vector_d &parameters, vector_d &weights)
{
	int		nSalinityClasses	= 3;
	float	min_range			= 0.0;
	float	max_range			= 1.0;

	m_error_code				= 0;
	m_salinity					= 0.0;

	m_parameters				= parameters;
	m_weights					= weights;

	m_membership.resize(nSalinityClasses);		// low, medium, high

	if ( _Check_weights(m_weights) ) {
		if ( _Check_ranges(parameters, min_range, max_range) ) {
			m_salinity = Calc_Salinity_v1(m_parameters, m_weights);
			Calc_MembershipDegree_v1(m_salinity, m_membership);
		} else {
			std::cout << "At least one of the parameters are out of range" << std::endl;
		}
	} else {
		std::cout << "Sum of weights is NOT equal to 1.0" << std::endl;
	}

}

CSalinity::CSalinity(vector_d &parameters, vector_i &weights, vector_b &checked)
{
	int		nSalinityClasses	= 3;
	float	min_range			= 0.0;
	float	max_range			= 1.0;

	m_error_code				= 0;
	m_salinity					= 0.0;
	m_salinity_local			= 0.0;
	m_salinity_expert			= 0.0;

	m_parameters				= parameters;
	m_weights_int				= weights;
	m_checked					= checked;

	m_membership.resize(nSalinityClasses);		// low, medium, high

	m_salinity = Calc_Salinity_v2(m_parameters, m_weights_int, m_checked);

	Calc_MembershipDegree_v1(m_salinity, m_membership);
}
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// DESTRUCTORS
//-----------------------------------------------------------------
CSalinity::~CSalinity()
{
	m_parameters.resize(0);
	m_weights.resize(0);
	m_membership.resize(0);
}

//-----------------------------------------------------------------
// SALINITY VALUE (STRING) (low, medium, high)
//-----------------------------------------------------------------
std::string CSalinity::Get_Salinity_3str()
{
	if (m_membership.size() == 3) {
		if ( m_membership[0] > m_membership[1] && m_membership[0] > m_membership[2] )
			return("low");
		if ( m_membership[1] > m_membership[0] && m_membership[1] > m_membership[2] )
			return("medium");
		if ( m_membership[2] > m_membership[0] && m_membership[2] > m_membership[1] )
			return("high");
	}
	return("");
}


//-----------------------------------------------------------------
// CALCULATE SALINITY VALUE
//-----------------------------------------------------------------
double CSalinity::Calc_Salinity_v1(vector_d &parms, vector_d &weights)
{
	double salinity = 0.0;

	for (unsigned int i = 0; i < parms.size(); i++) {
		salinity += parms[i] * weights[i];
	}

	return(salinity);
}
//-----------------------------------------------------------------

double CSalinity::Calc_Salinity_v2(vector_d &parms, vector_i &weights, vector_b &checked)
{
	int sum_weights = 0;
	double salinity = 0.0;

	for (unsigned int i = 0; i < weights.size(); i++) {
		if ( checked[i] ) sum_weights += weights[i];
	}

	if ( sum_weights > 0 ) {
		for (unsigned int i = 0; i < weights.size(); i++) {
			if ( checked[i] ) salinity += parms[i] * weights[i];
		}

		m_salinity_local = Calc_Salinity_Partly(parms, weights, checked, 0, 6);

		m_salinity_expert = Calc_Salinity_Partly(parms, weights, checked, 7, 13);

		return(salinity / sum_weights);

	} else {
		m_salinity_local = -1;
		m_salinity_expert = -1;
		return(-1);
	}
}
//-----------------------------------------------------------------

double CSalinity::Calc_Salinity_Partly(vector_d &parms, vector_i &weights, vector_b &checked,
									   int begin, int end)
{
	int sum_weights = 0;
	double salinity = 0.0;

	for (int i = begin; i <= end; i++) {
		if ( checked[i] ) sum_weights += weights[i];
	}

	if ( sum_weights > 0 ) {
		for (int i = begin; i <= end; i++) {
			if ( checked[i] ) salinity += parms[i] * weights[i];
		}

		return(salinity / sum_weights);
	} else {
		return(-1);
	}
}
//-----------------------------------------------------------------



//-----------------------------------------------------------------
// CALCULATE MEMBERSHIP DEGREE
//-----------------------------------------------------------------
void CSalinity::Calc_MembershipDegree_v1(double salinity, vector_d& membership)
{
	membership[0] = _Calc_MembershipDegree_Low(salinity);
	membership[1] = _Calc_MembershipDegree_Medium(salinity);
	membership[2] = _Calc_MembershipDegree_High(salinity);
}
//-----------------------------------------------------------------
double CSalinity::_Calc_MembershipDegree_Low(double salinity)
{
	if ( salinity >= 0.5 ) return(0.0);
	return(cos(salinity * PI));
}
//-----------------------------------------------------------------
double CSalinity::_Calc_MembershipDegree_Medium(double salinity)
{
	return( sin( (salinity + PI / 4)  * pow(PI,1.60551) ) );
}

//-----------------------------------------------------------------
double CSalinity::_Calc_MembershipDegree_High(double salinity)
{
	return( cos(salinity * PI + PI) );
}

//-----------------------------------------------------------------



//-----------------------------------------------------------------
bool CSalinity::_Check_weights(vector_d& weights) {
	// the sum of the weighting factors must be 1.0
	double sum = 0.0;
	for (unsigned int i = 0; i < weights.size(); i++) {
		sum += weights[i];
	}
	if (sum == 1.0) {
		return(true);
	} else {
		m_error_code = 1;
		return(false);
	}
}
//-----------------------------------------------------------------

bool CSalinity::_Check_ranges(vector_d &parm, double min, double max)
{
	for (unsigned int i = 0; i < parm.size(); i++) {
		if ( parm[i] < min || parm[i] > max) {
			m_error_code = 2;
			return(false);
		}
	}
	return(true);
}
//-----------------------------------------------------------------