//
// 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.
// 
#ifndef MORPHOGRAPHX_H
#define MORPHOGRAPHX_H
typedef unsigned short ushort;
typedef unsigned int uint;

#include <Config.hpp>

#include <Misc.hpp>
#include <Geometry.hpp>
#include <CutSurf.hpp>
#include <Process.hpp>
#include <TaskEditDlg.hpp>
#include <VisFlags.hpp>

#include <string.h>
#include <QMutex>
#include <QTime>
#include <QPointer>

#include <ui_GUI.h>
#include <ui_EditParms.h>
#include <ui_ProcessDocs.h>

class QDragEnterEvent;
class QDropEvent;
class QCloseEvent;
class ProcessParmsModel;
class Library;
class QFileSystemWatcher;
class QAction;
class PathEditorDlg;
class LabelEditorDlg;

namespace mgx 
{
  class ProcessThread;

  class SetupProcess;
  class Process;
}

class MorphoGraphX : public QMainWindow 
{
  Q_OBJECT

public:
  MorphoGraphX(QString appdir,QStringList addLibraries = QStringList());
  ~MorphoGraphX();

  // Must be the same in Process.hpp
  enum SystemCommand { UPDATE_VIEWER, LOAD_VIEW, SAVE_VIEW, SET_CURRENT_STACK,
                       TAKE_SNAPSHOT, UPDATE_STATE, SET_STATUS, SET_FRAME_VIS, SET_CAMERA_VIS };

  static QString AppDir;
  static QLabel* ActiveStack;

  typedef mgx::Point3u Point3u;
  typedef mgx::Point3f Point3f;

private:
  QDialog* editParmsDlog;
  Ui_EditParmsDialog* editParmsDialog;
  Ui::MainWindow ui;

  // Confocal stack objects
  mgx::ImgData stack1, stack2;

  // Cutting surface
  mgx::CutSurf cutSurf;

  size_t CudaHoldMem;
  bool initalView;
  bool needSaving;

  ProcessParmsModel* parmsModel;
  QString currentProcessName;
  bool currentProcessInTasks;
  mgx::Process* currentProcess;
  mgx::SetupProcess* currentSetup;
  mgx::ProcessThread* processThread;
  QList<QFileInfo> addedLibFiles;
  QList<Library*> loadedLibs;
#ifdef WATCH_PROCESS_FOLDERS
  QFileSystemWatcher* processWatcher;
  QTimer* processReloadTimer;
#endif
  QAction* resetParametersAct;
  QAction* editTasksAct;

  QMap<QString, mgx::ProcessDefinition> savedProc;

  TaskEditDlg::tasks_t tasks;

  QPointer<PathEditorDlg> editPathsDlg;
  QPointer<LabelEditorDlg> labelEditorDlg;

  const QString& projectFile() const;
  QHash<QString, QTreeWidgetItem*> stackFolders, meshFolders, ccFolders, miscFolders, dynamXFolders;

public:
  void setDebug(bool debug);

  // Launch a process
  bool LaunchProcess(const QString& processName, QStringList& parms, 
               mgx::Process::ProcessAction processAction, bool useGUI = true, bool saveDefaults = true);

  int activeStack() const;
  int activeMesh() const;
  QString getStackName(bool main, int num);
  QString getMeshName(int num);
  void setLoadFile(const QString& f) 
  {
    _loadFilename = f;
  }
  bool runningProcess() const 
  {
    return currentProcess != 0;
  }
  const mgx::Process* globalProcess() const;
  mgx::Process* globalProcess();
  static void writeProcessParms(const mgx::ProcessDefinition& def, QTextStream& pout);

signals:
  void processFinished();
  void processFailed();

private:
  void loadControls();
  void connectControlsIn();
  void connectControlsOut();
  void readParms();
  void writeParms();
  void clearMeshSelect();
  void updateVBOs();
  void readProcessParms();
  void createSetupProcess();
  void updateCurrentTasks(const TaskEditDlg::tasks_t& saved_tasks);
  QTreeWidgetItem* getFolder(QString name, 
                   QHash<QString, QTreeWidgetItem*>& folders, QTreeWidget* tree);
  void createItem(const mgx::ProcessDefinition& def);
  void updateDefinitions(QMap<QString, mgx::ProcessDefinition>& procDefs,
                         mgx::ProcessFactoryPtr factory, const 
                              QMap<QString, mgx::ProcessDefinition>& oldProcDefs);
  void setProjectFile(const QString &f);

