
#include <string>
#include <sstream>
#include <vector>
#include <cmath>

#include "Circuit.h"
#include "Input.h"
#include "Output.h"
#include "Rule.h"
#include "Piece.h"
#include "Node.h"

//inicializacia mnozin na prazdne mnoziny
Circuit::Circuit() {
    inputs = new vector<Input*>();
    outputs = new vector<Output*>();
    rules = new vector<Rule*>();
    pieces = new vector<Piece*>();
}

Circuit::~Circuit() {
}


void Circuit::addInput(Input* input) {
    this->inputs->push_back(input);
}

void Circuit::addOutput(Output* output) {
    this->outputs->push_back(output);
}

void Circuit::addRule(Rule* rule) {
    this->rules->push_back(rule);
}

void Circuit::addPiece(Piece* piece) {
    this->pieces->push_back(piece);
}

vector<Input*>* Circuit::getInputs() {
    return this->inputs;
}

vector<Output*>* Circuit::getOutputs() {
    return this->outputs;
}

vector<Rule*>* Circuit::getRules() {
    return this->rules;
}

vector<Piece*>* Circuit::getPieces() {
    return this->pieces;
}

bool Circuit::isRuleIncluded(string name) {
    Rule* tmpRule;
    for (unsigned int i = 0; i < rules->size(); i++) {
        tmpRule = rules->at(i);
        if (name.compare(
                tmpRule->getName()) == 0) {
            return true;
        }
    }
    return false;
}

bool Circuit::isInputIncluded(string name) {
    Input* tmpInput;
    for (unsigned int i = 0; i < inputs->size(); i++) {
        tmpInput = inputs->at(i);
        if (name.compare(
                tmpInput->getName()) == 0) {
            return true;
        }
    }
    return false;
}

bool Circuit::isOutputIncluded(string name) {
    Output* tmpOutput;
    for (unsigned int i = 0; i < outputs->size(); i++) {
        tmpOutput = outputs->at(i);
        if (name.compare(
                tmpOutput->getName()) == 0) {
            return true;
        }
    }
    return false;
}

bool Circuit::isPieceIncluded(string name) {
    Piece* tmpPiece;
    for (unsigned int i = 0; i < pieces->size(); i++) {
        tmpPiece = pieces->at(i);
        if (name.compare(tmpPiece->rule->getName()) == 0) {
            return true;
        }
    }
    return false;
}

Piece* Circuit::getPiece(string ruleName) {
    Piece* tmpPiece;
    for (unsigned int i = 0; i < pieces->size(); i++) {
        tmpPiece = pieces->at(i);
        if (tmpPiece->rule->getName().compare(ruleName) == 0) {
            return tmpPiece;
        }
    }
    return NULL;
}

Piece* Circuit::getPiece(Rule* rule) {
    return this->getPiece(rule->getName());
}

Connection* Circuit::getConnection(string name){
	Connection* c;
	for(unsigned int ic = 0; ic < connections.size(); ic++){
		c = &(connections.at(ic));
		if(c->name.compare(name) == 0){
			return c;
		}
	}
	return NULL;
}

int Circuit::iOfNode(Node *n){
	for(unsigned int in = 0; in < nodes.size(); in++){
		if(n->comp(nodes.at(in))){
			return in;
		}
	}
	return -1;
}

bool Circuit::isTherePiece(Point* p, Node *nEnd){
	Piece* piece;
	Node *n;
	if(p->x == nEnd->x && p->y == nEnd->y){
		return false;
	}
	for(unsigned int ip = 0; ip < pieces->size(); ip++){
		piece = pieces->at(ip);
		if(p->x >= piece->x && p->x <= piece->x + piece->width &&
			p->y >= piece->y && p->y <= piece->y + piece->height){
			return true;
		}
	}
	for(unsigned int in = 0; in < nodes.size(); in++){
		n = nodes.at(in);
		if(p->x == n->x && p->y == n->y){
			return true;
		}
	}
	return false;
}

bool Circuit::isThereConnection(int x, int y, string withoutCnnt){
	Point p1, p2;
	int xMax, xMin, yMax, yMin;
	for(unsigned int ic = 0; ic < this->connections.size(); ic++){
		if(withoutCnnt.compare(this->connections.at(ic).name) != 0){
			for(unsigned int ip = 1; ip < this->connections.at(ic).path.size(); ip++){
				p1 = this->connections.at(ic).path.at(ip - 1);
				p2 = this->connections.at(ic).path.at(ip);
				xMax = max(p1.x, p2.x);
				xMin = min(p1.x, p2.x);
				yMax = max(p1.y, p2.y);
				yMin = min(p1.y, p2.y);
				//ak p1 a p2 lezia horizontalne a X[x,y] je medzi nimi
				if(yMax == yMin && y == yMin && x <= xMax && x >= xMin){
					return true;
				}
				//ak p1 a p2 lezia vertikalne a X[x,y] je medzi nimi
				if(xMax == xMin && x == xMin && y <= yMax && y >= yMin){
					return true;
				}
			}
		}
	}
	return false;
}

