//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 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 "TransferMarkers.hpp"
#include <QLineEdit>
#include <QDoubleValidator>
#include <QItemSelection>
#include <QPushButton>

#include <QTextStream>
#include <stdio.h>

namespace mgx 
{
  MarkerColorDelegate::MarkerColorDelegate(QObject* parent) 
  				             : QItemDelegate(parent), _font("Monospace")
  {
    _font.setStyleHint(QFont::TypeWriter);
  }
  
  QWidget* MarkerColorDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const
  {
    QLineEdit* editor = new QLineEdit(parent);
    editor->setFont(_font);
    if(index.column() == 0)
      editor->setValidator(new QDoubleValidator(0, 1, 6, editor));
    else
      editor->setInputMask("\\#HHHHHHHH");
    return editor;
  }
  
  void MarkerColorDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
  {
    if(index.column() == 0) {
      bool ok;
      double value = index.model()->data(index, Qt::EditRole).toDouble(&ok);
      if(ok)
        dynamic_cast<QLineEdit*>(editor)->setText(QString::number(value, 'f', 6));
    } else {
      QString txt = index.model()->data(index, Qt::EditRole).toString();
      dynamic_cast<QLineEdit*>(editor)->setText(txt);
    }
  }
  
  void MarkerColorDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
  {
    QVariant value;
    if(index.column() == 0)
      value = dynamic_cast<QLineEdit*>(editor)->text().toDouble();
    else
      value = dynamic_cast<QLineEdit*>(editor)->text();
    model->setData(index, value, Qt::EditRole);
  }
  
