//
// 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.
// 
#ifndef SYSTEMPROCESSLOAD_HPP
#define SYSTEMPROCESSLOAD_HPP

#include <Process.hpp>
#include <QFileDialog>

/**
 * \file SystemProcessLoad.hpp
 * This file contains processes defined in MorphoGraphX to load data
 */

class QDialog;
class Ui_LoadStackDialog;
class Ui_LoadMeshDialog;
class Ui_ImportMeshDialog;
class QFile;
class QIODevice;

namespace mgx 
{
  mgx_EXPORT QList<int> extractVersion(QIODevice& file);
  
  /**
   * \class StackSwapBytes SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Process that swap bytes
   *
   * \ingroup StackProcess
   */
  class mgx_EXPORT StackSwapBytes : public Process {
  public:
    StackSwapBytes(const Process& process) : Process(process)
    {
      setName("Stack/Filters/Swap Bytes");
      setDesc("Swap the bytes of the values in the stack.");
      setIcon(QIcon(":/images/SwapBytes.png"));
    }
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      bool res = run(input, output);
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    bool run(const Store* input, Store* output);
  };
  
  /**
   * \class StackImport SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Import images forming a stack.
   *
   * \ingroup StackProcess
   */
  class mgx_EXPORT StackImport : public Process {
    Q_OBJECT
  public:
    StackImport(const Process& proc) : Process(proc)
    {
      setName("Stack/System/Import");
      setDesc("Import stack from a series of images");
      setIcon(QIcon(":/images/open.png"));

      addParm("Stack","Stack","-1");		// 0
      addParm("Store","Store","Current", storeChoice() << "Current");   // 1
      addParm("X Step","X Step","1.0");		// 2
      addParm("Y Step","Y Step","1.0");		// 3
      addParm("Z Step","Z Step","1.0");		// 4
      addParm("Brightness","Brightness","1.0");		// 5
      addParm("Profile File","Profile File","");		// 6
    }
  
    bool initialize(QWidget* parent);
  
    bool run()
    {
      int stackId = parm("Stack").toInt();
      QString storeName = parm("Store");
      Point3f step(parm("X Step").toFloat(), parm("Y Step").toFloat(), parm("Z Step").toFloat());
      float brightness(parm("Brightness").toFloat());
      QString filename(parm("Profile File"));
  
      // Get stack and store
      Stack* stk = currentStack();
      if(stackId != -1)
        stk = stack(parm("Stack").toInt());
      if(!checkState().stack(STACK_ANY, stackId))
        return false;
  
      Store* store = stk->currentStore();
      if(storeName == "Main")
        store = stk->main();
      else if(storeName == "Work")
        store = stk->work();
  
      return run(stk, store, step, brightness, filename);
    }

    /**
     * Import the stack
     * \param stack Stack that will contain the image
     * \param store Store that will contain the image
     * \param filenames Either a single text file containing all the
     * parameters, or a list of image files, one per z slice
     * \param brightness Multiplication coefficient to convert less than 16
     * bits images to 16 bits
     * \param step Size of a voxel
     */
    bool run(Stack* stack, Store* store, Point3f step, float brightness, QString filename);
  
  protected slots:
    void SaveProfile();
    void LoadProfile(QString filename = QString());
    void AddFilesSlot();
    void AddFilesSlot(const QStringList& files);
    void RemoveFilesSlot();
    void FilterFilesSlot();
    void SortAscendingSlot(bool val = true);
    void SortDescendingSlot();
  
  protected:
    bool LoadProfile(QString filename, Point3u& size, Point3f& step, float& brightness, QStringList& files);
    void setImageSize(Point3u size);
    void setImageSize(uint x, uint y, uint z) {
      setImageSize(Point3u(x, y, z));
    }
    void setImageResolution(Point3f step);
    void setImageResolution(float x, float y, float z) {
      setImageResolution(Point3f(x, y, z));
    }
    Point3f imageResolution();
  
    QString txtFile;
    QDialog* dlg;
    Ui_LoadStackDialog* ui;
    QString loadedFile;
    Point3u imageSize;
    QStringList imageFiles;
  };
  
  /**
   * \class StackOpen SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Open a stack in either the MGXS or INRIA formats.
   *
   * \ingroup StackProcess
   */
  class mgx_EXPORT StackOpen : public Process {
  public:
    StackOpen(const Process& proc) : Process(proc)
    {
      setName("Stack/System/Open");
      setDesc("Open a stack from a known 3D image format");
      setIcon(QIcon(":/images/open.png"));

      addParm("Filename","Filename","");		// 0
      addParm("Store","Store","Main", storeChoice());		// 1
      addParm("Stack Number","Stack number","0");		// 2
      addParm("HDF5 DataSetName","HDF5 DataSetName","/label");   // 2
    }
  
    bool run();

