/*
 * inputValue.cpp
 *
 *  Created on: Jul 18, 2017
 *      Author: vadims
 */
#include <iostream>
#include <sstream>
#include "InputValue.hpp"

InputValue::InputValue(double f)
{
	typeId=InputValue::SCALAR;
	argumentPos=0;
	factor = 1;
	value.push_back(f);
}


// reads from json format
// if passed value is scalar, one value is pushed into vector
// if passed value is object, it is analyzed
void InputValue::readJson(json j)
{
	factor = 1;
	if (j.is_string())
	{
		typeId = InputValue::STRING;
		argumentPos= 0;
		strValue = j.get<std::string>();
	}

	if (j.is_number())
	{
		typeId = InputValue::SCALAR;
		argumentPos= 0;
		value.push_back( j.get<float>() );
	}
	if (j.is_boolean() )
	{
		typeId = InputValue::BOOLEAN;
		if (j.get<bool>())
			value.push_back(1);
		else
			value.push_back(0);
	}
	else if (j.is_object())
	{

		if (j.find("factor") != j.end())
			factor = j.at("factor").get<double>();
		if (j.at("type").get<int>() == InputValue::EXPRESSION)
		{
			typeId = InputValue::EXPRESSION;
			argumentPos = 0;
			strValue = j.at("value").get<std::string>();
		}
		else if (j.at("type").get<int>() == InputValue::SCALAR)
		{
			if (j.find("kind") != j.end()) {
				if (j.at("kind") == "integer") {
					typeId = InputValue::INTEGER;
					argumentPos = 0;
					value.push_back(j.at("value").get<int>());
				}

			}
			else {

			typeId = InputValue::SCALAR;
			argumentPos = 0;
			value.push_back(j.at("value").get<double>() );
			}

		}

		else if (j.at("type").get<int>() == InputValue::TABLE)
		{
			InputValue::typeId = InputValue::TABLE;
			if (j.at("value").is_number())
				InputValue::value.push_back(j.at("value").get<double>());
			if (j.at("value").is_array())
			{
				for (json::iterator it = j.at("value").begin(); it != j.at("value").end(); ++it) {
					InputValue::value.push_back(it.value().get<double>());
				}
			}
			if (j.find("argument") != j.end())
			{
				argumentPos = j.at("argumentPosition").get<int>();
			}
			else
			{
				argumentPos = 0;
			}
		}

	}
}

InputValue::InputValue(const InputValue&iv)
{
	typeId = iv.typeId;
	value = iv.value;
	argumentPos = iv.argumentPos;
	strValue = iv.strValue;
	factor = iv.factor;
}



InputValue& InputValue::operator=( const InputValue& other ) {
	typeId = other.typeId;
	value = other.value;
	argumentPos = other.argumentPos;
	strValue = other.strValue;
	factor = other.factor;
    return *this;
 }


bool InputValue::operator==(const InputValue& rhs) const
{
	return ( (typeId == rhs.typeId) && (value == rhs.value) && (argumentPos == rhs.argumentPos) && (strValue == rhs.strValue));
}

double InputValue::getMaxValue()
{
	if (InputValue::typeId == InputValue::TABLE)
	{
		std::vector<float> values;
		for (int i=1; i< value.size(); i=i+2)
			values.push_back(value[i]);
		return *std::max_element(values.begin(), values.end());
	}

	if (InputValue::typeId == InputValue::SCALAR)
	{
		return value[0];
	}
	else return 0;
}

double InputValue::getFactor()
{
	return factor;
}

std::vector<double> InputValue::getValueVector()
{
	return value;
}
double InputValue::getMinValue()
{
	if (InputValue::typeId == InputValue::TABLE)
	{
		std::vector<double> values;
		for (int i=1; i< value.size(); i=i+2)
			values.push_back(value[i]);
		return *std::min_element(values.begin(), values.end());
	}

	if (InputValue::typeId == InputValue::SCALAR)
	{
		return value[0];
	}
	else return 0;
}

//warning, checking of value type is necessary!!!
void InputValue::setFloatValue(double val)
{
	InputValue::value[0] = val;
}


std::string InputValue::getValue()
{
	if ((InputValue::typeId == InputValue::STRING) or ((InputValue::typeId == InputValue::EXPRESSION)))
	{
		return strValue;
	}

	if (InputValue::typeId == InputValue::SCALAR)
	{
		std::stringstream out;
		out << std::scientific;
		out << InputValue::value[0];
		return out.str();
	}
	else if (InputValue::typeId == InputValue::INTEGER)
	{
		std::stringstream out;
		out << InputValue::value[0];
		return out.str();
	}
	else if (InputValue::typeId ==InputValue::BOOLEAN)
	{
		if (value[0]==1)
			return "1";
		else
			return "0";
	}
	else if (InputValue::typeId == InputValue::TABLE)
	{
		std::string outStr;
		// TEMPORARY!!! Remove or argumentPos == 0   !!!
		if ( (argumentPos == -1) or (argumentPos == 0) )
			outStr="InterpolationLinear[$Time]{";

		else
			outStr="InterpolationLinear[$" + std::to_string(argumentPos )  + "]{";

		int i=0;
		for (auto &k : InputValue::value)
		{
			if (i>0) outStr.append(", ");
			outStr.append(std::to_string(k));
			i++;
		}
		outStr.append("}");
		return outStr;
	}
	else if (InputValue::typeId == InputValue::COMPLEX)
	{
		std::string outStr="Complex[" + std::to_string(InputValue::value[0]) + ", " + std::to_string(InputValue::value[1]) + "]";
		return outStr;
	}
	else
	{
		return "NaN";
	}
}



