#include "universemodel.h"

#include <QBrush>
#include <QFont>
#include <QChar>
#include <QMetaEnum>

/**
 * @brief UniverseModel::UniverseModel
 * @param parent
 */
UniverseModel::UniverseModel(QObject *parent)
    : QAbstractTableModel(parent)
    , universe_(nullptr)
    , data_mode_(Decimal)
{
}


/**
 * @brief UniverseModel::headerData
 * @param section
 * @param orientation
 * @param role
 * @return
 */
QVariant UniverseModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  switch (role)
    {
    case Qt::DisplayRole:
      switch (orientation)
        {
        case Qt::Horizontal:
          return section + 1;
        case Qt::Vertical:
          return section * 10;
        }
    default:
      return QVariant();
    }
}


/**
 * @brief UniverseModel::rowCount
 * @param parent
 * @return
 */
int UniverseModel::rowCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    return 0;

  return 52;
}


/**
 * @brief UniverseModel::columnCount
 * @param parent
 * @return
 */
int UniverseModel::columnCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    return 0;

  return 10;
}


/**
 * @brief UniverseModel::data
 * @param index
 * @param role
 * @return
 */
QVariant UniverseModel::data(const QModelIndex &index, int role) const
{
  if (!checkIndex(index, CheckIndexOption::IndexIsValid))
    return QVariant();

  if (!universe_)
    return QVariant();

  uint16_t slot = (index.row() * 10) + (index.column() + 1);

  switch (role) {
    case Qt::DisplayRole:
      {
        if (slot == 0 || slot > 512)
          return QVariant();
        switch (data_mode_)
          {
          case Decimal:
            return universe_->slot(slot);
          case Hex:
            return QString("%1")
                .arg(universe_->slot(slot), 2, 16, QChar('0'))
                .toUpper();
          case Percent:
            return QString("%1%").arg((universe_->slot(slot) / 255.0F) * 100,
                                      0, 'f', 0, '0');
          }
        return QVariant();
      }
    case Qt::FontRole:
      return QFont("monospace");
    case Qt::TextAlignmentRole:
      return int(Qt::AlignCenter | Qt::AlignVCenter);
    case Qt::ForegroundRole:
      {
        if (slot > universe_->activeSlots() - 1)
          return QBrush(Qt::gray);
        return QVariant();
      }
    default:
      return QVariant();
    }
}


/**
 * @brief UniverseModel::setData
 * @param index
 * @param value
 * @param role
 * @return
 */
bool UniverseModel::setData(const QModelIndex &index, const QVariant &value,
                            int role)
{
  if (!universe_->isEditable())
    return false;

  if (data(index, role) == value)
    return false;

  uint16_t slot = (index.row() * 10) + (index.column() + 1);
  uint8_t  data;

  switch (data_mode_)
    {
    case Decimal:
      {
        if (!value.canConvert<uint>())
          return false;
        unsigned int d = value.toUInt();
        if (d > 255)
          return false;
        data = d;
      }
      break;
    case Hex:
      {
        if (!value.canConvert<QString>())
          return false;
        bool ok;
        unsigned int d = value.toString().toInt(&ok, 16);
        if (!ok || d > 255)
          return false;
        data = d;
      }
      break;
    case Percent:
      {
        if (!value.canConvert<double>())
          return false;
        int d = 255 * (value.toDouble() / 100);
        if (d < 0 || d > 255)
          return false;
        data = d;
      }
      break;
    }

  universe_->setValue(slot, data);
  emit dataChanged(index, index, QVector<int>() << role);
  return true;
}


/**
 * @brief UniverseModel::flags
 * @param index
 * @return
 */
Qt::ItemFlags UniverseModel::flags(const QModelIndex &index) const
{
  if (!checkIndex(index, CheckIndexOption::IndexIsValid))
    return Qt::NoItemFlags;

  if (!universe_)
    return Qt::NoItemFlags;

  uint16_t slot = (index.row() * 10) + (index.column() + 1);

  if (slot == 0 || slot > 512)
    return Qt::NoItemFlags;

  auto f = QAbstractItemModel::flags(index);
  return universe_->isEditable() ? f | Qt::ItemIsEditable : f;
}


/**
 * @brief UniverseModel::universeRefreshed
 */
void UniverseModel::universeRefreshed() {
    emit dataChanged(index(0,0), index(rowCount(), columnCount()));
    emit recievedUpdate(universe_);
}


/**
 * @brief UniverseModel::setDataMode
 * @param mode
 */
void UniverseModel::setDataMode(const QString mode)
{
   auto metaEnum = QMetaEnum::fromType<data_modes>();
   data_mode_ = static_cast<data_modes>(metaEnum.keyToValue(mode.toLocal8Bit()));
   emit dataChanged(index(0,0), index(rowCount(), columnCount()));
}


/**
 * @brief UniverseModel::setUniverse
 * @param universe
 */
void UniverseModel::setUniverse(QSacnUniverse *universe)
{
  if (universe_)
    disconnect(universe_, 0, this, 0);
  universe_ = universe;
  connect(universe_, &QSacnUniverse::changed,
          this, &UniverseModel::universeRefreshed);
}


/**
 * @brief UniverseModel::dataMode
 * @return
 */
UniverseModel::data_modes UniverseModel::dataMode()
{
  return data_mode_;
}