    /**
     * Open the image
     * \param stack Stack that will contain the image
     * \param store Store that will contain the image
     * \param filename File containing the image.
     */
    bool run(Stack* stack, Store* store, QString filename);

    bool initialize(QWidget* parent);
  
    bool loadMGXS_1_3(QIODevice& file, Stack* stack, Store* store);
    bool loadMGXS_1_2(QIODevice& file, Stack* stack, Store* store);
    bool loadMGXS_1_1(QIODevice& file, Stack* stack, Store* store);
    bool loadMGXS_1_0(QIODevice& file, Stack* stack, Store* store);
    bool loadMGXS_0(QIODevice& file, Stack* stack, Store* store);
    void centerImage();
  };
  

  /**
   * \class MeshLoad SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Load a mesh file.
   *
   * \ingroup MeshProcess
   */
  class mgx_EXPORT MeshLoad : public Process {
    Q_OBJECT
  public:
    MeshLoad(const Process& proc) : Process(proc)
    {
      setName("Mesh/System/Load");
      setDesc("Load a mesh from one of the known formats.");
      setIcon(QIcon(":/images/open.png"));

      addParm("Filename","Filename","");		// 0
      addParm("Transform","Transform","no",booleanChoice());		// 1
      addParm("Add","Add","no",booleanChoice());		// 2
      addParm("Stack Number","Stack number","0");		// 3
    }
  
    bool run();
    /**
     * Load a MGXM file
     * \param mesh Mesh that will contain the data
     * \param filename File containing the mesh.
     * \param scale If true, the mesh will be scaled according to the current
     * scale factor
     * \param transform If true, the mesh will be transformed according to
     * the current transformation frame
     * \param add If true, the mesh will be added to the current one instead
     * of replacing it.
     */
    bool run(Mesh* mesh, QString filename, bool transform, bool add);
    bool initialize(QWidget* parent);
  
    bool loadMGXM_0(QIODevice& file, Mesh* mesh, bool scale, bool transform, bool has_color = true);
    bool loadMGXM_1_0(QIODevice& file, Mesh* mesh, bool& scale, bool& transform, bool has_color = true);
    bool loadMGXM_1_1(QIODevice& file, Mesh* mesh, bool& scale, bool& transform, bool has_color = true);
    bool loadMGXM_1_2(QIODevice& file, Mesh* mesh, bool& scale, bool& transform, bool has_color = true);
    bool loadMGXM_1_3(QIODevice& file, Mesh* mesh, bool& scale, bool& transform);
    bool loadMGXM_2_0(QIODevice& file, const QList<int> &version, Mesh* mesh, bool& scale, bool& transform);
    bool loadMGXM_2_1(QIODevice& file, const QList<int> &version, Mesh* mesh, bool& scale, bool& transform);
  
    void findSignalBounds(Mesh* mesh);
  
  protected slots:
    void selectMeshFile();
  
  protected:
    void setMeshFile(const QString& filename);
  
    QDialog* dlg;
    Ui_LoadMeshDialog* ui;
  };
  
  /**
   * \class MeshImport SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Import a mesh from a file of another format
   *
   * \ingroup MeshProcess
   */
  class mgx_EXPORT MeshImport : public Process {
    Q_OBJECT
  
  public:
    MeshImport(const Process& proc) : Process(proc)
    {
      setName("Mesh/System/Import");
      setDesc("Import a mesh from one of the known formats.");
      setIcon(QIcon(":/images/open.png"));

      addParm("Filename","Filename","");		// 0
      addParm("Kind","Kind","PLY",
              QStringList() << "VTK Mesh" << "Text" << "Cells" << "Keyence" << "MeshEdit");	// 1
      addParm("Scale","Scale","yes", booleanChoice());		// 2
      addParm("Transform","Transform","no", booleanChoice());		// 3
      addParm("Add","Add","no", booleanChoice());		// 4
      addParm("Stack Number","Stack number","0");		// 5
    }
  
    bool run();
    /**
     * Import a mesh
     * \param mesh Mesh that will contain the data
     * \param filename File describing the mesh
     * \param type Type of the file. Must be one of 'Text', 'Cells', 'Keyence' or 'MeshEdit', or empty (i.e.
     * auto-detect)
     * \param scale If true, the mesh will be scaled
     * \param transform If true, the mesh will be transformed
     * \param add If true, the mesh won't be cleared before loading the file
     *
     * \sa MeshOpen
     */
    bool initialize(QWidget* parent);

    bool run(Mesh* mesh, QString filename, QString type, bool scale, bool transform, bool add);
  
  protected slots:
    void selectMeshFile();
    void selectMeshType(const QString& type);
  
  protected:
    void setMeshFile(const QString& filename, const QString& type = QString());
    QString properFile(QString filename, const QString& type) const;
  
