// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: transition.cpp 815 2011-09-10 12:57:27Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "transition.h"

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

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

using namespace std;

Transition::Transition() {}

Transition::Transition(const Edge* edge1, const Edge* edge2, uint32_t uid) :
    edge1(edge1),
    edge2(edge2),
    uid(uid)
{
    assert(edge1);
}

ostream& Transition::display(ostream& o) const {
    o <<  "    " << *edge1;
    if (edge2) {
	o << endl << "    " << *edge2;
    }    
    return o;
}

////////////////////////////////////////////////////////////////////

TransitionBuilder& TransitionBuilder::builder(const System* system) {
    static TransitionBuilder tb(system);
    return tb;
}

TransitionBuilder::TransitionBuilder(const System* system) : 
    nrEdges(system->getTotalNrEdges()),
    trans(nrEdges, vector<Transition*>(nrEdges+1, NULL)),
    nrTransitions(0)
{
    assert(system);

    static const uint32_t nrChans = system->getNrChannels();

    typedef vector<const Edge*> list_t;
    vector<list_t> bang(nrChans, list_t());
    vector<list_t> que(nrChans, list_t());
    
    for (uint32_t i = 0; i < nrChans; i++) {
	assert(bang[i].empty());
	assert(que[i].empty());
	bang[i].clear();
	que[i].clear();
    } 

    for_each (const Process* proc, system->procs) {
	for_each (const Edge* edge, proc->edges) {
	    vector<Edge*> edges;
	    switch (edge->getType()) 
	    {
	    case Edge::TAU:
		trans[edge->idInSystem][nrEdges] = new Transition(edge, NULL, nrTransitions++);
		alltrans.push_back(trans[edge->idInSystem][nrEdges]);
		assert(nrTransitions == alltrans.size());
		break;
	    case Edge::BANG:
		bang[edge->getAction()->id].push_back(edge);
		break;
	    case Edge::QUE:
		que[edge->getAction()->id].push_back(edge);
		break;
	    default:
		assert(false);
	    }
	}
    }
    // sync transitions 
    assert(bang.size() == nrChans && que.size() == nrChans); 
    for (uint32_t i = 0; i < nrChans; i++) {
	if (!bang[i].empty() && !que[i].empty()) {
	    for (list_t::iterator b = bang[i].begin(); b != bang[i].end(); ++b) {
		for (list_t::iterator q = que[i].begin(); q != que[i].end(); ++q) {
		    assert(*b && *q);
		    // no self sync
		    if (( (*b)->getProcess()->id != (*q)->getProcess()->id)) {
			trans[(*b)->idInSystem][(*q)->idInSystem] = new Transition(*b, *q, nrTransitions++);
			alltrans.push_back(trans[(*b)->idInSystem][(*q)->idInSystem]);
			assert(nrTransitions == alltrans.size());
		    }
		} 
	    } 
	}
    }
}


// stores the empty vector for edge in the abstract_edges map
// void TransitionBuilder::insert_deleted_edge(Edge* edge, map<int, vector<Edge*> >& abstract_edges) {
//     abstract_edges.insert(make_pair(edge->idInSystem, vector<Edge*>()));    
// }
