// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: system_abstractor.cpp 817 2011-09-10 13:31:09Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "system_abstractor.h"

#include "search/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/task.h"
#include "system/variable.h"

#include <set>

using namespace std;

SystemAbstractor::SystemAbstractor(const Task* task) : 
    task(task), 
    nrEdges(task->system->getTotalNrEdges()),
    abstractedEdges(nrEdges, NULL)
{}

void SystemAbstractor::abstractIntegers(const Edge* edge, vector<bool>& abstracted) const {
    // collect all written vars
    set<int32_t> writeVars;
    const vector<IntAssignment*>& assigns = edge->effect->intassigns;
    for (uint32_t i = 0; i < assigns.size(); i++) {
	writeVars.insert(assigns[i]->lhs->id);
    }
    // abstract all edges that read these vars
    for (uint32_t i = 0; i < task->system->procs.size(); i++) {
	const Process* proc = task->system->procs[i];
	for (uint32_t j = 0; j < proc->edges.size(); j++) {
	    const Edge* e = proc->edges[j];		
	    const Conjunction<IntConstraint>& cc = e->guard->intconstraints;
	    for (uint32_t k = 0; k < cc.size(); k++) {
		if (writeVars.find(cc[k]->lhs->id) != writeVars.end()) {
		    abstracted[e->idInSystem] = true;
		    break;
		}
	    }
	}
    }	
}

void SystemAbstractor::abstractClocks(const Edge* edge, vector<bool>& abstracted) const {
    // collect all reset clocks
    set<int32_t> resetClocks;
    const vector<ClockReset*>& resets = edge->effect->resets;
    for (uint32_t i = 0; i < resets.size(); i++) {
	resetClocks.insert(resets[i]->lhs->id);
    }
    // abstract all edges that read these vars
    for (uint32_t i = 0; i < task->system->procs.size(); i++) {
	const Process* proc = task->system->procs[i];
	for (uint32_t j = 0; j < proc->edges.size(); j++) {
	    const Edge* e = proc->edges[j];		
	    const Conjunction<ClockConstraint>& rr = e->guard->clockconstraints;
	    for (uint32_t k = 0; k < rr.size(); k++) {
		if (resetClocks.find(rr[k]->lhs->id) != resetClocks.end()) {
		    abstracted[e->idInSystem] = true;
		    break;
		}
	    }
	}
    }
}

void SystemAbstractor::abstractEdges(const Edge* edge, vector<bool>& abstracted) const {
    abstracted[edge->idInSystem] = true;	
    const Process* proc = edge->getProcess();
    for (uint32_t i = 0; i < proc->edges.size(); i++) {
	// abstract edges with same dst location
	const Edge* e = proc->edges[i];
	if (e->dst->idInSystem == edge->dst->idInSystem) {
	    abstracted[e->idInSystem] = true;
	}
    }
}

const vector<bool>* SystemAbstractor::abstractSystem(const Edge* edge) { 
    vector<bool>* abstracted = abstractedEdges[edge->idInSystem];
    if (abstracted == NULL) {
	abstracted = new vector<bool>(nrEdges, false);	

	abstractEdges(edge, *abstracted);
	abstractIntegers(edge, *abstracted); 
	abstractClocks(edge, *abstracted);

	abstractedEdges[edge->idInSystem] = abstracted;
    }
    return abstracted;
}

const vector<bool>* SystemAbstractor::abstractSystem(const Transition* trans) {
    const vector<bool>* fst_edge = abstractSystem(trans->edge1);
    if (trans->edge2) {
	vector<bool>* result = new vector<bool>(nrEdges, false);
	const vector<bool>* snd_edge = abstractSystem(trans->edge2);
	for (uint32_t i = 0; i < nrEdges; i++) {
	    (*result)[i] = (*fst_edge)[i] || (*snd_edge)[i];
	}
	return result;
    }
    return fst_edge;
}
