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

#ifndef SYSTEM_STATE_H
#define SYSTEM_STATE_H

#include "system/zone.h"

#include <cassert>
#include <iosfwd>
#include <map>

class DiscretePart;
class State;
class System;
class Target;
class Transition;

class DiscretePart {
private:
    static uint32_t data_size; // == nrInts + nrProcs
    static uint32_t extra_size; 
    static uint32_t nrInts;
    static uint32_t nrProcs;
public:
    typedef int32_t data_t;
    typedef int32_t extra_t;
    data_t* data;
    extra_t* extra_data; 
private:

    DiscretePart& operator=(const DiscretePart&);

    extra_t* allocate_extra_mem();
public:  
    DiscretePart();
    DiscretePart(const DiscretePart& dp);    
    ~DiscretePart();
    
    DiscretePart* init();
    DiscretePart* init(const DiscretePart& dp);        
    void share(const DiscretePart& dp);
    uint32_t hashKey() const;

    bool operator==(const DiscretePart& dp) const;
    bool operator!=(const DiscretePart& dp) const;
    extra_t extra(uint32_t index) const {
	return extra_data[index];
    }
    
    extra_t& extra(uint32_t index) {
	return extra_data[index];
    }
    
    data_t& proc(uint32_t procID) {
	assert(procID < nrProcs);
	return data[procID];
    }

    data_t proc(uint32_t procID) const {
	assert(procID < nrProcs);
	return data[procID];
    }

    data_t& var(uint32_t varID) {
	assert(varID < nrInts);
	return data[nrProcs + varID];
    }

    data_t var(uint32_t varID) const {
	assert(varID < nrInts);
	return data[nrProcs + varID];
    }
    
    std::ostream& display(std::ostream& o) const;
    std::ostream& prettyPrint(std::ostream& o, const System* system) const;   
    friend class StateBuilder;
    friend class State;
};

inline std::ostream& operator<<(std::ostream& o, const DiscretePart& dp) {
    return dp.display(o);
}

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

class StateBuilder {
private:    
    const System* system;
    uint32_t nrClocks;    
    uint32_t nrProcs;
    uint32_t nrInts;
    
    static const int32_t capacity = 100000;
    State* cache[capacity];
    int32_t cap;

    typedef std::map<std::string, int32_t> map_t; // maps a name to an index
    map_t lookup;    

    StateBuilder(const System* system);
    StateBuilder& operator=(const StateBuilder&);
public:    
    static const int32_t NOT_DEFINED = -1;
    
    void destroy(State* state);
    
    State* newState();
    State* newState(const State& state);
    State* newState(std::istream& i);

    void addField(const std::string& identifier);
    int32_t getField(const std::string& identifier) const;

    friend std::ostream& operator<<(std::ostream& o, const State& s);  
    friend class System;
};

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

class State {    
private:
    static StateBuilder* builder;
    static uint32_t nrClocks;
public:
    // TODO: replace this with a plain field. Removable information is
    // no longer needed.
    DiscretePart dp;

private:
    dbm::dbm_t dbm;

    // not implemented
    State& operator=(const State& state);
    ~State() {}

    State();
    State(const State& state);
    
    State* init();      
    State* init(const State& state);
    
public:    
    const Transition* reachedby;
    const State* predecessor;
    
    static void destroy(State* state);
    
    DiscretePart::extra_t extra(int32_t index) const { return dp.extra(index); }
    DiscretePart::extra_t& extra(int32_t index) { return dp.extra(index); }

    DiscretePart::data_t proc(int32_t index) const { return dp.proc(index); }
    DiscretePart::data_t& proc(int32_t index) { return dp.proc(index); }

    DiscretePart::data_t var(int32_t index) const { return dp.var(index); }
    DiscretePart::data_t& var(int32_t index) { return dp.var(index); }

    dbm::dbm_t& zone() { return dbm; }    
    const dbm::dbm_t& zone() const { return dbm; }
    
    State* clone() const;    
    uint32_t hashKey() const;    
    bool discEqual(const State* state) const;    
    bool operator==(const State& state) const;
    bool operator!=(const State& state) const;
    void share(DiscretePart& dp);
    void share(const State* state);
    
    std::ostream& prettyPrint(std::ostream& o, const System* system) const;
    std::ostream& display(std::ostream& o) const;
    std::ostream& serialize(std::ostream& o) const;
    void serialize(FILE* file) const;

    friend std::ostream& operator<<(std::ostream& o, const State& s); 
    friend class StateBuilder;
};

inline std::ostream& operator<<(std::ostream& o, const State& s) {
    return s.prettyPrint(o, State::builder->system);
}

#endif /* SYSTEM_STATE_H */
