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

#include <Process.hpp>

namespace mgx 
{
  
  ///\addtogroup StackProcess
  ///@{
  /**
   * \class AlignCanvas MergeStacks.hpp <MergeStacks.hpp>
   *
   * Align two stacks' canvas. Expand the canvas of the first stack so the
   * second one can be drawn onto it. If the 'change both stack' option is
   * selected, then all the stacks are extended and made aligned with the axis
   * of the reference system. Otherwise, the work store of the current stack is
   * replaced with the projection of the other stack onto the extended canvas,
   * keeping the resolution of the current stack.
   */
  class mgxBase_EXPORT AlignCanvas : public Process
  {
  public:
    AlignCanvas(const Process& process) : Process(process) 
    {
      setName("Stack/MultiStack/Align Canvas");
      setDesc("Align two stacks' canvas. Expand the canvas of the first stack so the\n"
      "second one can be drawn onto it. If the 'change both stack' option is\n" 
      "selected, then all the stacks are extended and made aligned with the\n" 
      "axis of the reference system. Otherwise, the work store of the current\n"
      "stack is replaced with the projection of the other stack onto the\n"
      "extended canvas, keeping the resolution of the current stack.");
      setIcon(QIcon(":/images/AlignCanvas.png"));
      
      addParm("Change both stacks","Extend the non-active stacks canvas as well","True",booleanChoice());
      addParm("Interpolation","How to resample data points","Linear", QStringList() << "Linear" << "Nearest");		
	}
  
    bool run()
    {
      if(not checkState().store(STORE_NON_EMPTY | STORE_VISIBLE, 0)
                                    .store(STORE_NON_EMPTY | STORE_VISIBLE, 1))
        return false;
      Stack* current = currentStack();
      Stack* other = stack(1 - current->id());
      if(current->currentStore()->labels() != other->currentStore()->labels())
        return setErrorMessage("Error, one stack is labeled and the other is not.");
      bool change_both = stringToBool(parm("Change both stacks"));
      bool result = run(current, other, change_both, parm("Interpolation"));
      if(not change_both and result)
        current->work()->show();
      return result;
    }
  
    bool run(Stack* target, Stack* other, bool change_both, QString interp = "Linear"); 
    bool projectGlobal(Stack* s1, Stack* s2, QString interp = "Linear");
    bool projectOnStack(Stack* target, const Stack* to_project, QString interp = "Linear");
  };
  
  /**
   * \class CombineStacks MergeStacks.hpp <MergeStacks.hpp>
   *
   * Combine the values of the main and work store onto the work store.
   */
  class mgxBase_EXPORT CombineStacks : public Process
  {
  public:
    CombineStacks(const Process& process) : Process(process) 
    {
	  setName("Stack/MultiStack/Combine Stacks");
	  setDesc("Combine the values of the main and work store onto the work store.");
	  setIcon(QIcon(":/images/CombineStacks.png"));
	  
	  addParm("Method","Method","Average", QStringList() << "Max" << "Min" << "Average" << "Product" << "Add" << "Subtract");		
	}
  
    bool run()
    {
      if(not checkState().store(STORE_NON_EMPTY | STORE_VISIBLE | STORE_MAIN)
                                .store(STORE_NON_EMPTY | STORE_VISIBLE | STORE_WORK))
        return false;
      Stack* current = currentStack();
      if(current->main()->labels() != current->work()->labels())
        return setErrorMessage("Error, one store is labeled and the other is not.");
      return run(current, parm("Method"));
    }
  
    bool run(Stack* target, QString method);

  };
  
  /**
   * \class MergeStacks MergeStacks.hpp <MergeStacks.hpp>
   *
   * Merge the main store of the current stack with the current store of the other one.
   * The current stack will be aligned with the other before the stores being combines.
   * The method argument is simply passed to the Combine_Stacks process.
   */
  class mgxBase_EXPORT MergeStacks : public Process
  {
  public:
    MergeStacks(const Process& process) : Process(process) 
    {
	  setName("Stack/MultiStack/Merge Stacks");
	  setDesc("Merge the main store of the current stack with the current store of the other one.\n"
	  "The current stack will be resampled if required.");
	  setIcon(QIcon(":/images/MergeStacks.png"));
	  
	  addParm("Method","Method","max", QStringList() << "max" << "min" << "average" << "product");
	  addParm("Interpolation","Interpolation","Linear", QStringList() << "Linear" << "Nearest");		
	}
  
    bool run()
    {
      if(not checkState().store(STORE_NON_EMPTY | STORE_VISIBLE, 0)
                                  .store(STORE_NON_EMPTY | STORE_VISIBLE, 1))
        return false;
      Stack* current = currentStack();
      Stack* other = stack(1 - current->id());
      if(current->currentStore() != current->main())
        return setErrorMessage("Error, the current stack must have its main store active.");
      if(current->currentStore()->labels() != other->currentStore()->labels())
        return setErrorMessage("Error, one stack is labeled and the other is not.");
      bool result = run(current, other, parm("Method"), parm("Interpolation"));
      if(result)
        current->work()->show();
      return result;
    }
  
    bool run(Stack* target, const Stack* other, QString method, QString interp = "Linear");
  
  };
///@}
}
#endif
