// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: constraint.cpp 816 2011-09-10 13:19:07Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "constraint.h"
#include "location.h"
#include "process.h"
#include "state.h"
#include "variable.h"

#include "common/message.h"

#include <cassert>
#include <ostream>

using namespace std;

ostream& operator<<(ostream& o, Constraint::comp_t comp) {
    switch (comp) {
    case Constraint::LT: return o << "<";
    case Constraint::LE: return o << "<=";
    case Constraint::EQ: return o << "==";
    case Constraint::GE: return o << ">=";
    case Constraint::GT: return o << ">";
    case Constraint::NEQ: return o << "!=";
    default:
	assert(false);
	return o;
    }
}

Constraint::Constraint(comp_t comp, Expression::type_t type, int32_t id) : 
    Expression(type),
    comp(comp),
    id(id)
{}

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

ClockConstraint::ClockConstraint(Clock* lhs, comp_t comp, int32_t rhs, int32_t id) :
    Constraint(comp, Expression::CONSTRAINT, id),
    lhs(lhs),
    rhs(rhs)
{
    assert(lhs);
    if (comp == NEQ) {
	error() << "!= not supported in clock constraints" << endl;
    }
}

ClockConstraint::ClockConstraint(Clock* lhs, comp_t comp, const Constant* rhs, int32_t id) :
    Constraint(comp, Expression::CONSTRAINT, id),
    lhs(lhs),
    rhs(rhs->value)
{
    assert(lhs && rhs);
}

varset_t ClockConstraint::readVars() const { 
    return lhs; 
}

bool ClockConstraint::operator==(const ClockConstraint& cc) const {
    return lhs->id == cc.lhs->id && comp == cc.comp && rhs == cc.rhs;
}

bool ClockConstraint::isSatBy(const State* state) const {
    switch (comp) {
    case Constraint::LT: return state->zone().satisfies(lhs->id, 0, dbm_bound2raw(rhs, dbm_STRICT)); 
    case Constraint::LE: return state->zone().satisfies(lhs->id, 0, dbm_bound2raw(rhs, dbm_WEAK)); 
    case Constraint::EQ: return state->zone().satisfies(lhs->id, 0, dbm_bound2raw(rhs, dbm_WEAK)) && 
	    state->zone().satisfies(0, lhs->id, dbm_bound2raw(-rhs, dbm_WEAK));
    case Constraint::GE: return state->zone().satisfies(0, lhs->id, dbm_bound2raw(-rhs, dbm_WEAK)); 
    case Constraint::GT: return state->zone().satisfies(0, lhs->id, dbm_bound2raw(-rhs, dbm_STRICT));  
    case Constraint::NEQ: 
    default:
	assert(false);
	return false;
    }
}

bool ClockConstraint::constrain(State* state) const {
    switch (comp) {
    case Constraint::LT: return state->zone().constrain(lhs->id, 0, dbm_bound2raw(rhs, dbm_STRICT)); 
    case Constraint::LE: return state->zone().constrain(lhs->id, 0, dbm_bound2raw(rhs, dbm_WEAK)); 
    case Constraint::EQ: return state->zone().constrain(lhs->id, 0, dbm_bound2raw(rhs, dbm_WEAK)) && 
	    state->zone().constrain(0, lhs->id, dbm_bound2raw(-rhs, dbm_WEAK));
    case Constraint::GE: return state->zone().constrain(0, lhs->id, dbm_bound2raw(-rhs, dbm_WEAK)); 
    case Constraint::GT: return state->zone().constrain(0, lhs->id, dbm_bound2raw(-rhs, dbm_STRICT));  
    case Constraint::NEQ: 
    default:
	assert(false);
	return false;
    }
}

ostream& ClockConstraint::display(ostream& o) const {
    return o << lhs->name << " " << comp << " " << rhs; 
}

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

IntConstraint::IntConstraint(Integer* lhs, comp_t comp, int32_t rhs, int32_t id) :
    Constraint(comp, Expression::CONSTRAINT, id),
    lhs(lhs),
    rhs(rhs)
{
    assert(lhs);
}

IntConstraint::IntConstraint(Integer* lhs, comp_t comp, const Constant* rhs, int32_t id) :
    Constraint(comp, Expression::CONSTRAINT, id),
    lhs(lhs),
    rhs(rhs->value)
{
    assert(lhs && rhs);
}

varset_t IntConstraint::readVars() const { 
    return lhs;
}

bool IntConstraint::operator==(const IntConstraint& ic) const {
    return lhs->id == ic.lhs->id && comp == ic.comp && rhs == ic.rhs;
}

bool IntConstraint::isSatBy(const State* state) const {
    const int32_t value = state->var(lhs->id);

    switch (comp) {
    case Constraint::LT:  return value <  rhs; 
    case Constraint::LE:  return value <= rhs; 
    case Constraint::EQ:  return value == rhs; 
    case Constraint::GE:  return value >= rhs; 
    case Constraint::GT:  return value >  rhs; 
    case Constraint::NEQ: return value != rhs; 
    default:
	assert(false);
	return false;
    }
}

ostream& IntConstraint::display(ostream& o) const {
    return o << lhs->name << " " << comp << " " << rhs; 
}

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

LocationConstraint::LocationConstraint(Location* loc, int32_t id) : 
    Constraint(EQ, Expression::CONSTRAINT, id),
    loc(loc)
{}

bool LocationConstraint::operator==(const LocationConstraint& lc) const {
    return loc->idInSystem == lc.loc->idInSystem;
}

varset_t LocationConstraint::readVars() const {
    return loc;    
}

bool LocationConstraint::isSatBy(const State* state) const {
    return state->proc(loc->proc->id) == loc->idInProcess;
}

ostream& LocationConstraint::display(ostream& o) const {
    return o << loc->proc->name << "." << loc->name;
}

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

ClockConstraint* ConstraintFactory::create(Clock* lhs, Constraint::comp_t comp, int32_t rhs) {
    return clocks.insert(new ClockConstraint(lhs, comp, rhs, clocks.size())).first;
}

ClockConstraint* ConstraintFactory::create(Clock* lhs, Constraint::comp_t comp, const Constant* rhs) {
    return create(lhs, comp, rhs->value);
}
IntConstraint* ConstraintFactory::create(Integer* lhs, Constraint::comp_t comp, int32_t rhs) {
    return ints.insert(new IntConstraint(lhs, comp, rhs, ints.size())).first;
}

IntConstraint* ConstraintFactory::create(Integer* lhs, Constraint::comp_t comp, const Constant* rhs) {
    return create(lhs, comp, rhs->value);
}

LocationConstraint* ConstraintFactory::create(Location* loc) {
    return locs.insert(new LocationConstraint(loc, locs.size())).first;
}
