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

#include "normalizer.h"

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

#include <iomanip>

using namespace std;

static inline bool isReset(uint32_t clockID, const Effect* effect) {
    for (uint32_t i = 0; i < effect->resets.size(); i++) {
        if (effect->resets[i]->lhs->id == clockID) {
            return true;
        }
    }
    return false;
}

void Normalizer::init() {

    for (uint32_t p = 0; p < system->procs.size(); p++) {
        const Process* proc = system->procs[p];

        // iterate over all transitions ...
        for (uint32_t i = 0; i < proc->edges.size(); i++) {
            const Edge* edge = proc->edges[i];
            const Conjunction<ClockConstraint>& ccs = edge->guard->clockconstraints;
            for (uint32_t j = 0; j < ccs.size(); j++) {
                process(proc, edge->src, ccs[j]);
            }
        }

        // ... and all location invs
        for (uint32_t i = 0; i < proc->locs.size(); i++) {
            const Location* loc = proc->locs[i];
            const Conjunction<ClockConstraint>& ccs = loc->inv->clockconstraints;
            for (uint32_t j = 0; j < ccs.size(); j++) {
                process(proc, loc, ccs[j]);
            }
        }
    }
}

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

LocalLUNormalizer::LocalLUNormalizer(const System* system) :
    Normalizer(system),
    nrClocks(system->getTotalNrClocks()),
    nrProcs(system->procs.size())
{
    reserveMemory(lower);
    reserveMemory(upper);
    init();

    for (uint32_t i = 0; i < system->procs.size(); i++) {
        close(lower, system->procs[i]);
        close(upper, system->procs[i]);
    }
}

void LocalLUNormalizer::reserveMemory(array_t& array) const {
    array.assign(system->procs.size(), vector<vector<int32_t> >());
    for (uint32_t p = 0; p < nrProcs; p++) {
        const Process* proc = system->procs[p];
        array[p].assign(proc->locs.size(), vector<int32_t>() );
        for (uint32_t l = 0; l < proc->locs.size(); l++) {
            array[p][l].assign(nrClocks, -dbm_INFINITY);
            array[p][l][0] = 0;
        }
    }
}

void LocalLUNormalizer::close(array_t& bounds, const Process* proc) {
    bool stable = false;
    while (!stable) {
        stable = true;
        for (uint32_t i = 0; i < proc->edges.size(); i++) {
            const Edge* edge = proc->edges[i];
            int32_t source = edge->src->idInProcess;
            int32_t target = edge->dst->idInProcess;

            vector<int32_t> &sourceBnd = bounds[proc->id][source];
            const vector<int32_t> &targetBnd = bounds[proc->id][target];

            for (uint32_t j = 0; j < nrClocks; j++) {
                if (!isReset(j, edge->effect)) {
                    if (targetBnd[j] > sourceBnd[j]) {
                        sourceBnd[j] = targetBnd[j];
                        stable = false;
                    }
                }
            }
        }
    }
}

void LocalLUNormalizer::process(const Process* proc, const Location* loc, const ClockConstraint* cc) {
    switch (cc->comp) {
    case Constraint::LT:
    case Constraint::LE:
        upper[proc->id][loc->idInProcess][cc->lhs->id] = std::max(upper[proc->id][loc->idInProcess][cc->lhs->id], cc->rhs);
        break;
    case Constraint::EQ:
        lower[proc->id][loc->idInProcess][cc->lhs->id] = std::max(lower[proc->id][loc->idInProcess][cc->lhs->id], cc->rhs);
        upper[proc->id][loc->idInProcess][cc->lhs->id] = std::max(upper[proc->id][loc->idInProcess][cc->lhs->id], cc->rhs);
        break;
    case Constraint::GE:
    case Constraint::GT:
        lower[proc->id][loc->idInProcess][cc->lhs->id] = std::max(lower[proc->id][loc->idInProcess][cc->lhs->id], cc->rhs);
        break;
    default:
        assert(false);
    }
}

void LocalLUNormalizer::prepareNormalization(vector<int32_t>& ceils, const array_t& bounds, const State* next) const {
    ceils.assign(nrClocks, -dbm_INFINITY);
    ceils[0] = 0;

    for (uint32_t i = 0; i < nrProcs; i++) {
        int32_t location = next->proc(i);
        for (uint32_t j = 1; j < nrClocks; j++) {
            ceils[j] = std::max(ceils[j], bounds[i][location][j]);
        }
    }

}

void LocalLUNormalizer::normalize(State* next) const {
    static vector<int32_t> floors;
    static vector<int32_t> ceils;

    prepareNormalization(floors, lower, next);
    prepareNormalization(ceils, upper, next);

    next->zone().extrapolateLUBounds(&floors[0], &ceils[0]);
}