double InputValue::getDoubleValue()
{
	if ((InputValue::typeId == InputValue::STRING) or ((InputValue::typeId == InputValue::EXPRESSION)))
	{
		return 0.0;
	}

	if (InputValue::typeId == InputValue::SCALAR)
	{
		return value[0];
	}
	else if (InputValue::typeId == InputValue::BOOLEAN)
	{
		if (value[0] == 1)
			return 1.0;
		else
			return 0.0;
	}
	else if (InputValue::typeId == InputValue::TABLE)
	{
		return value[1];
	}
	else if (InputValue::typeId == InputValue::COMPLEX)
	{
		std::string outStr = "Complex[" + std::to_string(InputValue::value[0]) + ", " + std::to_string(InputValue::value[1]) + "]";
		return InputValue::value[0];
	}
	else
	{
		return std::numeric_limits<double>::quiet_NaN();
	}
}

double InputValue::getDoubleValue(double argVal)
{
	if ((InputValue::typeId == InputValue::STRING) or ((InputValue::typeId == InputValue::EXPRESSION)))
	{
		return 0.0;
	}

	if (InputValue::typeId == InputValue::SCALAR)
	{
		return value[0];
	}
	else if (InputValue::typeId == InputValue::BOOLEAN)
	{
		if (value[0] == 1)
			return 1.0;
		else
			return 0.0;
	}
	else if (InputValue::typeId == InputValue::TABLE)
	{
		//check for exact match
		for (int i = 0; i < value.size()/2; i++)
		{
			if ((abs(value[i * 2] - argVal) < 1e-6))
				return value[i * 2 + 1];
		}

		//check between i and i+1
		for (int i = 0; i < value.size() / 2 - 1; i++)
		{
			if ((value[i * 2] < argVal) and (value[(i + 1) * 2] > argVal))
			{
				double x1 = value[i * 2];
				double x2 = value[(i + 1) * 2];
				double y1 = value[i * 2 + 1];
				double y2 = value[(i + 1) * 2 + 1];
				return (y2 - y1) / (x2 - x1) * (argVal - x1) + y1;
			}

		}
		return value[1];
	}
	else if (InputValue::typeId == InputValue::COMPLEX)
	{
		std::string outStr = "Complex[" + std::to_string(InputValue::value[0]) + ", " + std::to_string(InputValue::value[1]) + "]";
		return InputValue::value[0];
	}
	else
	{
		return std::numeric_limits<double>::quiet_NaN();
	}
}



std::string InputValue::getValueWithEnds()
{
	if ((InputValue::typeId == InputValue::STRING) or ((InputValue::typeId == InputValue::EXPRESSION)))
	{
		return strValue;
	}

	if (InputValue::typeId == InputValue::SCALAR)
	{
		return std::to_string(InputValue::value[0]);
	}
	else if (InputValue::typeId == InputValue::BOOLEAN)
	{
		if (value[0]==1)
			return "1";
		else
			return "0";
	}
	else if (InputValue::typeId == InputValue::TABLE)
	{
		std::string outStr;
		// TEMPORARY!!! Remove or argumentPos == 0   !!!
		if ( (argumentPos == -1) or (argumentPos == 0) )
			outStr="InterpolationLinear[$Time]{";

		else
			outStr="InterpolationLinear[$" + std::to_string(argumentPos )  + "]{";

		addEndValues();
		int i=0;
		for (auto &k : InputValue::value)
		{
			if (i>0) outStr.append(", ");
			outStr.append(std::to_string(k));
			i++;
		}
		outStr.append("}");
		return outStr;
	}
	else if (InputValue::typeId == InputValue::COMPLEX)
	{
		std::string outStr="Complex[" + std::to_string(InputValue::value[0]) + ", " + std::to_string(InputValue::value[1]) + "]";
		return outStr;
	}
	else
	{
		return "NaN";
	}
}


std::string InputValue::getDerivativeWithEnds()
{
		std::string outStr;
		outStr="dInterpolationAkima[$" + std::to_string(argumentPos )  + "]{";
		addEndValues();
		int i=0;
		for (auto &k : InputValue::value)
		{
			if (i>0) outStr.append(", ");
			outStr.append(std::to_string(k));
			i++;
		}
		outStr.append("}" );
		return outStr;
}

void InputValue::addEndValues()
{
	float END_MIN = -1e10;
	float END_MAX = 1e10;
	if (value[0] > END_MIN)
	{
		float arg = value[0];
		float val = value[1];
		value.insert(value.begin(), val);
		value.insert(value.begin(), END_MIN);
	}

	size_t sv = value.size();
	if (value[sv-2] < END_MAX)
	{
		float val = value[sv-1];
		value.push_back(END_MAX);
		value.push_back(val);
	}
}


int InputValue::getArgumentPos()
{
	return argumentPos;
}

InputValue::valueType InputValue::getTypeId()
{
	return typeId;
}

bool InputValue::isTable()
{
	if (typeId == InputValue::TABLE)
		return true;
	else
		return false;
}

bool InputValue::isBoolean()
{
	if (typeId == InputValue::BOOLEAN)
		return true;
	else
		return false;
}


std::pair<std::string,std::string> InputValue::getFirstPair()
{
	if (typeId==InputValue::TABLE)
	{
		return std::make_pair(std::to_string(value[0]), std::to_string(value[1]));
	}
	else
		return std::make_pair("0", "0");
}



