//
// 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 <ColorEditDlg.hpp>
#include <ui_ColorEditDlg.h>
#include <QAbstractButton>
#include <QColorDialog>
#include <QFileDialog>
#include <QDir>
#include <Information.hpp>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QMenu>
#include <QPoint>
#include <QItemSelectionModel>
#include "Dir.hpp"
#include <stdlib.h>
#include <Random.hpp>
#include <Geometry.hpp>

using mgx::Colorb;
using mgx::ColorMap;
using mgx::Point2f;
using mgx::Point3f;
using mgx::Point4f;
using mgx::ran;

// Color Model class methods

ColorModel::ColorModel(std::vector<Colorb> &colors, Point2f &bounds)
  : colorsDest(colors), colorsLocal(colors), boundsDest(bounds), boundsLocal(bounds) {}

int ColorModel::rowCount(const QModelIndex& parent) const
{
  if(parent.isValid())
    return 0;
  return colorsLocal.size();
}

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

QVariant ColorModel::data(const QModelIndex& index, int role) const
{
  if(index.column() > 0)
    return QVariant();
  if(index.row() >= (int)colorsLocal.size())
    return QVariant();
  int n = index.row();
  switch(role) {
  case Qt::DisplayRole:
    return QString::number(n);
  case Qt::DecorationRole:
    return (QColor)colorsLocal[n];
  }
  return QVariant();
}

void ColorModel::reset()
{
  loadPresetColors("Jet");
}

void ColorModel::presetColors(const QString &preset)
{
  loadPresetColors(preset);
  emit dataChanged(index(0), index(colorsLocal.size() - 1));
}

void ColorModel::loadPresetColors(const QString &preset)
{
  beginResetModel();
  uint oldSize = colorsLocal.size();
  ColorMap colorMap(&colorsLocal);
  colorMap.setColorMap(preset, oldSize);
  uint newSize = colorsLocal.size();
  if(oldSize != newSize)
    setNbColors(newSize);
  endResetModel();
}

void ColorModel::apply() 
{
  colorsDest = colorsLocal;
  boundsDest = boundsLocal;
}

bool ColorModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
  if(index.column() > 0)
    return false;
  if(index.row() >= (int)colorsLocal.size())
    return false;
  if(role == Qt::DecorationRole) {
    colorsLocal[index.row()] = (Colorb)value.value<QColor>();
    emit dataChanged(index, index);
  }
  return false;
}

void ColorModel::setNbColors(int n)
{
  int prev = rowCount();
  if(n > prev)
    insertRows(prev, n - prev);
  else if(n < prev)
    removeRows(n, prev - n);
}

bool ColorModel::insertRows(int row, int count, const QModelIndex& parent)
{
  int prev = rowCount();
  if(row < prev or parent.isValid())
    return false;
  int n = prev + count;
  beginInsertRows(QModelIndex(), prev, n - 1);
  colorsLocal.resize(n);
  for(int i = prev; i < n; ++i)
    colorsLocal[i] = colorsLocal[i - prev];
  endInsertRows();
  return true;
}

bool ColorModel::removeRows(int row, int count, const QModelIndex& parent)
{
  int prev = rowCount();
  if(row != prev - count or parent.isValid())
    return false;
  beginRemoveRows(QModelIndex(), row, prev - 1);
  colorsLocal.resize(row);
  endRemoveRows();
  return true;
}

// Color Edit Dialog class methods

ColorEditDlg::ColorEditDlg(ColorMap &cMap, QWidget* parent)
  : QDialog(parent), colorMap(cMap)
{
  ui = new Ui::ColorEditDlg();
  ui->setupUi(this);
  model = new ColorModel(colorMap.colors(), colorMap.bounds());
  ui->colorsView->setModel(model);
  connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(changeNbItems()));
  connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(changeNbItems()));
  if(colorMap.colors().size() == 0)
    model->reset();
  ui->nbColors->setValue(model->rowCount());

  // If Color Map has bounds set in dialog
  if(colorMap.hasBounds()) {
    ui->minBounds->setValue(colorMap.bounds()[0]);
    ui->maxBounds->setValue(colorMap.bounds()[1]);
    ui->labelBounds->setVisible(true);
    ui->minBounds->setVisible(true);
    ui->maxBounds->setVisible(true);
  } else {
    ui->labelBounds->setVisible(false);
    ui->minBounds->setVisible(false);
    ui->maxBounds->setVisible(false);
  }
}

ColorEditDlg::~ColorEditDlg()
{
  ui->colorsView->setModel(0);
  delete model;
  delete ui;
}

void ColorEditDlg::on_presetColors_currentIndexChanged(QString preset)
{
  if(preset.size() == 0)
    return;
  model->presetColors(preset);
  ui->presetColors->setCurrentIndex(0);
  ui->nbColors->setValue(model->rowCount());
}