    bool loadText(Mesh* mesh, const QString& filename, bool scale, bool transform, bool add);
    bool loadCells(Mesh* mesh, const QString& filename, bool scale, bool transform, bool add);
    bool loadKeyence(Mesh* mesh, const QString& filename, bool scale, bool transform, bool add);
    bool loadMeshEdit(Mesh* mesh, const QString& filename, bool scale, bool transform, bool add);
    bool loadMeshVTK(Mesh* mesh, const QString& filename, bool& scale, bool transform, bool add);
    bool loadMeshOBJ(Mesh* mesh, const QString& filename, bool scale, bool transform, bool add);
    bool loadMeshPLY(Mesh* mesh, const QString& filename, bool scale, bool transform, bool add);
  
    QDialog* dlg;
    Ui_ImportMeshDialog* ui;
  };
  
  /**
   * \class LoadAllData SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Load all the files as specified in the various objects (filenames).
   *
   * Note that currently, it will load only the main store of the stacks as
   * only they can have any data.
   *
   * \ingroup MiscellaneousProcess
   */
  class mgx_EXPORT LoadAllData : public Process
  {
  public:
    LoadAllData(const Process& proc) : Process(proc)
    {
      setName("Misc/System/Load All");
      setDesc("Load the data for all existing objects, using the filename and properties set in them.");
      setIcon(QIcon(":/images/open.png"));
    }
  
    bool run();
  
  protected:
    bool loadStore(Stack* stack, Store* store, QStringList& errors);
  };
  
  /**
   * \class LoadViewFile SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   * Load a view file and all the associated files.
   *
   * \ingroup GlobalProcess
   */
  class mgx_EXPORT LoadViewFile : public Process {
  public:
    LoadViewFile(const Process& proc) : Process(proc)
    {
      setName("Misc/System/Load View");
      setDesc("Load a view file and set all the fields and interface. Does not load the data though.");
      setIcon(QIcon(":/images/open.png"));

      addParm("Filename","Filename","");		// 0
    }

    bool run();
    /**
     * Load the file \c filename
     */
    bool run(QString filename);
  
    bool initialize(QWidget* parent);
  
  };
  
  /**
   * \class ResetMeshProcess SystemProcessLoad.hpp <SystemProcessLoad.hpp>
   *
   * Reset a mesh
   *
   * \ingroup MeshProcess
   */
  class mgx_EXPORT ResetMeshProcess : public Process {
  public:
    ResetMeshProcess(const Process& proc) : Process(proc)
    {
      setName("Mesh/System/Reset");
      setDesc("Reset a mesh, -1 for current.");
      setIcon(QIcon(":/images/ClearStack.png"));

      addParm("Mesh","Mesh","-1");		// 0
    }
  
    bool run();
    bool run(Mesh* m);
  
  };

  /**
   * \class SaveParents MeshProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Save mapping from labels to parents
   *
   * \ingroup MeshProcess
   */
  class mgx_EXPORT SaveParents : public Process {
  public:
    SaveParents(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Save Parents");
      setDesc("Save map of labels to parents labels to a file");
      setIcon(QIcon(":/images/ParentsSave.png"));

      addParm("Filename","Path to spreadsheet file.","");		// 0
      addParm("Existing Labels Only","Save only labels that currently exist.","Yes", booleanChoice());
    }

    bool initialize(QWidget* parent);
  
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh* m = currentMesh();
      return run(m, parm("Filename"), stringToBool(parm("Existing Labels Only")));
    }
  
    bool run(Mesh* mesh, const QString& filename, bool saveOnlyExisting);
  
  };
  
  /**
   * \class LoadParents ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Load mapping from each label to a label in a different mesh (parent).
   */
  class mgx_EXPORT LoadParents : public Process 
  {
  public:
    LoadParents(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Load Parents");
      setDesc("Load map of labels to parents from a file");
      setIcon(QIcon(":/images/Parents.png"));

      addParm("Filename","Path to label parents file.","");		// 0
      addParm("FileType","File type, CSV(label, parent) or TXT(parent: label, label ...)","CSV",
              QStringList() << "CSV" << "TXT");		// 1
      addParm("Keep","Keep existing parents","No", booleanChoice());		// 2

    }
  
    bool initialize(QWidget* parent);
  
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh* m = currentMesh();
      return run(m, parm("Filename"), parm("FileType"), stringToBool(parm("Keep")));
    }
  
    bool run(Mesh* mesh, const QString& filename, const QString &fileType, bool keep);
  
  };
  
  /**
   * \class ResetParents MeshProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Clear label to parent mapping
   *
   * \ingroup MeshProcess
   */
  class mgx_EXPORT ResetParents : public Process {
  public:
    ResetParents(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Reset Parents");
      setDesc("Clear mapping from parents to labels");
      setIcon(QIcon(":/images/ParentsClear.png"));
    }
  
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh* m = currentMesh();
      return run(m);
    }
  
    bool run(Mesh* mesh);
  };
}

#endif
