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

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

#include "common/hash.h"
#include "common/helper.h"

#include <dbm/ClockAccessor.h>

#include <cassert>
#include <cstdlib>
#include <iostream>

using namespace std;

uint32_t State::nrClocks = 0;
uint32_t DiscretePart::nrProcs = 0;
uint32_t DiscretePart::nrInts = 0;
uint32_t DiscretePart::data_size = 0;
uint32_t DiscretePart::extra_size = 0;
StateBuilder* State::builder = NULL;

static inline uint32_t getBit(const uint32_t* bits, uint32_t index) {
    assert(bits);
    return (bits[index >> 5] >> (index & 31)) & 1;
}

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

DiscretePart::extra_t* DiscretePart::allocate_extra_mem() {
    if (extra_size == 0) {
	return NULL;
    } else {
	return new data_t[extra_size];
    }
}

DiscretePart::DiscretePart() : 
    data(new data_t[data_size]),
    extra_data(allocate_extra_mem())
{
    init();
}

DiscretePart::~DiscretePart() {
    delete [] extra_data;
    delete [] data;
}

DiscretePart* DiscretePart::init() {
    std::fill(data, data + data_size, 0);
    std::fill(extra_data, extra_data + extra_size, 0);
    return this;
}

DiscretePart::DiscretePart(const DiscretePart& dp) : 
    data(new data_t[data_size]),
    extra_data(allocate_extra_mem())
{
    init(dp);
}

DiscretePart* DiscretePart::init(const DiscretePart& dp) {
    std::copy(dp.data, dp.data + data_size, data);
    std::copy(dp.extra_data, dp.extra_data + extra_size, extra_data);
    return this;
}

bool DiscretePart::operator==(const DiscretePart& dp) const {
    return std::equal(data, data + data_size, dp.data);
}

bool DiscretePart::operator!=(const DiscretePart& dp) const {
    return !(*this == dp);
}

void DiscretePart::share(const DiscretePart& dp) {
    assert(dp.data && data);
    assert(*this == dp);
    
    if (dp.data != data) {
    	delete [] data;
    	data = dp.data;
    }
}

uint32_t DiscretePart::hashKey() const {
    return sax_hash(data, data + data_size);
}

std::ostream& DiscretePart::display(std::ostream& o) const {
    printContainer(data, data + data_size, " ", o);    
    return printContainer(extra_data, extra_data + extra_size, " ", o);    
}

std::ostream& DiscretePart::prettyPrint(std::ostream& o, const System* system) const {
    for (uint32_t i = 0; i < system->procs.size(); i++) {
	const Process* p = system->procs[i];
 	o << p->name << "." << p->locs[proc(i)]->name << " ";
    }
    o << endl;
    uint32_t pos = 0;
    for (uint32_t i = 0; i < system->ints.size(); i++) {
	o << system->ints[i]->name << "=" << var(pos++) << " ";
    }
    
    for (uint32_t j = 0; j < system->procs.size(); j++) {
	const Process* proc = system->procs[j];
	for (uint32_t l = 0; l < proc->ints.size(); l++) {
	    o << proc->name << "." << proc->ints[l]->name << "=" << var(pos++) << " ";
	}
    }
    return o;
}

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

StateBuilder::StateBuilder(const System* system) : 
    system(system),
    nrClocks(system->getTotalNrClocks()),
    nrProcs(system->procs.size()),
    nrInts(system->getTotalNrIntegers()),
    cap(0)
{
    DiscretePart::nrInts = nrInts;
    DiscretePart::nrProcs = nrProcs;
    assert(DiscretePart::extra_size == 0);
    DiscretePart::data_size = nrInts + nrProcs;
    State::nrClocks = nrClocks;
    State::builder = this;
}

void StateBuilder::addField(const std::string& identifier) {
    if (lookup.insert(make_pair(identifier, DiscretePart::extra_size)).second) { 
	// we only inc the variable if identifier is not in the
	// map
	DiscretePart::extra_size++;
    }
}

int32_t StateBuilder::getField(const std::string& identifier) const {
    map_t::const_iterator it = lookup.find(identifier);
    if (it != lookup.end()) {
	return it->second;
    } else {
	return NOT_DEFINED;
    }
}

