//
// 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 STACKPROCESSLABELS_HPP
#define STACKPROCESSLABELS_HPP

#include <Process.hpp>

namespace mgx 
{
  ///\addtogroup StackProcess
  ///@{
  /**
   * \class WatershedStack ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Compute the seeded watershed of the current stack using the CImg library.
   */
  class mgxBase_EXPORT WatershedStack : public Process 
  {
  public:
    WatershedStack(const Process& process) : Process(process) 
    {
	  setName("Stack/Segmentation/Watershed3D");
      setDesc("3D Watershed on the current labeled stack.");	
      setIcon(QIcon(":/images/SegmentMesh.png"));
	}
  
    bool run()
    {
      if(!checkState().store(STORE_MAIN | STORE_VISIBLE | STORE_NON_LABEL)
          .store(STORE_WORK | STORE_VISIBLE | STORE_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* main = stack->main();
      Store* labels = stack->work();
      return run(stack, main, labels);
    }
  
    bool run(Stack* stack, Store* main, Store* labels);
  };
  
  /**
   * \class ConsolidateRegions ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Try to merge segmented regions based on their contact area and the total voxel
   * intensity at their boundary.
   */
  class mgxBase_EXPORT ConsolidateRegions : public Process 
  {
  public:
    ConsolidateRegions(const Process& process) : Process(process) 
    {
		
      setName("Stack/Segmentation/Consolidate Regions");
      setDesc("Consilodate regions after watershed overseeding");
      setIcon(QIcon(":/images/Consolidate.png"));
      
      addParm("Threshold","Threshold","5000");
      addParm("Min Voxels","Min Voxels","100");		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_MAIN | STORE_VISIBLE | STORE_NON_LABEL)
                 .store(STORE_WORK | STORE_VISIBLE | STORE_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* main = stack->main();
      Store* labels = stack->work();
      return run(main, labels, parm("Threshold").toUInt(), parm("Min Voxels").toUInt());
    }
  
    bool run(Store* data, Store* labels, uint threshold, uint minvoxels);
  
  };
  
  /**
   * \class ConsolidateRegionsNormalized ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Variation on the ConsolidateRegions process.
   */
  class mgxBase_EXPORT ConsolidateRegionsNormalized : public Process 
  {
  public:
    ConsolidateRegionsNormalized(const Process& process) : Process(process) 
    {
	  setName("Stack/Segmentation/Consolidate Regions Normalized");
      setDesc("Consolidate regions with normalization (slower)");
      setIcon(QIcon(":/images/Consolidate.png")); 
      
      addParm("Tolerance","Tolerance","0.3");		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_MAIN | STORE_VISIBLE | STORE_NON_LABEL)
                    .store(STORE_WORK | STORE_VISIBLE | STORE_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* main = stack->main();
      Store* labels = stack->work();
      return run(main, labels, parm("Tolerance").toFloat());
    }
  
    bool run(Store* data, Store* labels, float tolerance);
    
  };
  
  /**
   * \class ThresholdLabelDelete ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Delete labels that have too few or too many voxels.
   */
  class mgxBase_EXPORT ThresholdLabelDelete : public Process 
  {
  public:
    ThresholdLabelDelete(const Process& process) : Process(process) 
    {
		
      setName("Stack/Segmentation/Delete Labels by Threshold");
      setDesc("Delete Labels above/below voxel thresholds");
      setIcon(QIcon(":/images/DeleteLabel.png"));
      
      addParm("Min voxels","Min voxels","1000");
      addParm("Max voxels","Max voxels","0");			
	}
  
    bool run()
    {
      if(!checkState().store(STORE_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* input = stack->currentStore();
      Store* output = stack->work();
      if(!input)
        return false;
      bool res = run(input, output, parm("Min voxels").toInt(), parm("Max voxels").toInt());
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    bool run(const Store* input, Store* labels, uint minVoxels, uint maxVoxels);
  };
  
  /**
   * \class LocalMaximaStack ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Find the local maxima of the current image
   *
   * \note If the algorithm finds more than 65535 maxima, it will fail as it
   * cannot number them all.
   */
  class mgxBase_EXPORT LocalMaximaStack : public Process 
  {
  public:
    LocalMaximaStack(const Process& process) : Process(process) 
    {
      setName("Stack/Segmentation/Local Maxima");
      setDesc("Find local maxima and possibly number them");
      setIcon(QIcon(":/images/LocalMaxima.png"));
      
      addParm("X Radius","X Radius (µm)","5.0");
      addParm("Y Radius","Y Radius (µm)","5.0");
      addParm("Z Radius","Z Radius (µm)","5.0");
      addParm("Start Label","Start Label","2");
      addParm("Threshold", "Only consider voxel values larger than this value as maxima", "10000");		
      addParm("Value", "Value to set voxel if not labeling", "60000");		
	}
	
    bool run()
    {
      if(!checkState().store(STORE_NON_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* input = stack->currentStore();
      Store* output = stack->work();
      if(!input)
        return false;
      Point3f radius(parm("X Radius").toFloat(), parm("Y Radius").toFloat(), parm("Z Radius").toFloat());
      uint label = parm("Start Label").toUInt();
      uint threshold = parm("Threshold").toUInt();
      uint value = parm("Value").toUInt();
      bool res = run(input, output, radius, label, threshold, value);
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    bool run(const Store* input, Store* labels, Point3f radius, uint startLabel, uint threshold, uint value);
  };
  
  /**
   * \class FillLabelStack ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Replace one label by another one on the stack
   */
  class mgxBase_EXPORT FillLabelStack : public Process 
  {
  public:
    FillLabelStack(const Process& process) : Process(process) 
    { 
	  setName("Stack/Segmentation/Fill Label");
      setDesc("Replace a label with another one");
      setIcon(QIcon(":/images/FillLabel.png"));
      
      addParm("Filled label","Filled label","1000");
      addParm("New label","New label","0");					
	}
  
    bool run()
    {
      if(!checkState().store(STORE_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* input = stack->currentStore();
      Store* output = stack->work();
      if(!input)
        return false;
      bool res = run(input, output, parm("Filled label").toUShort(), parm("New label").toUShort());
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    bool run(const Store* input, Store* output, ushort filledLabel, ushort newLabel);
  
  };
  
  /**
   * \class EraseAtBorderStack ProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Erase any label touching the border of the image.
   */
  class mgxBase_EXPORT EraseAtBorderStack : public Process 
  {
  public:
    EraseAtBorderStack(const Process& process) : Process(process) 
    {
      setName("Stack/Segmentation/Erase at Border");
      setDesc("Erase any labelled region touching the border of the image");		
	  setIcon(QIcon(":/images/EraseLabel.png"));		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_LABEL))
        return false;
      Stack* stack = currentStack();
      Store* input = stack->currentStore();
      Store* output = stack->work();
      if(!input)
        return false;
      bool res = run(input, output);
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    bool run(const Store* input, Store* output);

  };

  
  /**
   * \class StackExportByLabel StackProcessLabels.hpp <StackProcessLabels.hpp>
   *
   * Export stack to individual files, one per label
   */
  class mgxBase_EXPORT StackExportByLabel : public Process
  {
  public:
    StackExportByLabel(const Process& process) : Process(process) 
    {
	  setName("Stack/Segmentation/Export by Label");
      setDesc(" Export stack to individual files, one per label");

      addParm("File Prefix","Prefix for file name","Cell");	// 0				
	}

    //bool initialize(QStringList& parms, QWidget* parent);

    bool run();

  };
  ///@}
}

#endif