bool Circuit::arePntsOnTheSamePath(Point *p1, Point *p2){
	//body na ceste
	Point pPath1, pPath2;
	int xMax, xMin, yMax, yMin;
	for(unsigned int ic = 0; ic < this->connections.size(); ic++){
		for(unsigned int ip = 1; ip < this->connections.at(ic).path.size(); ip++){
			pPath1 = this->connections.at(ic).path.at(ip - 1);
			pPath2 = this->connections.at(ic).path.at(ip);
			xMax = max(pPath1.x, pPath2.x);
			xMin = min(pPath1.x, pPath2.x);
			yMax = max(pPath1.y, pPath2.y);
			yMin = min(pPath1.y, pPath2.y);
			//ak p1 a p2 lezia horizontalne a X[x,y] je medzi nimi
			if(yMax == yMin && p1->y == yMin && p1->x <= xMax && p1->x >= xMin 
				&& p2->y == yMin && p2->x <= xMax && p2->x >= xMin){
				return true;
			}
			//ak p1 a p2 lezia vertikalne a X[x,y] je medzi nimi
			if(xMax == xMin && p1->x == xMin && p1->y <= yMax && p1->y >= yMin
				&& p2->x == xMin && p2->y <= yMax && p2->y >= yMin){
				return true;
			}
		}
	}
	return false;
}

int Circuit::numOfPaths(Point p1, Point p2){
	int crossings = 0;
	double dx, dy;
	double t, ct;
	double maxLength, cMaxLength;
	Point cp1, cp2, v, cv;
	Connection c;
	for(unsigned int ic = 0; ic < connections.size(); ic++){
		c = connections.at(ic);
		for(unsigned int ip = 0; ip < c.path.size() - 1; ip++){
			cp1 = c.path.at(ip);
			cp2 = c.path.at(ip + 1);
			if(cp1.x == p1.x && cp1.y == p1.y && cp2.x == p2.x && cp2.y == p2.y){
				dx = p2.x -p1.x;
				dy = p2.y -p2.x;
				maxLength = sqrt(dx * dx + dy * dy);
				crossings += int(maxLength);
			}
			else{
				v.x = p2.x - p1.x;
				v.y = p2.y - p1.y;
				cv.x = cp2.x - cp1.x;
				cv.y = cp2.y - cp1.y;
				if(cv.x == 0 || cv.y == 0 || v.y == 0){
					if( (p1.x+p2.x)/2 == (cp1.x+cp2.x)/2 &&
						(p1.y+p2.y)/2 == (cp1.y+cp2.y)/2 ){
						crossings++;
					}
				}
				else{
					t = (cv.x * (p1.y - cp1.y) - cv.y * (p1.x -cp1.x)) / 
					(cv.x * (cv.y - v.y));
					ct = (p1.x - cp1.x + cv.x * t) / cv.x;
					dx = p2.x -p1.x;
					dy = p2.y -p2.x;
					maxLength = sqrt(dx * dx + dy * dy);
					dx = cp2.x -cp1.x;
					dy = cp2.y -cp2.x;
					cMaxLength = sqrt(dx * dx + dy * dy);
					if(t >= 0 && t <= maxLength && ct >= 0 && ct <= cMaxLength){
						crossings++;
					}
				}
			}
		}
	}
	return crossings;
}

string Circuit::toString() {
    string outs = "Circuit:\n";
    stringstream ss;
    Input* input;
    Output* output;
    Rule* rule;
    Rule* rinput;
    Piece* piece;
	Connection c;
    vector<Rule*>* rinputs;

    for (unsigned int i = 0; i < inputs->size(); i++) {
        input = inputs->at(i);
        outs += "in: " + input->getName() + "\n";
    }
    for (unsigned int i = 0; i < outputs->size(); i++) {
        output = outputs->at(i);
        outs += "out: " + output->getName() + "\n";
    }
    for (unsigned int i = 0; i < rules->size(); i++) {
        rule = rules->at(i);
        outs += "rule: " + rule->getName() + " [" + rule->getType() + "]\n";
        rinputs = rule->getInputs();
        for (unsigned int j = 0; j < rinputs->size(); j++) {
            rinput = rinputs->at(j);
            outs += "\t rin: " + rinput->getName() + "\n";
        }
    }
    for (unsigned int i = 0; i < pieces->size(); i++) {
        piece = pieces->at(i);
        ss << "piece: " << piece->rule->getName() << " [" << piece->x << ", " <<
            piece->y << ", " << piece->width << "x" << piece->height << "]" << endl;
    }

	for (unsigned int i = 0; i < connections.size(); i++){
		c = connections.at(i);
		ss << "Connection: " << c.name << " in:" << c.inner << endl;
		for (unsigned int ip = 0; ip < c.path.size(); ip++){
			ss << "\t [" << c.path.at(ip).x << "," << c.path.at(ip).y << "], dir=" 
				<< c.path.at(ip).dir << endl;
		}
	}
	outs += ss.str();

    return outs;
}
