// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: ffheur.cpp 810 2011-09-03 15:21:54Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "ff_heuristic/ffheur.h"
#include "ff_heuristic/ffsystem.h"
#include "ff_heuristic/ffarray.h"
#include "ff_heuristic/ffset.h"
#include "ff_heuristic/ffloc.h"
#include "ff_heuristic/ffedge.h"
#include "ff_heuristic/ffvar.h"
#include "ff_heuristic/ffassign.h"

#include "system/state.h"
#include "system/task.h"
#include "system/system.h"

#include <ext/slist>
#include <cassert>
#include <iostream>

namespace ff {

    using namespace std;
    using __gnu_cxx::slist;

    FFHeuristic::FFHeuristic(const Task* task, const Options* opts) : 
	Heuristic(task, opts),
	system(new System(task))
    {
	enabledBangEdges.assign(system->getNrChannels(), PtrArray<Edge>(system->getNrEdges()));
	enabledQueEdges.assign(system->getNrChannels(), PtrArray<Edge>(system->getNrEdges()));
	activeChannels.assign(system->getNrChannels(), false);
	assigns.assign(task->system->getTotalNrIntAssignments());
    }    

    void FFHeuristic::showReachedLocations() {
 	PtrSet<Location>& locs = system->getReachedLocations();
	for (uint32_t i = 0; i < system->getNrLocations(); i++) {
	    if (locs.isMember(system->getLocations()[i])) {
		cout << *system->getLocations()[i] << " ";
	    }
	}
	cout << endl;
    }

    void FFHeuristic::showVariables() {
	for (uint32_t i = 0; i < system->getVariables().size(); i++) {
	    cout << *system->getVariables()[i] << endl;
	}
    }

    void FFHeuristic::showActiveChannels() {
	for (uint32_t i = 0; i < activeChannels.size(); i++) {
	    if (activeChannels[i]) {
		cout << i << " ";
	    }
	}
	cout << endl;
    }

    void FFHeuristic::show() {
	cout << "************* " << system->getLevel() << " ***************" << endl; 
	showReachedLocations();
	showVariables();
    }

    void FFHeuristic::initialiseAbstractSystem(const State* state) {
	system->init(state);
	for (uint32_t i = 0; i < system->getNrChannels(); i++) {
	    enabledBangEdges[i].clear();
	    enabledQueEdges[i].clear();
	}
	activeChannels.assign(system->getNrChannels(), false);	
	assigns.clear();
	init();
    }

    int32_t FFHeuristic::compute(const State* state) {
	initialiseAbstractSystem(state);
	PtrSet<Location>& locs = system->getReachedLocations();

	// waiting edges are edges whoese from location is reached	
	static vector<Edge*> waitingEdges(system->getNrEdges(), NULL);
	waitingEdges.clear();

	copyIntegerVars();
	
	while (!system->goalReached()) {
	    // updates waitingEdges. After the for loop waitingEdges
	    // contains all edges whoese from locations are reached
	    // and have not been applied so far.
	    bool changed = false;
	    updateWaitingEdges(locs, waitingEdges);
	    
	    // applies all enabled edges from waitingEdges and removes
	    // them. An edges is at most applied once.
	    uint32_t insertHere = 0;
	    for (uint32_t i = 0; i < waitingEdges.size(); ++i) {
		Edge* edge = waitingEdges[i];

		// if edge is enabled, remove it from waitingEdges and
		// apply its assignment and add its to location to the
		// set of reached locations. 
		if (edge->isEnabled(system->getLevel())) {
		    switch (edge->getType()) {
		    case Edge::TAU:
			changed |= processEdge(edge);
			break;
		    case Edge::BANG:
			if (activeChannels[edge->getChannelNr()]) {
			    changed |= processEdge(edge);
			} else {
			    enabledBangEdges[edge->getChannelNr()].add(edge);
			}
			break;
		    case Edge::QUE:
			if (activeChannels[edge->getChannelNr()]) {
			    changed |= processEdge(edge);
			} else {
			    enabledQueEdges[edge->getChannelNr()].add(edge);
			}
			break;
		    }
		} else { // edges is not enabled, keep it in waitingEdges
		    waitingEdges[insertHere++] = edge;
		}       	    
	    }
	    waitingEdges.resize(insertHere);
	    
	    // processes the sync edges. The edges stored in
	    // enabledEdges are edges whoese from location is reached
	    // and guard is satisfied.
	    for (uint32_t i = 0; i < system->getNrChannels(); i++) {
		if (isActiveChannel(i)) {
		    activeChannels[i] = true;
		    changed |= processEdges(enabledBangEdges[i]);
		    changed |= processEdges(enabledQueEdges[i]);
		}
	    }

	    changed |= assigns.applyAssignments();
	
	    if (!changed) {
		return INT_MAX;
	    }
	    system->incLevel();
	    copyIntegerVars();
	}
	return system->getLevel();
    }
}
