// -*- mode:c++; c-style:k&r; c-basic-offset:4; indent-tabs-mode: nil; -*-
// vi:set et cin sw=4 cino=>se0n0f0{0}0^0\:0=sl1g0hspst0+sc3C0/0(0u0U0w0m0:

#ifndef __VCD_HPP__
#define __VCD_HPP__

#include <string>
#include <vector>
#include <set>
#include <map>
#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/thread.hpp>
#include "cstdint.hpp"
#include "error.hpp"

using namespace std;
//using namespace boost;

typedef const void *vcd_id_t;

class vcd_node {
public:
    vcd_node() throw();
public:
    typedef map<string, vcd_node> nodes_t;
public:
    vcd_id_t id; // invalid iff 0
    nodes_t children; // empty() <==> vcd_id == 0
};

class vcd_writer {
public:
    vcd_writer(const uint64_t &time, const boost::shared_ptr<ofstream> out,
               uint64_t start, uint64_t end) throw(err);
    void new_signal(const vcd_id_t &id, const vector<string> &path,
                    unsigned width) throw(err);
    void add_value(vcd_id_t id, uint64_t val) throw(err);
    void commit() throw(err); // call in the beginning of every tick
                              // to flush posedge values to VCD
    void finalize() throw(err); // call once at end to flush VCD
    void tick() throw();
    uint64_t get_time() const throw();
    void fast_forward_time(uint64_t new_time) throw();
    bool is_drained() const throw();
private:
    typedef map<vcd_id_t, string> id_map_t;
    typedef map<vcd_id_t, unsigned> widths_t;
    typedef map<vcd_id_t, uint64_t> cur_values_t;
    typedef map<vcd_id_t, uint64_t> last_values_t;
    typedef map<vcd_id_t, boost::tuple<streampos, streampos> > stream_locs_t;
private:
    string get_fresh_id() throw();
    void check_header() throw(err);
    void check_timestamp() throw(err);
    void write_val(const string &id, uint64_t val) throw(err);
    void writeln_val(const string &id, uint64_t val) throw(err);
    void declare_vars(const vcd_node::nodes_t &nodes,
                      const map<vcd_id_t, string> &ids) throw(err);
    bool is_sleeping() const throw();
private:
    uint64_t time;
    const uint64_t start_time;
    const uint64_t end_time;
    uint64_t last_timestamp;
    id_map_t ids;
    widths_t widths;
    cur_values_t cur_values;
    last_values_t last_values;
    set<vcd_id_t> dirty;
    string last_id;
    vcd_node::nodes_t paths;
    stream_locs_t def_locs;
    stream_locs_t init_locs;
    bool initialized;
    bool finalized;
    boost::shared_ptr<ofstream> out;
    mutable boost::recursive_mutex vcd_mutex;
};

#endif // __VCD_HPP__
