
#ifndef HISTORY_HH_
#define HISTORY_HH_

#include <vector>
#include <deque>
#include <map>
#include <QMutex>
#include <QColor>

#include <stdint.h>

class HistoryBase : public QObject
{
  Q_OBJECT;

public:
  typedef uint32_t history_date_t;

  struct mem_info_s
  {
    uint8_t _data;
    unsigned _type;
  };

  /** get the state of a memory cell at the given date */
  virtual bool memInfo(mem_info_s &i, uint64_t addr, uint64_t date) const = 0;

  struct reg_info_s
  {
    uint64_t _data;
    std::string _name;
  };

  virtual bool regInfo(reg_info_s &i, unsigned int cpu, unsigned int reg, uint64_t date) const = 0;

  struct event_info_s
  {
    uint64_t _data;
    unsigned _type;
    uint64_t _addr;
    unsigned _be;
    uint64_t _size;
    unsigned _cpu_id;
    uint64_t _cycle;
    uint64_t _pc;
    history_date_t _date;
    history_date_t _prev;
  };

  /** get number of cpus  */

  /** get the most recent event on a memory cell at the given date. */
  virtual bool memEventInfo(event_info_s &i, uint64_t addr, uint64_t date) const = 0;

  /** get the most recent event on a register at the given date. */
  virtual bool regEventInfo(event_info_s &i, unsigned cpu, unsigned reg, uint64_t date) const = 0;

  /** get event at the given date. */
  virtual bool eventInfo(event_info_s &i, uint64_t date) const = 0;

  /** get events count */
  virtual history_date_t eventCount() const = 0;

  /** get date of last event */
  virtual history_date_t eventLast() const = 0;

  virtual void init() = 0;

  virtual void cpuReset(unsigned int cpu, unsigned regs_count, 
                        unsigned pc_reg, unsigned sp_reg, const char reg_names[][8]) = 0;

  static const char * EventTypeName(unsigned e);

  virtual unsigned cpuCount() const = 0;

  virtual unsigned regCount(unsigned int cpu) const = 0;

  virtual QString regName(unsigned int cpu, unsigned int reg) const = 0;

signals:
  void initEvent();

  void resetEvent(unsigned cpu);

  /* A new event is available. The signal is emited for last
     duplicated event only. Address and size are only valid for memory related events. */
  void newEvent(unsigned int date, unsigned int type, qulonglong addr, qulonglong size);
};

template <typename Addr, typename Data>
class History : public HistoryBase
{
public:

  History();

  void init();
  void cpuReset(unsigned int cpu, unsigned regs_count,
                unsigned pc_reg, unsigned sp_reg, const char reg_names[][8]);

  void setData(Addr addr, uint8_t *data, size_t size);
  void memAccess(unsigned type, Addr addr, Data data, unsigned be, unsigned cpu, uint64_t cycles);
  void regWrite(unsigned id, Data data, unsigned cpu, uint64_t cycles);
  void memState(unsigned type, Addr start, Addr size, unsigned cpu, uint64_t cycles);
  QString regName(unsigned int cpu, unsigned int reg) const;

private:

  typedef std::vector<uint8_t> region_data_t;
  typedef std::vector<uint16_t> region_state_t;
  typedef std::vector<history_date_t> region_hist_t;

  struct region_s
  {
    region_s(size_t s)
      : _data(s),
	_type(s),
	_hist(s)
    {
    }

    /* information is stored for all memory cells of the region */
    region_data_t _data;    //< cell data before oldest history entry
    region_state_t _type;   //< cell state before oldest history entry
    region_hist_t _hist;    //< last history index for each cell
  };

  typedef std::map<Addr, region_s *> region_map_t;

  struct reg_bank_s
  {
    std::vector<std::string> _names;     //< register names
    region_data_t _data;    //< register data before oldest history entry
    region_hist_t _hist;    //< last history index for each reg
    uint64_t _last_cycle;
    unsigned _pc_reg;
    unsigned _sp_reg;
    Data _last_pc;
  };

  typedef std::map<Addr, reg_bank_s *> regs_map_t;

  struct history_entry_s
  {
    union {
      uint64_t _timestamp;          //< timestamp event

      struct {
        union {
          Addr _addr;               //< mem access ops & state ops event
          uint32_t _cycles_diff;    //< regs ops event
        } __attribute__((packed));

        union {
          Data _data;               //< mem access ops & regs ops event
          Addr _size;               //< state ops event
        } __attribute__((packed));
      } __attribute__((packed));

    } __attribute__((packed));

    history_date_t _prev;       //< link to previous history entry for the same cell/reg
    uint16_t _type;

    union {
      uint8_t _be;              //< mem access event
      uint8_t _reg_id;          //< regs ops event
    } __attribute__((packed));

    uint8_t  _cpu_id:4;
    uint8_t  _pc_inc:4;         //< pc increment after this event
  } __attribute__((packed));

  bool regInfo(reg_info_s &i, unsigned int cpu, unsigned int reg, uint64_t date) const;
  bool memEventInfo(event_info_s &i, uint64_t addr, uint64_t date) const;
  bool regEventInfo(event_info_s &i, unsigned cpu, unsigned reg, uint64_t date) const;
  bool eventInfo(event_info_s &i, uint64_t date) const;
  history_date_t eventCount() const;
  history_date_t eventLast() const;
  bool memInfo(mem_info_s &i, uint64_t addr, uint64_t date) const;
  unsigned cpuCount() const;
  unsigned regCount(unsigned int cpu) const;

  static void mergeType(unsigned &type, history_entry_s &e);

  void memEvent(Addr raddr, Addr start, Addr size,
		region_s *r, history_entry_s &e);

  void dropHist();

  history_date_t _hist_first;   //< date of oldest history entry. 0 is reserved
  history_date_t _hist_size;    //< max number of history entries

  std::deque<history_entry_s> _hist;   //< all history

  region_map_t _mem_map;               //< memory regions map, key is start addr
  regs_map_t _regs_map;                //< cpu regs map, key is cpu id

  mutable QMutex _mutex;
};

#endif
