// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: cg_operator.cpp 823 2011-10-01 14:48:13Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "causalgraph/cg_operator.h"
#include "causalgraph/cg_variable.h"

#include "system/edge.h"
#include "system/assignment.h"
#include "system/constraint.h"
#include "system/guard.h"
#include "system/variable.h"
#include "system/process.h"
#include "system/location.h"
#include "system/effect.h"

#include <cassert>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

using namespace std;

namespace cg {

    // TODO: use class variables vector<CGVariable*> for the source and destination variables
    
    CGOperator::CGOperator(Edge* edge1, Edge* edge2, const IDMapper& procid_to_varid) :
	procid_to_varid(procid_to_varid),
	edge1(edge1),
	edge2(edge2)
    {}
    
    void CGOperator::getSourceVariablesForEdge(const Edge* edge, const vector<CGVariable*>& all_variables, vector<CGVariable*>& variables) const {
	foreach (const IntConstraint* ic, edge->guard->intconstraints) {
	    int index = ic->lhs->id;
	    
	    CGVariable* var = all_variables[index];
	    assert(ic->lhs->name == var->name);
	    
	    variables.push_back(var);
	}
	
	// variable corresponding to process
	int varid = procid_to_varid(PROCESS, edge->getProcess()->id);

	if (varid != -1) {
	    variables.push_back(all_variables[varid]);
	}
	
	// integer assignments v:=w cause an implicit guard variable w
	foreach (const IntAssignment* ia, edge->effect->intassigns) {
	    // const vector<IntAssignment*>& intassignments = edge->effect->intassigns;
	    // for (uint32_t i = 0; i < intassignments.size(); i++) {
	    const VarAssignment* va = dynamic_cast<const VarAssignment*>(ia);
	    if (va) {
		variables.push_back(all_variables[va->rhs->id]);
	    }
	}
    }
    
    void CGOperator::getDestinationVariablesForEdge(const Edge* edge, const vector<CGVariable*>& all_variables, vector<CGVariable*>& variables) const {
	
	// lhs integer assignment variables
	foreach (const IntAssignment* ia, edge->effect->intassigns) {
	    variables.push_back(all_variables[ia->lhs->id]);
	}
	
	// variable corresponding to process
	int varid = procid_to_varid(PROCESS, edge->getProcess()->id);	
	if (varid != -1) {
	    variables.push_back(all_variables[varid]);
	}
    }

    vector<CGVariable*> CGOperator::getSourceVariables(const vector<CGVariable*>& all_variables) const {
	vector<CGVariable*> variables;
	
	getSourceVariablesForEdge(edge1, all_variables, variables);
	if (edge2) {
	    getSourceVariablesForEdge(edge2, all_variables, variables);
	}
	
	return variables;
    }
    
    vector<CGVariable*> CGOperator::getDestinationVariables(const vector<CGVariable*>& all_variables) const {
	vector<CGVariable*> variables;
	
	getDestinationVariablesForEdge(edge1, all_variables, variables);
	if (edge2)
	    getDestinationVariablesForEdge(edge2, all_variables, variables);
	
	return variables;
    }
    
    void CGOperator::getIntegerAssignments(vector<const IntAssignment*>& integerassignments) const {
	integerassignments.clear();
	foreach (const IntAssignment* ia, edge1->effect->intassigns) {
	    integerassignments.push_back(ia);
	}
	
	if (edge2) {
	    foreach (const IntAssignment* ia, edge2->effect->intassigns) {
		integerassignments.push_back(ia);
	    }
	}
    }

    void CGOperator::getIntegerConstraints(vector<const IntConstraint*>& integerconstraints) const {
	integerconstraints.clear();
	foreach (const IntConstraint* ic, edge1->guard->intconstraints) {
	    integerconstraints.push_back(ic);
	}
	if (edge2) {
	    foreach (const IntConstraint* ic, edge2->guard->intconstraints) {
		integerconstraints.push_back(ic);
	    }
	}
    }
        
    std::ostream& CGOperator::display(std::ostream& o) const {
	o << *edge1;
	if (edge2) {
	    o << endl << *edge2;
	}
	return o;
    }    
}
