
#include "mem_model.hh"
#include "reg_model.hh"
#include "event_model.hh"
#include "mem_view.hh"
#include "ui_mem_view.h"
#include "memchecker_events.h"

#include <limits.h>

void MemView::showAllEvents()
{
  eventsView->setModel(0);
  delete _emodel;
  _emodel = new EventModelAll(_hist, this);
  eventsView->setModel(_emodel);
  label_2->setText("All events");
  showallButton->setEnabled(false);

  QModelIndex ndx = _emodel->indexFromDate(eventSpinBox->value());
  if (ndx.isValid())
    eventsView->scrollTo(ndx, QAbstractItemView::PositionAtTop);
}

void MemView::showMemEvents(const QModelIndex &index)
{
  uint64_t addr = _mmodel->getAddress(index);

  eventsView->setModel(0);
  delete _emodel;
  _emodel = new EventModelMem(_hist, addr, this);
  eventsView->setModel(_emodel);

  QString s;
  s.sprintf("Events for address %08llx", addr);
  label_2->setText(s);
  showallButton->setEnabled(true);
}

void MemView::showRegEvents(const QModelIndex &index)
{
  unsigned cpu, reg;
  if (!_rmodel->getReg(cpu, reg, index))
    return;

  eventsView->setModel(0);
  delete _emodel;
  _emodel = new EventModelReg(_hist, cpu, reg, this);
  eventsView->setModel(_emodel);

  label_2->setText(QString("Events for register %1 on cpu %2").arg(_hist->regName(cpu, reg)).arg(cpu));
  showallButton->setEnabled(true);
}

void MemView::newView()
{
  uint64_t first, last;
  QItemSelectionModel *sm;

  if (!(sm = memView->selectionModel()) ||
      !_mmodel->selectedAddrRows(first, last, sm))
    _mmodel->getAddrRange(first, last);

  QDialog *d = new MemView(_hist, first, last - first + 1);
  d->setAttribute(Qt::WA_DeleteOnClose);
  QString s;
  s.sprintf("%08llx to %08llx", first, last);
  d->setWindowTitle(s);
  d->show();
}

void MemView::showDate(const QModelIndex &index)
{
  if (!_emodel)
    return;

  HistoryBase::event_info_s i;
  if (!_emodel->eventInfo(i, index))
    return;

  eventSpinBox->setValue(i._date);
}

void MemView::showEvent(const QModelIndex &index)
{
  if (!_emodel)
    return;

  HistoryBase::event_info_s i;
  if (!_emodel->eventInfo(i, index))
    return;

  switch (i._type) {
  case ISS_MCEVENT_READ_START:
  case ISS_MCEVENT_READ_DONE:
  case ISS_MCEVENT_WRITE_START:
  case ISS_MCEVENT_WRITE_DONE:
  case ISS_MCEVENT_LL_START:
  case ISS_MCEVENT_LL_DONE:
  case ISS_MCEVENT_SC_START:
  case ISS_MCEVENT_SC_DONE:
  case ISS_MCEVENT_SC_FAILED:
  case ISS_MCEVENT_MEM_DECLARE:
  case ISS_MCEVENT_TYPE_GLOBAL:
  case ISS_MCEVENT_TYPE_GLOBAL_RO:
  case ISS_MCEVENT_TYPE_ALLOC:
  case ISS_MCEVENT_TYPE_FREE:
  case ISS_MCEVENT_TYPE_SPECIAL:
  case ISS_MCEVENT_LOCK_ENABLE:
  case ISS_MCEVENT_LOCK_DISABLE:
  case ISS_MCEVENT_STACK_ENABLE:
  case ISS_MCEVENT_STACK_DISABLE:
  case ISS_MCEVENT_VALUE_INVALID:
  case ISS_MCEVENT_VALUE_UNDEFINED:
  case ISS_MCEVENT_VALUE_INIT:
    tabWidget->setCurrentIndex(0);
    memView->scrollTo(_mmodel->getIndex(i._addr), QAbstractItemView::EnsureVisible);

    if (QItemSelectionModel *sm = memView->selectionModel())
      sm->select(_mmodel->getSelection(i._addr, i._addr + i._size - 1),
		 QItemSelectionModel::ClearAndSelect);
    break;

  case ISS_MCEVENT_REG_CHANGE: {
    tabWidget->setCurrentIndex(1);
    QModelIndex ndx = _rmodel->getIndex(i._cpu_id, i._addr);
    regView->scrollTo(ndx, QAbstractItemView::EnsureVisible);
    regView->setCurrentIndex(ndx);
    break;
  }

  }
}