  void updateViewer();
  void loadView(mgx::Process *proc, QString fileName);
  void saveView(mgx::Process *proc, QString fileName);
  void setCurrentStack(mgx::Process *proc, const QString &store, int id);
  void takeSnapshot(mgx::Process *proc, QString fileName, 
        float overSampling, int width, int height, int quality, bool expandFrustum);
  void updateStateFromProcess(mgx::Process* proc);
  void setStatus(QString fileName, bool alsoPrint = true);

  bool setFrameVis(const QString &name, ulong flags, const mgx::Point3d &pos, const mgx::Quaternion &orient); 
  bool setCameraVis(const mgx::Point3d &pos, const mgx::Quaternion &orient, const mgx::Point3d &center, double zoom); 

public slots:

  // Process communication
  void systemCommand(mgx::Process *proc, int command, QStringList parms);

  void modified(bool state = true);
  void storeParameters();
  void ImportMesh(int stack);
  void ExportMesh(int stack);
  void ResetMesh(int mesh);
  void LoadMesh(int mesh);
  void SaveMesh(int mesh);
  void ImportStack(int stack, bool main);
  void ImportStackSeries(int stack, bool main);
  void OpenStack(int stack, bool main);
  void SaveStack(int stack, bool main);
  void ExportStack(int stack, bool main);
  void updateCurrentStack();

  // Shortcuts
  void toggleControlsTab();
  void toggleStackTabs();
  void toggleControlKeyInteraction();
  void toggleMeshViewDropdown();
  void toggleMeshSurfaceView();
  void toggleMainCheckBox();
  void toggleWorkCheckBox();

  //Attributes
  void ManageAttributesMesh(int mesh);

  // Open session file
  void FileOpen(const QString& filename);
  void setModelPath(const QString& path);
  // Open any file, using extension to figure out which file type
  void AutoOpen(const QString& filename = QString(), int stack = 0, bool main = true);
  void AutoRunSlot();
  void resetDefaultParameters();
  void editTasks();
  void ReloadTasks();
  void DebugDialogSlot();
  void ResetCutSurf();
  void SetCutSurfControl();
  void ResetClipControl(float sceneRadius);
  void changeSelectLabel(int lab);
  void UpdateSliderScaleSlot();

private slots:
  void stack1UnloadedSlot();
  void stack2UnloadedSlot();
  void changeStack1SizeSlot(const Point3u& size, const Point3f& step, const Point3f& origin);
  void changeStack2SizeSlot(const Point3u& size, const Point3f& step, const Point3f& origin);

  void RotateSlot(bool checked);

  void ProcessCommandSlot(QTreeWidgetItem* current, QTreeWidgetItem* prev);
  void ProcessTasksCommandSlot(QTreeWidgetItem* current, QTreeWidgetItem* prev);
  void SeedStackSlot(bool val);
  void VoxelEditRadiusSlot(int val) {
    mgx::ImgData::VoxelEditRadius = val;
  }
  void FillWorkDataSlot(bool val) {
    mgx::ImgData::FillWorkData = val;
  }

  void LoadView(const QString& filename);
  void SaveView(const QString& filename);

  void FileEditPathsSlot();
  void FileOpenSlot();
  void FileSaveSlot();
  void FileSaveAsSlot();
  void FileQuickSaveSlot();
  void ExitSlot();

