#include "LinearFunctionBlock.h"
#include <iostream>     // std::cout
#include <fstream>      // std::ifstream

using namespace std;

LinearFunctionBlock::LinearFunctionBlock() {
	//printf(" > Linear Function Block created\n");
}

LinearFunctionBlock::LinearFunctionBlock(string name) : Unit(name) {
	//printf(" > %s (id:%u) created\n", name.c_str(), id);
}

LinearFunctionBlock::LinearFunctionBlock(string name, string configFilePath) : Unit(name) {
	//printf(" > %s (id:%u) created\n", name.c_str(), id);
	
	ifstream configfile(configFilePath);
	string line;

	bool fileExists = false;

	while (getline(configfile, line))
	{
		fileExists = true;

		bool lbIncl, ubIncl;
		bool x1inf, x2inf;
		string token;
		float x1, x2, kOrY1, dOrY2;
		bool kD;

		size_t pos1, pos2, pos3, pos4;
		
		if (line[0] == '[')
			lbIncl = true;
		else if (line[0] == ']')
			lbIncl = false;
		else
			continue;

		pos1 = line.find(",");
		if (pos1 != string::npos) {
			if (pos1 < 2)
				continue;
		}
		else
			continue;

		token = line.substr(1, pos1 - 1);

		if (token.length() == 1 && token[0] == 'i')
			x1inf = true;
		else {
			x1inf = false;
			x1 = stof(token, NULL);
		}
		
		pos2 = line.find(";");
		if (pos2 != string::npos)
			if (pos2 >= 5)
				if (line[pos2 - 1] == ']')
					ubIncl = true;
				else if (line[pos2 - 1] == '[')
					ubIncl = false;
				else
					continue;
			else
				continue;
		else
			continue;

		token = line.substr(pos1+1, pos2-pos1-2);

		if (token.length() == 1 && token[0] == 'i') {
			x2inf = true;
		}
		else {
			x2inf = false;
			x2 = stof(token, NULL);
		}

		if (pos2 < line.length()) {

			if (line[pos2 + 1] == '(') {

				pos4 = line.find(")", pos2);

				if (pos4 != string::npos) {
					if (pos4 < pos2 + 5)
						continue;
					kD = true;
				}
				else
					continue;
			}
			else if (line[pos2 + 1] == '[') {

				pos4 = line.find("]", pos2);

				if (pos4 != string::npos) {
					if (pos4 < pos2 + 5)
						continue;
					kD = false;
				}
				else
					continue;
			}
			else
				continue;

			pos3 = line.find(",", pos2);

			if (pos3 != string::npos) {
				if (pos3 < pos2 + 3) 
					continue;
			}
			else
				continue;

			token = line.substr(pos2 + 2, pos3 - pos2 - 2);
			kOrY1 = stof(token, NULL);

			token = line.substr(pos3 + 1, pos4 - pos3 - 1);
			dOrY2 = stof(token, NULL);


			//TODO: consider unsteady functions
			//TODO: differentiate between -infinity and +infinity
			LinearFunction *linearFunction = new LinearFunction();
			if (lbIncl == false && ubIncl == false)
				linearFunction->setDomain(false, false);
			else if(lbIncl == false)
				linearFunction->setDomain(false, true, x2);
			else if (ubIncl == false)
				linearFunction->setDomain(true, x1, false);
			else
				linearFunction->setDomain(true, x1, true, x2);



			if (kD == true)
				linearFunction->setKandD(kOrY1, dOrY2);
			else
				linearFunction->setKandD(x1, kOrY1, x2, dOrY2);

			addLinearFunction(linearFunction);
		}
	}

	if (fileExists == false) {
		cout << "FILE DOES NOT EXIST!" << endl;
		getchar();
	}

	//printFunction();
}


LinearFunctionBlock::~LinearFunctionBlock() {
	//TODO: delete allocated memory
}

//NOTE: for this time being, linear functions have to be filled beginning from lowest x value 
bool LinearFunctionBlock::addLinearFunction(LinearFunction *linearFunction) {
	if (lLinearFunctions.empty()) {
		//printf("empty\n");
		if (!(linearFunction->getDomain()->lowerBoundaryExist())) {
			lLinearFunctions.push_back(linearFunction);
			//printf(" - added function\n");
			return true;
		}
	}
	else
	{
		//printf("nicht empty\n");
		if (lLinearFunctions.back()->getDomain()->upperBoundaryExist() && linearFunction->getDomain()->lowerBoundaryExist()) {
			//printf("last function ub = %f, new function lb = %f\n", lLinearFunctions.back()->getDomain()->getUpperBoundary(), linearFunction->getDomain()->getLowerBoundary());
			if (lLinearFunctions.back()->getDomain()->getUpperBoundary() == linearFunction->getDomain()->getLowerBoundary()) {
				lLinearFunctions.push_back(linearFunction);
				//printf(" - added function\n");
				return true;
			}
		}
	}

	printf(" - couldn't add function\n");
	return false;
}


//TODO: jump discontinuity -> user must have the probability to set the value there
float LinearFunctionBlock::getY(float x) {	
	for (auto &linearFunction : lLinearFunctions) {
		//printf("x\n");
		if (linearFunction->getDomain()->lowerBoundaryExist() && linearFunction->getDomain()->upperBoundaryExist()) {
			if (x >= linearFunction->getDomain()->getLowerBoundary() && x <= linearFunction->getDomain()->getUpperBoundary()) {
				return linearFunction->getY(x);
			}
		}
		else if (linearFunction->getDomain()->lowerBoundaryExist()) {
			if (x >= linearFunction->getDomain()->getLowerBoundary()) {
				return linearFunction->getY(x);
			}
		}
		else if (linearFunction->getDomain()->upperBoundaryExist()) {
			if (x <= linearFunction->getDomain()->getUpperBoundary()) {
				return linearFunction->getY(x);
			}
		}
		else {
			return linearFunction->getY(x);
		}
	}

	//printf("hier?\n");

	//TODO: default return value is maybe not the best
	return 0;
}


void LinearFunctionBlock::printFunction() {

	if (lLinearFunctions.empty())
		cout << "is leer!" << endl;

	for (auto &linearFunction : lLinearFunctions) {
		printf("y = k*%f%+f for %+f < y < %+f\n", linearFunction->getK(), linearFunction->getD(), linearFunction->getDomain()->getLowerBoundary(), linearFunction->getDomain()->getUpperBoundary());
	}
}