void ColorEditDlg::on_buttonBox_clicked(QAbstractButton* btn)
{
  switch(ui->buttonBox->buttonRole(btn)) {
  case QDialogButtonBox::ApplyRole:
  case QDialogButtonBox::AcceptRole:
    model->apply();
    emit update();
    break;
  case QDialogButtonBox::ResetRole:
    model->reset();
    emit update();
    break;
  default:
    break;
  }
}

void ColorEditDlg::on_importColors_clicked()
{
  QString filename = QFileDialog::getOpenFileName(this, "Select labels file name", mgx::currentPath(),
                                                  "Palette files (*.mgxc);;All files (*.*)");

  if(!QFile::exists(filename) and !filename.endsWith(".mgxc"))
    filename += ".mgxc";
  QFile file(filename);
  if(!file.open(QIODevice::ReadOnly)) {
    mgx::Information::out << "Error, cannot open file '" << filename << "' for reading" << endl;
    return;
  }
  QTextStream ts(&file);
  QString header = ts.readLine();
  if(!header.startsWith("MGX COLORS")) {
    mgx::Information::out << "Error, file '" << filename << "' is not a MorphoGraphX label file" << endl;
    return;
  }
  QStringList hv = header.split(" ");
  if(hv.size() != 3 or hv[2] != "1.0") {
    mgx::Information::out << "Error, file '" << filename << "' has an unknown version number" << endl;
    return;
  }
  QString cntString = ts.readLine();
  int cnt;
  bool ok;
  cnt = cntString.trimmed().toInt(&ok);
  if(!ok or cnt < 0) {
    mgx::Information::out << "Error, in file '" << filename << "': Invalid number of colors" << endl;
    return;
  }
  QAbstractItemModel* m = ui->colorsView->model();
  int n = m->rowCount();
  if(n > cnt)
    m->removeRows(cnt, n - cnt);
  else if(n < cnt)
    m->insertRows(n, cnt - n);
  for(int i = 0; i < cnt; ++i) {
    QString color = ts.readLine().trimmed();
    if(color.isEmpty()) {
      mgx::Information::out << "Error, in file '" << filename << "': Color " << i
                                   << " is invalid or non-existent (expected: " << cnt << " colors)" << endl;
      return;
    }
    QColor col = QColor(color);
    QModelIndex idx = m->index(i, 0);
    m->setData(idx, col, Qt::DecorationRole);
  }
  ui->nbColors->setValue(cnt);
}

void ColorEditDlg::on_exportColors_clicked()
{
  QString filename = QFileDialog::getSaveFileName(this, "Select labels file name", "",
                                                  "Palette files (*.mgxc);;All files (*.*)");

  if(!filename.endsWith(".mgxc"))
    filename += ".mgxc";
  QFile file(filename);
  if(!file.open(QIODevice::WriteOnly)) {
    mgx::Information::out << "Error, cannot open file '" << filename << "' for writing" << endl;
    return;
  }
  QTextStream ts(&file);
  QAbstractItemModel* m = ui->colorsView->model();
  ts << "MGX COLORS 1.0" << endl;
  int cnt = m->rowCount();
  ts << cnt << endl;
  for(int i = 0; i < cnt; ++i) {
    QModelIndex idx = m->index(i, 0);
    QColor col = m->data(idx, Qt::DecorationRole).value<QColor>();
    ts << col.name() << endl;
  }
  file.close();
}

void ColorEditDlg::on_colorsView_doubleClicked(const QModelIndex& idx)
{
  QColor col = model->data(idx, Qt::DecorationRole).value<QColor>();
  if(col.isValid()) {
    col = QColorDialog::getColor(col, this, "Change color");
    if(col.isValid()) {
      model->setData(idx, col, Qt::DecorationRole);
    }
  }
}

void ColorEditDlg::on_setNbColors_clicked() 
{
  model->setNbColors(ui->nbColors->value());
}

//void ColorEditDlg::on_colorsView_customContextMenuRequested(const QPoint& pos)
//{
//  QModelIndex idx = ui->colorsView->indexAt(pos);
//  if(idx.isValid()) {
//    selectedColor = idx.row();
//    QMenu* menu = new QMenu(this);
//    menu->addAction(interpolateAction);
//    menu->popup(ui->colorsView->mapToGlobal(pos));
//  }
//}

void ColorEditDlg::changeNbItems() 
{
  ui->nbColors->setValue(ui->colorsView->model()->rowCount());
}

void ColorEditDlg::on_minBounds_valueChanged(double d)
{
  model->setBoundsMin(d);
}

void ColorEditDlg::on_maxBounds_valueChanged(double d)
{
  model->setBoundsMax(d);
}
