// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: gd.cpp 814 2011-09-10 12:44:37Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "gd_heuristic/gd.h"

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

#include "common/message.h"

using namespace std;

GDHeuristic::GDHeuristic(const Task* task, const Options* opts) :
    Heuristic(task, opts),
    matrices()
{
    vector<bool> noAbstraction(task->system->getTotalNrEdges(), false);
    init(noAbstraction);
}

void GDHeuristic::init(const vector<bool>& abstracted) {
    const Conjunction<LocationConstraint>& locs = task->target->getLocationConstraints();
    static bool first = true; // to prevent multiple output
    if (first && locs.empty()) {
	first = false;
	warning() << "The query contains no goal locations";
    }
    matrices.clear();
    for (uint32_t i = 0; i < locs.size(); ++i) {
	const Location* loc = locs[i]->loc;
	const Process* proc = loc->proc;
	uint32_t nrStates = proc->locs.size();
	uint32_t nrEdges = proc->edges.size();
	
	matrices.push_back(Matrix(nrStates));	    
	Matrix& matrix = matrices.back();
	
	for (uint32_t j = 0; j < nrEdges; ++j) {
	    const Edge* edge = proc->edges[j];
	    if (abstracted[edge->idInSystem]) {
		continue;
	    }
	    uint32_t fromId = edge->src->idInProcess;
	    uint32_t toId = edge->dst->idInProcess;
	    
	    // as the from location and the to location can be equal
	    // (self loop) we have to pay attention. Entries in the
	    // diagonal of the matrix must always be 0.
	    if (fromId != toId) {
		matrix.at(fromId, toId) = 1;
	    }
	}
	matrix.floyd_warshall();	    
    }
}

int32_t GDHeuristic::compute(const State* state) {
    int32_t result = 0;

    const Conjunction<LocationConstraint>& tlocs = task->target->getLocationConstraints();
    for (uint32_t i = 0; i < tlocs.size(); ++i) {	    
	uint32_t procId = tlocs[i]->loc->proc->id;
	uint32_t currentLocId = state->proc(procId);
	uint32_t targetId = tlocs[i]->loc->idInProcess;

	int32_t update_value = matrices[i].at(currentLocId, targetId);
	if (update_value == INT_MAX) {
	    return INT_MAX;
	}
	
	update(result, update_value);
    }
    return result;
}
