#include <Projection.h>
#include <parser.h>
#include <command>


Projection :: Projection(int ID, string argvComp, argvOpt geometry, const vector <Component *> &inputComps) :
	ID_(ID),
	argvComp_(argvComp),
	geometry_(geometry),
	inputComps_(inputComps)
{
	terminal_ = "wxt";
	border_ = 0.0;
	xmin_ = 0;
	xmax_ = 0;
	ymin_ = 0;
	ymax_ = 0;
	sRange_ = false;

	extractArgv();

	if ( geometry_ == TRIANGLE )
		addCmd(new TriangCommand(this));

	gplt = new Gnuplot("lines");
	gplt->reset_plot();
	gplt->cmd("set term %s noraise title 'Projection %d'", terminal_.c_str(), ID);
}


Gnuplot * Projection :: projection()
{
	return gplt;
}


void Projection :: addComp(Component *comp_)
{
	inputComps_.push_back(comp_);
}


void Projection :: addCmd(Command *cmd_)
{
	cmds_.push_back(cmd_);
}


Command * Projection :: cmd(int i)
{
	return cmds_[i];
}


int Projection :: nCmd()
{
	return cmds_.size();
}


int Projection :: ID()
{
	return ID_;
}


string Projection :: terminal()
{
	return terminal_;
}


string Projection :: compToProj(int i)
{
	return compToProj_[i];
}


string Projection :: compToLabel(int i)
{
	return compToLabel_[i];
}


string Projection :: colorToProj(int i)
{
	return colorToProj_[i];
}


int Projection :: nComps()
{
	return compToProj_.size();
}


float Projection :: xmin()
{
	if ( sRange_ )
		return xSmin_;
	else
		return xmin_;
}


float Projection :: xmax()
{
	if ( sRange_ )
		return xSmax_;
	else
		return xmax_;
}


float Projection :: ymin()
{
	if ( sRange_ )
		return ySmin_;
	else
		return ymin_;
}


float Projection :: ymax()
{
	if ( sRange_ )
		return ySmax_;
	else
		return ymax_;
}


void Projection :: extractArgv()
{
	string argvLabel_ = argvComp_;
	string argvRange_ = argvComp_;

	replaceCompName();
	calcRange(argvRange_);

	// Strip component names to projection
	stringstream ss (argvComp_);

	while ( ss.good() )
	{
		string substr;
		getline (ss, substr, ':');
		compToProj_.push_back(substr);
	}

	// apply triangular tranformation 
	if ( geometry_ == TRIANGLE )
	{
		if ( compToProj_.size() > 2 )
		{
			cout << "Triangular projection must have only two components!" << endl;
			exit(EXIT_FAILURE);
		}
		triangularTransform();
	}

	// Strip component names to Label
	stringstream sl (argvLabel_);

	while ( sl.good() )
	{
		string substr;
		getline (sl, substr, ':');
		compToLabel_.push_back(substr);
	}

	findColors();
}


void Projection :: calcRange(string argvRange_)
{
	Parser prs;
	string minRange_ = argvRange_;
	string maxRange_ = argvRange_;
	stringstream nstr;

	// Substitute for min values
	for (int i=0; i < inputComps_.size(); i++)
	{
		nstr << inputComps_[i]->min();
		replaceAll (minRange_, inputComps_[i]->name(), nstr.str());
		nstr.str(string());
	}

	// Substitute for max values
	for (int i=0; i < inputComps_.size(); i++)
	{
		nstr << inputComps_[i]->max();
		replaceAll (maxRange_, inputComps_[i]->name(), nstr.str());
		nstr.str(string());
	}

	// Strip component names to min Range
	vector <float> min;
	stringstream s1 (minRange_);

	while ( s1.good() )
	{
		string substr;
		getline (s1, substr, ':');
		min.push_back(prs.parse(substr.c_str()));
	}

	// Strip component names to max Range
	vector <float> max;
	stringstream s2 (maxRange_);

	while ( s2.good() )
	{
		string substr;
		getline (s2, substr, ':');
		max.push_back(prs.parse(substr.c_str()));
	}

	xmin_ = min[0];
	xmax_ = max[0];
	ymin_ = min[1];
	ymax_ = max[1];

	for (int r=2; r < min.size(); r++)
	{
		if ( min[r] < ymin_ ) ymin_ = min[r];
		if ( max[r] > ymax_ ) ymax_ = max[r];
	}

	ymin_ = ymin_ - border_ * (ymax_ - ymin_)/2;
	ymax_ = ymax_ + border_ * (ymax_ - ymin_)/2;
}


void Projection :: replaceCompName()
{
	stringstream nstr;

	for (int i=0; i < inputComps_.size(); i++)
	{
		nstr << "$" << i+1;
		replaceAll (argvComp_, inputComps_[i]->name(), nstr.str());
		nstr.str(string());
	}

}


void Projection :: replaceAll(string& str, const string& from, const string& to)
{
	if(from.empty())
		return;

	size_t start_pos = 0;

	while((start_pos = str.find(from, start_pos)) != string::npos)
	{
		str.replace(start_pos, from.length(), to);
		start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
	}

}


bool Projection :: sRange()
{
	return sRange_;
}


void Projection :: setSrange(float xmin, float xmax, float ymin, float ymax)
{
	xSmin_ = xmin;
	xSmax_ = xmax;
	ySmin_ = ymin;
	ySmax_ = ymax;
	sRange_ = true;
}


void Projection :: resetSrange(void)
{
	sRange_ = false;
}


void Projection :: triangularTransform()
{
/*
      subroutine map ( x, y, xmap, ymap )
      real     x, y, xmap, ymap

      real     rt3ov2
      data     rt3ov2  /  0.8660254  /

      xmap = x + 0.5 * y
      ymap = rt3ov2 * y

      return
      end
*/

	compToProj_[0] = "(" + compToProj_[0] + "+0.5*" +  compToProj_[1] + ")";
	compToProj_[1] = "(0.8660254*" +  compToProj_[1] + ")";

}


void Projection :: findColors()
{
	size_t found;
	for (int i=1; i < compToProj_.size(); i++)
	{
		for (int j=0; j < inputComps_.size(); j++)
		{
 			found = compToProj_[i].find(inputComps_[i]->name());
			if ( found != string::npos )
				colorToProj_.push_back(inputComps_[i]->color());
		}
	}
}


void Projection :: showComps()
{
		cout << endl << "Projection.showComps() - Available components:" << endl;
		for (int i=0; i < inputComps_.size(); i++)
		{
			cout << "Component Object " << i+1 << endl;
			cout << "   Name: " << inputComps_[i]->name() << endl;
			cout << "   Min: " << inputComps_[i]->min() << endl;
			cout << "   Max: " << inputComps_[i]->max() << endl;
		}
		cout << endl;

		for (int i=0; i < compToProj_.size(); i++)
		{
			cout << "Comp to project: " << compToProj_[i] << endl;
		}
		cout << endl;
		cout << endl;
}


