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

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

#include "system/task.h"
#include "system/edge.h"
#include "system/variable.h"
#include "system/location.h"
#include "system/process.h"
#include "system/constraint.h"

#include "common/message.h"

#include <cassert>
#include <algorithm>
#include <iostream>

namespace cg {
  
    using namespace std;

    DomainTransitionGraph::DomainTransitionGraph(const CGVariable* var, const vector<CGVariable*>& all_variables, const IDMapper& procid_to_varid) : 
	vertices(var->upper - var->lower + 1, Vertex()),
	level(var->level),
	all_variables(all_variables),
	procid_to_varid(procid_to_varid)
    {
	debug() << "init domainTransitionGraph for variable " << var->name << " with level " << var->level << endl;
	assert(level != -1);
    }
    
    void DomainTransitionGraph::processEQConstraint(CGVariable* var, int value, Condition& cond) {
	cond.push_back(make_pair(var, value));
    }
    
    void DomainTransitionGraph::addTransition(int from, int to, const CGOperator* op, int op_index, const vector<pair<CGVariable*, int> >& additionalConstraints) {

	Transition trans(to, op_index);
	Condition& cond = trans.condition;
	vector<const IntConstraint*> intconstraints;
	vector<const IntAssignment*> intassignments;
	op->getIntegerConstraints(intconstraints);
	op->getIntegerAssignments(intassignments);
	
	for (uint32_t i = 0; i < intconstraints.size(); i++) {
	    const IntConstraint* ic = intconstraints[i];
	    
	    // TODO: HACK! So far, this only works for EQ constraints
	    // TODO: think about how to resolve this
	    assert (ic->comp == IntConstraint::EQ);
	    
	    CGVariable* var = all_variables[ic->lhs->id];
	    int value = ic->rhs;
	    
	    // only variables != var occur in transition guards
	    // TODO: != or < ?
	    if (var->level < level) {
		processEQConstraint(var, value, cond);
	    }
	    
	}
	
	Location* src_loc1 = op->edge1->src;
	int src_index1 = src_loc1->idInProcess;
	int procID1 = src_loc1->proc->id;
	int varid = procid_to_varid(PROCESS,procID1);
	if (varid != -1) {
	    CGVariable* var1 = all_variables[varid];
	    if (var1->level < level) {
		cond.push_back(make_pair(var1, src_index1));
	    }
	}
	
	if (!op->is_tau_operator()) {
	    Location* src_loc2 = op->edge2->src;
	    int src_index2 = src_loc2->idInProcess;
	    varid = procid_to_varid(PROCESS, src_loc2->proc->id);
	    if (varid != -1) {
		CGVariable* var2 = all_variables[varid];
		if (var2->level < level) {
		    cond.push_back(make_pair(var2, src_index2));
		}
	    }
	}
	
	for (uint32_t i = 0; i < additionalConstraints.size(); i++) {
	    if (additionalConstraints[i].first->level < level) {
		cond.push_back(additionalConstraints[i]);
	    }
	}
	
	vertices[from].push_back(trans);
    }
    
    
    // Fast Downwards procedure to remove duplicates and dominated transitions in the DTG
    bool DomainTransitionGraph::Transition::operator<(const Transition &other) const {
	if (target != other.target)
	    return target < other.target;
	else
	    return condition.size() < other.condition.size();
    }
       

    void DomainTransitionGraph::finalize() {
	
	for(uint32_t i = 0; i < vertices.size(); i++) {
	    // For all sources, sort transitions according to targets and condition length
	    sort(vertices[i].begin(), vertices[i].end());
	    
	    Vertex::iterator it = unique(vertices[i].begin(), vertices[i].end());
	    vertices[i].erase(it, vertices[i].end());

	    // For all transitions, sort conditions (acc. to pointer addresses)
	    for(uint32_t j = 0; j < vertices[i].size(); j++) {  
		Transition &trans = vertices[i][j];
		Condition &cond = trans.condition;
		sort(cond.begin(), cond.end());
	    }
	    // Look for dominated transitions
	    vector<Transition> undominated_trans;
	    vector<bool> is_dominated;
	    is_dominated.resize(vertices[i].size(), false);
	    for(uint32_t j = 0; j < vertices[i].size(); j++) {
		if(!is_dominated[j]) {
		    Transition &trans = vertices[i][j];
		    undominated_trans.push_back(trans);
		    Condition &cond = trans.condition;
		    uint comp = j + 1; // compare transition no. j to no. comp
		    // comp is dominated if it has same target and same and more conditions
		    while(comp < vertices[i].size()) {
			if(is_dominated[comp]) {
			    comp++;
			    continue;
			}
			Transition &other_trans = vertices[i][comp];
			assert(other_trans.target >= trans.target);
			if(other_trans.target != trans.target)
			    break; // transition and all after it have different targets
			else { //domination possible
			    assert(other_trans.condition.size() >= cond.size());
			    
			    if(cond.size() == 0) {
				is_dominated[comp] = true; // comp is dominated
				comp++;
			    }	      
			    else {
				bool same_conditions = true;
				for(uint32_t k = 0; k < cond.size(); k++) {
				    bool cond_k = false;
				    for(uint32_t l = 0; l < other_trans.condition.size(); l++) {
					if(other_trans.condition[l].first > cond[k].first){
					    break; // comp doesn't have this condition, not dominated
					}
					if(other_trans.condition[l].first == cond[k].first &&
					   other_trans.condition[l].second == cond[k].second) {
					    cond_k = true;
					    break; // comp has this condition, look for next cond
					}
				    }
				    if (!cond_k) { // comp is not dominated
					same_conditions = false; 
					break;
				    }
				}
				is_dominated[comp] = same_conditions;
				comp++;
			    }
			}
		    }
		}
	    }
	    
	    // remove the line below to avoid removing dominated transitions
	    vertices[i] = undominated_trans;
	}
    }

    std::ostream& DomainTransitionGraph::display(std::ostream& o) const {
	o << "Level: " << level << endl;
	for (uint32_t i = 0; i < vertices.size(); i++) {
	    o << "  From value " << i << ":" << endl;
	    for (uint32_t j = 0; j < vertices[i].size(); j++) {
		const Transition &trans = vertices[i][j];
		o << "    " << "To value " << trans.target << endl;
		for (uint32_t k = 0; k < trans.condition.size(); k++) {
		    o << "      if " << trans.condition[k].first->name
		      << " = " << trans.condition[k].second << endl;
		}
	    }
	}
	return o;
    } 
}