void MemView::addrEntered()
{
  QString s = addrEdit->text();
  bool ok;

  uint64_t addr = s.toULong(&ok, 16);
  if (ok) {
    addrEdit->setText("");
    memView->scrollTo(_mmodel->getIndex(addr), QAbstractItemView::PositionAtTop);
  }
}

void MemView::newEvent(unsigned int date, unsigned int type,
		       qulonglong addr, qulonglong size)
{
  _last_date = date;
  _last_addr = addr;
  _last_size = size;

  if (_track_timer < 0 && trackButton->isChecked())
    _track_timer = startTimer(300);
}

void MemView::timerEvent(QTimerEvent *event)
{
  killTimer(_track_timer);
  _track_timer = -1;

  eventSpinBox->setValue(_last_date);

  if (_emodel) {
    _emodel->update();

    QModelIndex ndx = _emodel->indexFromDate(_last_date);
    if (ndx.isValid())
      eventsView->scrollTo(ndx);
  }

  memView->scrollTo(_mmodel->getIndex(_last_addr), QAbstractItemView::EnsureVisible);
  if (QItemSelectionModel *sm = memView->selectionModel())
    sm->select(_mmodel->getSelection(_last_addr, _last_addr + _last_size - 1),
	       QItemSelectionModel::ClearAndSelect);
}

void MemView::initEvent()
{
  showAllEvents();
}

MemView::MemView(HistoryBase *hist, uint64_t addr, uint64_t size, QWidget *parent)
{
  setupUi(this);
  _mmodel = new MemModel(hist, addr, size, this);
  _rmodel = new RegModel(hist, this);
  _emodel = 0;
  _hist = hist;
  _track_timer = -1;

  memView->setModel(_mmodel);
  for (int i = 0; i < 32; i++)
    memView->setColumnWidth(i, 32);
  memView->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
  memView->verticalHeader()->setResizeMode(QHeaderView::Fixed);

  regView->verticalHeader()->setResizeMode(QHeaderView::Fixed);
  regView->setModel(_rmodel);

  connect(_hist, SIGNAL(newEvent(unsigned int, unsigned int, qulonglong, qulonglong)),
	  this, SLOT(newEvent(unsigned int, unsigned int, qulonglong, qulonglong)));

  connect(_hist, SIGNAL(resetEvent(unsigned)),
	  _rmodel, SLOT(resetEvent(unsigned)));

  connect(_hist, SIGNAL(initEvent()),
	  this, SLOT(initEvent()));

  connect(addrEdit, SIGNAL(returnPressed()),
	  this, SLOT(addrEntered()));

  connect(newviewButton, SIGNAL(clicked()),
	  this, SLOT(newView()));

  connect(showallButton, SIGNAL(clicked()),
	  this, SLOT(showAllEvents()));

  connect(memView, SIGNAL(doubleClicked(const QModelIndex &)),
	  this, SLOT(showMemEvents(const QModelIndex &)));

  connect(regView, SIGNAL(doubleClicked(const QModelIndex &)),
	  this, SLOT(showRegEvents(const QModelIndex &)));

  connect(eventsView, SIGNAL(doubleClicked(const QModelIndex &)),
	  this, SLOT(showDate(const QModelIndex &)));

  connect(eventsView, SIGNAL(clicked(const QModelIndex &)),
	  this, SLOT(showEvent(const QModelIndex &)));

  connect(eventSpinBox, SIGNAL(valueChanged(int)),
	  _mmodel, SLOT(setDate(int)));

  connect(eventSpinBox, SIGNAL(valueChanged(int)),
	  _rmodel, SLOT(setDate(int)));

  eventSpinBox->setMaximum((unsigned int)-1 >> 1);

  //  showAllEvents();
}

