//
// 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 "GlobalProcess.hpp"
#include "MeshProcessCreation.hpp"
#include "Information.hpp"
#include "Progress.hpp"
#include <QFileDialog>

using qglviewer::Vec;

namespace mgx 
{
  bool SetCurrentStack::run(const QString &store, int id) 
  {
    return setCurrentStack(id, store);
  }
  REGISTER_PROCESS(SetCurrentStack);
  
  bool SaveGlobalTransform::initialize(QWidget* parent)
  {
    QString filename = parm("Filename");
    if(filename.isEmpty())
      filename = QFileDialog::getSaveFileName(parent, "Choose transform file to save", QDir::currentPath(),
                                              "Text files (*.txt)");
    if(filename.isEmpty())
      return false;
    if(!filename.endsWith(".txt", Qt::CaseInsensitive))
      filename += ".txt";
    setParm("Filename", filename);
    return true;
  }
  
  bool SaveGlobalTransform::run(const QString& filename)
  {
    QFile file(filename);
    if(!file.open(QIODevice::WriteOnly)) {
      setErrorMessage(QString("File '%1' cannot be opened for writing").arg(filename));
      return false;
    }
    QTextStream out(&file);
  
    // Get difference between stacks
    qglviewer::Frame f1(stack(0)->getFrame().worldInverse()), f2;
    f2.setFromMatrix(stack(1)->getFrame().worldMatrix());
    f2.setReferenceFrame(&f1);
  
    // Write to file
    Matrix4d m(f2.worldMatrix());
    out << transpose(m) << endl;
    file.close();
  
    SETSTATUS(QString("Transform saved to: %1").arg(filename));
    return true;
  }
  REGISTER_PROCESS(SaveGlobalTransform);
  
  bool JoinRegionsSegment::run(Store* work, Mesh* mesh, float cubeSize)
  {
    const Stack* stack = work->stack();
    HVecUS& data = work->data();
    const vvGraph& S = mesh->graph();
  
    // Get selected vertices, and find connected region
    vvGraph V(mesh->activeVertices());
    mesh->getConnectedVertices(S, V);
  
    // Find center of all cells (should be closed surfaces)
    typedef std::pair<int, int> IntIntPair;
    typedef std::pair<int, Point3f> IntPoint3fPair;
    std::map<int, Point3f> posMap;
    std::map<int, int> cntMap;
    int count = 0;
    while(!V.empty()) {
      vvGraph T;
      T.insert(*V.begin());
      mesh->getConnectedVertices(S, T);
      forall(const vertex& v, T) {
        posMap[count] += Point3f(v->pos);
        cntMap[count]++;
        V.erase(v);
      }
      count++;
    }
    forall(const IntIntPair& p, cntMap)
      posMap[p.first] /= p.second;
  
    // Find stack label of center of regions and put in a set
    std::set<int> stackLabelSet;
    forall(const IntPoint3fPair& p, posMap) {
      Point3i imgPos = stack->worldToImagei(p.second);
      int stackLabel = data[stack->offset(imgPos)];
      if(stackLabel > 0)
        stackLabelSet.insert(data[stack->offset(imgPos)]);
    }
  
    // Update voxels
    int newLabel = *(stackLabelSet.begin());
    for(uint i = 0; i < data.size(); i++) {
      if(data[i] == 0)
        continue;
      if(stackLabelSet.count(data[i]) > 0)
        data[i] = newLabel;
    }
  
    // Also update mesh labels
    V = vvGraph(mesh->activeVertices());
    mesh->getConnectedVertices(S, V);
    forall(const vertex& v, V) {
      v->selected = false;
      v->label = newLabel;
    }
  
    // Now re-march the label
    if(cubeSize > 0) {
      MarchingCube3D mc(*this);
      mc.run(mesh, work, cubeSize, 1, 0, newLabel);
    }
  
    mesh->updateTriangles();
    mesh->updateSelection();
    work->changed();
    work->setLabels(true);
    return true;
  }
  REGISTER_PROCESS(JoinRegionsSegment);
}
