
#include <QColor>

#include "mem_model.hh"
#include "history.hh"
#include "memchecker_events.h"

MemModel::MemModel(HistoryBase *hist,
		   uint64_t addr, uint64_t size,
		   QObject *parent)
  : QAbstractItemModel(parent)
  , _addr(addr)
  , _size(size)
  , _hist(hist)
{
  _cache_addr = -1;
  _date = 1;
}

void MemModel::getAddrRange(uint64_t &first, uint64_t &last) const
{
  first = _addr;
  last = _addr + _size - 1;
}

void MemModel::setDate(int date)
{
  _cache_addr = -1;
  _date = date;
  emit dataChanged(index(0, 0), index(_size / bytes_per_rows - 1, bytes_per_rows - 1));
}

uint64_t MemModel::getAddress(const QModelIndex &index) const
{
  return _addr + index.row() * bytes_per_rows + index.column();
}

QModelIndex MemModel::getIndex(uint64_t addr) const
{
  if (addr < _addr || addr >= _addr + _size)
    return QModelIndex();
  addr -= _addr;
  return createIndex(addr / bytes_per_rows, addr % bytes_per_rows, 0);
}

QItemSelection MemModel::getSelection(uint64_t first, uint64_t last) const
{
  first = std::max(std::min(first, _addr + _size - 1), _addr) - _addr;
  last = std::max(std::min(last, _addr + _size - 1), _addr) - _addr;

  if (first / bytes_per_rows == last / bytes_per_rows)
    {
      return QItemSelection(createIndex(first / bytes_per_rows, first % bytes_per_rows, 0),
			    createIndex(last / bytes_per_rows, last % bytes_per_rows, 0));
    }

  QItemSelection sel;

  if (first % bytes_per_rows)
    {
      sel.select(createIndex(first / bytes_per_rows, first % bytes_per_rows, 0),
		 createIndex(first / bytes_per_rows, bytes_per_rows - 1, 0));
      first += bytes_per_rows - first % bytes_per_rows;
    }

  if (last % bytes_per_rows)
    {
      sel.select(createIndex(last / bytes_per_rows, 0, 0),
		 createIndex(last / bytes_per_rows, last % bytes_per_rows, 0));
      last -= last % bytes_per_rows;
    }

  if (first < last)
    {
      sel.select(createIndex(first / bytes_per_rows, 0, 0),
		 createIndex(last / bytes_per_rows - 1, bytes_per_rows - 1, 0));
    }

  return sel;
}

bool MemModel::selectedAddrRows(uint64_t &first, uint64_t &last, const QItemSelectionModel *sm) const
{
  int min_row = INT_MAX, max_row = 0;
  foreach(QModelIndex ndx, sm->selection().indexes())
    {
      min_row = std::min(min_row, ndx.row());
      max_row = std::max(max_row, ndx.row());
    }

  if (min_row > max_row)
    return false;

  first = _addr + bytes_per_rows * min_row;
  last = _addr + bytes_per_rows * max_row + bytes_per_rows - 1;

  return true;
}

QVariant MemModel::data(const QModelIndex &index, int role) const
{
  if (!index.isValid())
    return QVariant();

  uint64_t addr = _addr + index.row() * bytes_per_rows + index.column();

  if (addr != _cache_addr)
    _cache_ok = _hist->memInfo(_cache, _cache_addr = addr, _date);

  bool ok = _cache_ok;
  HistoryBase::mem_info_s &i = _cache;

  switch (role)
    {
    case Qt::DisplayRole: {
      if (!ok)
	return QVariant();

      switch (i._type & ISS_MCEVENT_VALUE_MASK)
	{
	case ISS_MCEVENT_VALUE_INVALID:
	case ISS_MCEVENT_VALUE_INIT:
	  break;

	case ISS_MCEVENT_VALUE_UNDEFINED:
	default:
	  return QVariant();
	}

      QString s;
      s.sprintf("%02x", i._data);
      return QVariant(s);
    }

    case Qt::BackgroundRole: {
      if (!ok)
	return QVariant(Qt::gray);

      QColor c;

      if (i._type & ISS_MCEVENT_STACK_ENABLE)
	c = QColor(255, 255, 220);

      else switch (i._type & ISS_MCEVENT_TYPE_MASK)
	{
	case ISS_MCEVENT_TYPE_NONE:
	  c = Qt::white;
	  break;
	case ISS_MCEVENT_TYPE_GLOBAL:
	  c = QColor(220, 220, 255);
	  break;
	case ISS_MCEVENT_TYPE_GLOBAL_RO:
	  c = QColor(180, 180, 255);
	  break;
	case ISS_MCEVENT_TYPE_ALLOC:
	  c = QColor(220, 255, 220);
	  break;
	case ISS_MCEVENT_TYPE_FREE:
	  c = QColor(255, 180, 180);
	  break;
	case ISS_MCEVENT_TYPE_SPECIAL:
	  c = QColor(255, 180, 255);
	  break;
	}

      if (addr & 4)
	c = c.darker(110);

      return QVariant(c);
    }

    case Qt::ForegroundRole: {
      if (!ok)
	return QVariant(Qt::black);

      QColor c;

      switch (i._type & ISS_MCEVENT_VALUE_MASK)
	{
	case ISS_MCEVENT_VALUE_INVALID:
	  c = Qt::red;
	  break;
	case ISS_MCEVENT_VALUE_UNDEFINED:
	  c = Qt::darkYellow;
	  break;
	case ISS_MCEVENT_VALUE_INIT:
	  c = Qt::black;
	  break;
	default:
	  c = Qt::magenta;
	}

      return QVariant(c);
    }

    default:
      return QVariant();
    }
}

Qt::ItemFlags MemModel::flags(const QModelIndex &index) const
{
  return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant MemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (role != Qt::DisplayRole)
    return QVariant();

  QString s;

  switch (orientation) {
  case Qt::Horizontal: 
    s.sprintf("%02x", section);
    break;
  case Qt::Vertical:
    s.sprintf("%08x", _addr + section * bytes_per_rows); 
    break;
  }

  return QVariant(s);
}

QModelIndex MemModel::index(int row, int column, const QModelIndex &parent) const
{
  if (row < _size / bytes_per_rows && column < bytes_per_rows)
    return createIndex(row, column, 0);
  else
    return QModelIndex();
}

QModelIndex MemModel::parent(const QModelIndex &index) const
{
  return QModelIndex();
}

int MemModel::rowCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    return 0;
  return _size / bytes_per_rows;
}

int MemModel::columnCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    return 0;
  return bytes_per_rows;
}



