//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2016 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#include "Colors.hpp"

namespace mgx 
{
  Colors* Colors::_instance = 0;
  
  const QString Colors::colorNames[Colors::NbColors]
    = { "Mesh", "Border", "Selection", "Cell labels", "Stack Bounding Box", "Mesh", "Border",
        "Selection", "Cell labels", "Stack Bounding Box", "Background", "Clipping Plane 1",
        "Clipping Plane 2", "Clipping Plane 3", "Cutting Surface", "Legend", "Scale Bar",
        "Edited pixels" };
  
  const QString Colors::categoryNames[3] = { "General", "Stack 1", "Stack 2" };
  
  const int Colors::categoryShift[3] = { StartOthers, StartMesh1, StartMesh2 };
  
  const int Colors::categorySize[3] = { NbColors - StartOthers, StartMesh2 - StartMesh1, StartOthers - StartMesh2 };
  
  Colors::Colors() : QAbstractItemModel(), colors((int)NbColors), _font("Monospace")
  {
    _font.setStyleHint(QFont::TypeWriter);
  
    resetColors();
  }
  
  void Colors::resetColors()
  {
    colors[BackgroundColor] = Qt::black;
    colors[Mesh2BorderColor] = colors[Mesh1BorderColor] = Qt::cyan;
    colors[Mesh1Color] = colors[Mesh2Color] = Qt::yellow;
    // colors[Mesh1PointColor] = colors[Mesh2PointColor] = Qt::yellow;
    colors[Mesh1SelectColor] = colors[Mesh2SelectColor] = Qt::red;
    colors[Mesh1CellsColor] = colors[Mesh2CellsColor] = Qt::white;
    colors[Stack1BBoxColor] = colors[Stack2BBoxColor] = QColor(128, 128, 128);
    colors[Clip1GridColor] = colors[Clip2GridColor] = colors[Clip3GridColor] = QColor(233, 233, 233);
    colors[CuttingPlaneGridColor] = QColor(233, 233, 233);
    colors[LegendColor] = Qt::white;
    colors[VoxelEditColor] = Qt::white;
    colors[ScaleBarColor] = Qt::white;
    emit colorsChanged();
  }
  
  bool Colors::hasChildren(const QModelIndex& parent) const {
    return parent == QModelIndex() or parent.internalId() < 4;
  }
  
  int Colors::rowCount(const QModelIndex& parent) const
  {
    if(parent == QModelIndex()) {
      return 3;
    }
    if(parent.internalId() < 4) {
      return categorySize[parent.row()];
    }
    return 0;
  }
  
  int Colors::columnCount(const QModelIndex&) const {
    return 2;
  }
  
  Qt::ItemFlags Colors::flags(const QModelIndex&) const {
    return Qt::ItemIsEnabled;
  }
  
  QVariant Colors::data(const QModelIndex& index, int role) const
  {
  
    if(index.parent() == QModelIndex()) {
      if(index.column() > 0 or index.row() > 2)
        return QVariant();
  
      if(role == Qt::DisplayRole)
        return categoryNames[index.row()];
      else
        return QVariant();
    } else {
      if(index.column() > 1 or index.row() >= rowCount(index.parent()))
        return QVariant();
      int idx = index.row() + categoryShift[index.parent().row()];
      if(index.column() == 0) {
        if(role == Qt::DisplayRole)
          return colorNames[idx];
        else
          return QVariant();
      } else {
        switch(role) {
        case Qt::DecorationRole:
          return colors[idx];
        case Qt::DisplayRole:
          return colors[idx].name();
        case Qt::FontRole:
          return _font;
        default:
          return QVariant();
        }
      }
    }
  }
  
  bool Colors::setData(const QModelIndex& index, const QVariant& value, int role)
  {
    if(index.parent() != QModelIndex() and index.column() == 1 and index.row() < rowCount(index.parent())
       and role == Qt::DecorationRole) {
      int idx = index.row() + categoryShift[index.parent().row()];
      QColor col = value.value<QColor>();
      if(col.isValid()) {
        colors[idx] = col;
        emit colorsChanged();
        return true;
      } else
        return false;
    }
    return false;
  }
  
  QVariant Colors::headerData(int section, Qt::Orientation orientation, int role) const
  {
    if(section < 2 and orientation == Qt::Horizontal and role == Qt::DisplayRole) {
      if(section == 0)
        return QString("Item");
      else
        return QString("Color");
    }
    return QVariant();
  }
  
  void Colors::readParms(Parms& parms, QString section)
  {
    for(int i = 0; i < 3; ++i) {
      int shift = categoryShift[i];
      for(int j = 0; j < categorySize[i]; ++j) {
        int idx = j + shift;
        QString name = categoryNames[i] + colorNames[idx];
        name.replace(" ", "");
        Colorf f(-1, -1, -1, -1);
        Colorf default_color = colors[idx];
        if(parms(section, name, static_cast<Vector<4, float>&>(f), static_cast<Vector<4, float>&>(default_color)))
          colors[idx] = (QColor)f;
      }
    }
    emit colorsChanged();
  }
  
  void Colors::writeParms(QTextStream& pout, QString section)
  {
    pout << endl;
    pout << "[" << section << "]" << endl;
    for(int i = 0; i < 3; ++i) {
      int shift = categoryShift[i];
      for(int j = 0; j < categorySize[i]; ++j) {
        int idx = j + shift;
        QString name = categoryNames[i] + colorNames[idx];
        name.replace(" ", "");
        pout << name << ": " << Colorf(colors[idx]) << endl;
      }
    }
  }
  
  void Colors::backupColors() {
    backup = colors;
  }
  
  void Colors::restoreColors()
  {
    colors = backup;
    emit colorsChanged();
  }
  
  QModelIndex Colors::index(int row, int column, const QModelIndex& parent) const
  {
    if(parent == QModelIndex()) {
      if(row < 3)
        return createIndex(row, column, row + 1);
      return QModelIndex();
    }
    if(parent.internalId() < 4) {
      int idx = categoryShift[parent.row()] + row;
      return createIndex(row, column, 4 + idx);
    }
    return QModelIndex();
  }
  
  QModelIndex Colors::parent(const QModelIndex& index) const
  {
    if(index == QModelIndex())
      return QModelIndex();
    qint64 id = index.internalId();
    qint64 row = id - 4;
    if(row < 0 or row > NbColors)
      return QModelIndex();
    for(int i = 0; i < 3; ++i) {
      if(row >= categoryShift[i] and row < categoryShift[i] + categorySize[i])
        return createIndex(i, 0, 1 + i);
    }
    return QModelIndex();
  }
}