  void MarkerColorDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option,
                                                 const QModelIndex&) const
  {
    editor->setGeometry(option.rect);
  }
  
  TransferMarkerModel::TransferMarkerModel(const std::vector<double>& m, const std::vector<QColor>& cols,
                                           Interpolation mo, bool rgba, QObject* parent)
    : QAbstractTableModel(parent)
    , markers(m)
    , colors(cols)
    , mode(mo)
    , showRgba(rgba)
    , _font("Monospace")
  {
    _font.setStyleHint(QFont::TypeWriter);
  }
  
  int TransferMarkerModel::rowCount(const QModelIndex&) const {
    return markers.size();
  }
  
  Qt::ItemFlags TransferMarkerModel::flags(const QModelIndex& index) const
  {
    if(!index.isValid())
      return Qt::NoItemFlags;
    int row = index.row();
    int column = index.column();
    if(column == 0 and (row == 0 or row == int(markers.size()) - 1))
      return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    else
      return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
  }
  
  QVariant TransferMarkerModel::data(const QModelIndex& index, int role) const
  {
    if(!index.isValid())
      return QVariant();
    int row = index.row();
    int column = index.column();
  
    if(row >= int(markers.size()) or column > 1)
      return QVariant();
  
    if(column == 0) {
      if(role == Qt::DisplayRole or role == Qt::EditRole)
        return markers[row];
    } else {
      switch(role) {
      case Qt::DisplayRole:
      case Qt::EditRole:
        return colorText(row);
      case Qt::DecorationRole:
        return colors[row];
      case Qt::FontRole:
        return _font;
      default:
        return QVariant();
      }
    }
    return QVariant();
  }
  
  bool TransferMarkerModel::setData(const QModelIndex& index, const QVariant& value, int)
  {
    if(!index.isValid())
      return false;
    int row = index.row();
    int column = index.column();
  
    if(row >= int(markers.size()) or column > 1)
      return false;
  
    if(column == 0) {
      if(row == 0 or row == int(markers.size()) - 1)
        return false;
      bool ok;
      double val = value.toDouble(&ok);
      if(!ok)
        return false;
      markers[row] = val;
      emit dataChanged(index, index);
      return true;
    } else {
      if(setColorText(row, value.toString())) {
        emit dataChanged(index, index);
        return true;
      }
    }
    return false;
  }
  
  QVariant TransferMarkerModel::headerData(int section, Qt::Orientation orientation, int role) const
  {
    if(role != Qt::DisplayRole)
      return QVariant();
    switch(orientation) {
    case Qt::Horizontal:
      if(section == 0)
        return "Position";
      else if(section == 1)
        return "Color";
      break;
    case Qt::Vertical:
      return QString::number(section);
    }
    return QVariant();
  }
  
  void TransferMarkerModel::addMarker(const QItemSelection& selection)
  {
    if(selection.empty())
      return;
    int max_row = -1;
    const QModelIndexList& lst = selection.indexes();
    for(int i = 0; i < lst.size(); ++i)
      if(max_row < lst[i].row())
        max_row = lst[i].row();
    if(max_row < int(markers.size()) - 1) {
      beginInsertRows(QModelIndex(), max_row + 1, max_row + 1);
      double new_pt = (markers[max_row] + markers[max_row + 1]) / 2;
      QColor col1 = colors[max_row];
      QColor col2 = colors[max_row + 1];
      QColor col;
      switch(mode) {
      case TransferFunction::RGB: {
        double r = (col1.redF() + col2.redF()) / 2;
        double g = (col1.greenF() + col2.greenF()) / 2;
        double b = (col1.blueF() + col2.blueF()) / 2;
        double a = (col1.alphaF() + col2.alphaF()) / 2;
        col.setRgbF(r, g, b, a);
      } break;
      case TransferFunction::HSV: {
        double h = (col1.hueF() + col2.hueF()) / 2;
        double s = (col1.valueF() + col2.valueF()) / 2;
        double v = (col1.saturationF() + col2.saturationF()) / 2;
        double a = (col1.alphaF() + col2.alphaF()) / 2;
        col.setHsvF(h, s, v, a);
      } break;
      case TransferFunction::CYCLIC_HSV: {
        double h1 = col1.hueF();
        double h2 = col2.hueF();
        if(h2 - h1 > 0.5)
          h1 += 1;
        else if(h1 - h2 > 0.5)
          h2 += 1;
        double h = (h1 + h2) / 2;
        if(h > 1)
          h -= 1;
        double s = (col1.valueF() + col2.valueF()) / 2;
        double v = (col1.saturationF() + col2.saturationF()) / 2;
        double a = (col1.alphaF() + col2.alphaF()) / 2;
        col.setHsvF(h, s, v, a);
      } break;
      }
      markers.insert(markers.begin() + max_row + 1, new_pt);
      colors.insert(colors.begin() + max_row + 1, col);
      endInsertRows();
    }
  }
  
  void TransferMarkerModel::removeMarker(const QItemSelection& selection)
  {
    if(selection.empty())
      return;
    int min_row = markers.size(), max_row = -1;
    const QModelIndexList& lst = selection.indexes();
    for(int i = 0; i < lst.size(); ++i) {
      if(min_row > lst[i].row())
        min_row = lst[i].row();
      if(max_row < lst[i].row())
        max_row = lst[i].row();
    }
    if(min_row == 0)
      min_row = 1;
    if(max_row == int(markers.size()) - 1)
      max_row -= 1;
    if(min_row > max_row)
      return;
    beginRemoveRows(QModelIndex(), min_row, max_row);
    markers.erase(markers.begin() + min_row, markers.end() + max_row + 1);
    colors.erase(colors.begin() + min_row, colors.end() + max_row + 1);
    endRemoveRows();
  }
  
  void TransferMarkerModel::spreadMarkers(const QItemSelection& selection)
  {
    if(selection.empty())
      return;
    int min_row = markers.size(), max_row = -1;
    const QModelIndexList& lst = selection.indexes();
    for(int i = 0; i < lst.size(); ++i) {
      if(min_row > lst[i].row())
        min_row = lst[i].row();
      if(max_row < lst[i].row())
        max_row = lst[i].row();
    }
    if(max_row <= min_row)
      return;
    double min_marker = markers[min_row];
    double max_marker = markers[max_row];
    double delta = (max_marker - min_marker) / (max_row - min_row);
    double value = min_marker;
    for(int i = min_row + 1; i < max_row; ++i) {
      value += delta;
      markers[i] = value;
    }
    emit dataChanged(index(min_row + 1, 0), index(max_row - 1, 0));
  }
  
  void TransferMarkerModel::rgbaMode()
  {
    if(not showRgba) {
      showRgba = true;
      emit dataChanged(index(1, 1), index(markers.size(), 1));
    }
  }
  
  void TransferMarkerModel::hsvaMode()
  {
    if(showRgba) {
      showRgba = false;
      emit dataChanged(index(1, 1), index(markers.size(), 1));
    }
  }
  
  QString TransferMarkerModel::colorText(int idx) const
  {
    QColor col = colors[idx];
    if(showRgba) {
      return QString("#%1%2%3%4")
             .arg(col.red(), 2, 16, QChar('0'))
             .arg(col.green(), 2, 16, QChar('0'))
             .arg(col.blue(), 2, 16, QChar('0'))
             .arg(col.alpha(), 2, 16, QChar('0'));
    } else {
      return QString("#%1%2%3%4")
             .arg((int)floor(col.hueF() * 255), 2, 16, QChar('0'))
             .arg(col.value(), 2, 16, QChar('0'))
             .arg(col.saturation(), 2, 16, QChar('0'))
             .arg(col.alpha(), 2, 16, QChar('0'));
    }
  }
  
  bool TransferMarkerModel::setColorText(int idx, QString txt)
  {
    if(txt.size() != 9 or txt[0] != '#')
      return false;
    QColor col;
    if(showRgba) {
      bool ok;
      int r = txt.mid(1, 2).toInt(&ok, 16);
      if(!ok)
        return false;
      int g = txt.mid(3, 2).toInt(&ok, 16);
      if(!ok)
        return false;
      int b = txt.mid(5, 2).toInt(&ok, 16);
      if(!ok)
        return false;
      int a = txt.mid(7, 2).toInt(&ok, 16);
      if(!ok)
        return false;
      col = QColor::fromRgb(r, g, b, a);
    } else {
      bool ok;
      double h = txt.mid(1, 2).toInt(&ok, 16) / 255.;
      if(!ok)
        return false;
      double s = txt.mid(3, 2).toInt(&ok, 16) / 255.;
      if(!ok)
        return false;
      double v = txt.mid(5, 2).toInt(&ok, 16) / 255.;
      if(!ok)
        return false;
      double a = txt.mid(7, 2).toInt(&ok, 16) / 255.;
      if(!ok)
        return false;
      col = QColor::fromHsvF(h, s, v, a);
    }
    if(col.isValid()) {
      colors[idx] = col;
      return true;
    }
    return false;
  }
}