void StateBuilder::destroy(State* state) {
    if (cap < capacity) {
     	cache[cap] = state;
	assert(cache[cap]);
	cap++;
    } else {
	delete state;
    }
}

State* StateBuilder::newState() {
    if (cap > 0) {
	assert(cache[cap-1]);
	return cache[--cap]->init();
    } else {
	return new State;
    }
}

State* StateBuilder::newState(const State& state) {
    if (cap > 0) {
	assert(cache[cap-1]);
     	return cache[--cap]->init(state);
    } else {
	return new State(state);
    }
}

State* StateBuilder::newState(istream& in) {
    State* res = newState();
    res->dbm.setInit();
    for (uint32_t i = 0; i < nrProcs; i++) {
	in >> res->proc(i);
    }
    for (uint32_t i = 0; i < nrInts; i++) {
	in >> res->var(i);
    }
    int32_t counter = 0;
    char c;
    while (in >> c) {
	in.unget();
	if (c == ':') {
	    break;
	} 
	
	uint32_t i, j;
	raw_t bound;
	in >> i >> j >> bound;
	res->dbm.constrain(i, j, bound);
	counter++;
	assert(counter <=nrClocks*nrClocks);
    }
    return res;
}

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

State::State() :
    dp(),
    dbm(nrClocks),
    reachedby(NULL),
    predecessor(NULL)
{
    dbm.setZero();
}

State* State::init() {
    // same functionality as default c'tor
    reachedby = NULL;
    predecessor = NULL;
    dp.init();
    dbm.setZero();
    return this;
}
    
State::State(const State& state) : 
    dp(state.dp),
    dbm(state.dbm),
    reachedby(state.reachedby),
    predecessor(&state)
{
}

State* State::init(const State& state) {
    // same functionality as copy c'tor
    reachedby = state.reachedby;
    predecessor = &state;
    dp.init(state.dp);
    dbm = state.dbm;
    return this;
}
    
void State::destroy(State* state) {
    builder->destroy(state);
}

State* State::clone() const {
    return builder->newState(*this);
}

uint32_t State::hashKey() const {
    return dp.hashKey();
}    
    
bool State::discEqual(const State* state) const {
    return dp == state->dp;
}

bool State::operator==(const State& state) const {
    return dp == state.dp && dbm == state.dbm;
}

bool State::operator!=(const State& state) const {
    return !(*this == state);
}

void State::share(DiscretePart& dp) {
    this->dp.share(dp);
}

void State::share(const State* state) {
    dp.share(state->dp);
}

ostream& State::prettyPrint(ostream& o, const System* system) const {
    dp.prettyPrint(o, system);
    return o << endl << zone().toString(MyClockAccessor(system));
}

ostream& State::display(ostream& o) const {
    return o << "(" << dp << ")"; 
}

void State::serialize(FILE* file) const {
    for (uint32_t i = 0; i < DiscretePart::data_size; i++) {
	fprintf(file, " %d", dp.data[i]);
    }
    // fprintf(file, ". ");    
    uint32_t* keep = new uint32_t[bits2intsize(nrClocks * nrClocks)];
    dbm.analyzeForMinDBM(keep);
    
    for (uint32_t i = 0; i < nrClocks; i++) {
	for (uint32_t j = 0; j < nrClocks; j++) {
	    if (getBit(keep, i * nrClocks + j)) {
		fprintf(file, " %d %d %d", i, j, dbm(i, j));
	    }
	}
    }
    delete [] keep;
    //fprintf(file, ".");
}

ostream& State::serialize(ostream& o) const {
    printContainer(dp.data, dp.data + DiscretePart::data_size, " ", o);
    o << ". ";
    
    uint32_t* keep = new uint32_t[bits2intsize(nrClocks * nrClocks)];
    dbm.analyzeForMinDBM(keep);
    
    for (uint32_t i = 0; i < nrClocks; i++) {
	for (uint32_t j = 0; j < nrClocks; j++) {
	    if (getBit(keep, i * nrClocks + j)) {
		o << i << ' ' 
		  << j << ' '
		  << dbm(i, j) 
		  << ". ";
	    }
	}
    }
    delete [] keep;
    return o << ".";
}