  void GlobalContrastSlot();
  void GlobalBrightnessSlot();
  void GlobalShininessSlot();
  void GlobalSpecularSlot();

  void Stack1ImportSlot();
  void Stack1ImportSeriesSlot();
  void Stack1SaveSlot();
  void Stack1OpenSlot();
  void Stack1ExportSlot();
  void Stack1ResetSlot();
  void Stack2ImportSlot();
  void Stack2ImportSeriesSlot();
  void Stack2SaveSlot();
  void Stack2OpenSlot();
  void Stack2ExportSlot();
  void Stack2ResetSlot();

  void Stack1WorkImportSlot();
  void Stack1WorkImportSeriesSlot();
  void Stack1WorkSaveSlot();
  void Stack1WorkOpenSlot();
  void Stack1WorkExportSlot();
  void Stack2WorkImportSlot();
  void Stack2WorkImportSeriesSlot();
  void Stack2WorkSaveSlot();
  void Stack2WorkOpenSlot();
  void Stack2WorkExportSlot();

  void Mesh1ImportSlot();
  void Mesh1ExportSlot();
  void Mesh1LoadSlot();
  void Mesh1SaveSlot();
  void Mesh1ResetSlot();
  void Mesh2ImportSlot();
  void Mesh2ExportSlot();
  void Mesh2LoadSlot();
  void Mesh2SaveSlot();
  void Mesh2ResetSlot();

  void MeshSelectAll();
  void MeshUnselect();
  void MeshInvertSelection();
  void MeshAddCurrentLabel();
  void MeshAddUnlabeled();
  void MeshRemoveCurrentLabel();
  void ChangeCurrentSeed();

  void Mesh1ManageAttributes();
  void Mesh2ManageAttributes();

  void UserManualSlot();
  void DoxygenSlot();
  void QGLViewerHelpSlot();
  void AboutSlot();

  void LabelColorSlot();
  void LabelColorSlot(QIcon& icon);

  void on_actionAddNewSeed_triggered(bool on);
  void on_actionAddCurrentSeed_triggered(bool on);
  void on_actionDrawSignal_triggered(bool on);
  void on_actionPickLabel_triggered(bool on);
  void on_actionGrabSeed_triggered(bool on);
  void on_actionFillLabel_triggered(bool on);
  void on_actionPickFillLabel_triggered(bool on);
  void on_actionPickFillParent_triggered(bool on);  
  void on_actionMeshSelect_toggled(bool on);
  void on_actionLassoSelect_toggled(bool on);
  void on_actionTriangleSelect_toggled(bool on);
  void on_actionLabelSelect_triggered(bool on);
  void on_actionConnectedSelect_triggered(bool on);

  void on_actionPickVolLabel_triggered(bool on);
  void on_actionPickFillVolumeLabel_triggered(bool on);
  void on_actionFillVolumeLabel_triggered(bool on);
  void on_actionVoxelEdit_triggered(bool on);
  void on_actionDeleteVolumeLabel_triggered(bool on);

  void on_actionRewind_triggered(bool on);
  void on_actionStop_triggered(bool on);
  void on_actionStep_triggered(bool on);
  void on_actionRun_triggered(bool on);

  void EraseSelectSlot();
  void FillSelectSlot();
  void DeleteSelectionSlot();
  void EditParmsSlot();
  void ProcessDocsSlot();
  void ColorPaletteSlot();
  void LabelsEditSlot(bool on);
  void EditParmsSaveAsSlot();
  void SwapSurfacesSlot();
  void ProgressStopSlot();

  void ProcessCommandTabSlot(int tab);

  void SaveProcessesParameters();
  void RecallProcessesParameters();
  void ReloadProcesses();
  void ProcessCommandFinished();

protected:
  void dragEnterEvent(QDragEnterEvent* event);
  void dropEvent(QDropEvent* event);
  void closeEvent(QCloseEvent* e);
  void saveSettings();

  QTime currentProcessTime;
  QString _loadFilename;
};

#endif
