/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbWrapperCommandLineLauncher.h"

// Single value parameter
#include "otbWrapperChoiceParameter.h"
#include "otbWrapperDirectoryParameter.h"
#include "otbWrapperInputFilenameParameter.h"
#include "otbWrapperOutputFilenameParameter.h"
#include "otbWrapperInputImageParameter.h"
#include "otbWrapperInputVectorDataParameter.h"
#include "otbWrapperOutputImageParameter.h"
#include "otbWrapperOutputVectorDataParameter.h"
#include "otbWrapperNumericalParameter.h"
#include "otbWrapperListViewParameter.h"
#include "otbWrapperOutputProcessXMLParameter.h"
#include "otbWrapperAddProcessToWatchEvent.h"

// List value parameter
#include "otbWrapperInputImageListParameter.h"
#include "otbWrapperInputVectorDataListParameter.h"
#include "otbWrapperInputFilenameListParameter.h"
#include "otbWrapperStringListParameter.h"


#include "otbWrapperApplicationRegistry.h"
#include "otbWrapperTypes.h"
#include <itksys/RegularExpression.hxx>
#include <string>
#include <iostream>

using std::string;

namespace otb
{
namespace Wrapper
{

CommandLineLauncher::CommandLineLauncher() :
  /*m_Expression(""),*/m_VExpression(), m_WatcherList(), m_ReportProgress(true)
{
  m_Application = nullptr;
  m_Parser = CommandLineParser::New();
  m_LogOutput = itk::StdStreamLogOutput::New();
  m_LogOutput->SetStream(std::cout);

  // Add the callback to be added when a AddProcessToWatch event is invoked
  m_AddProcessCommand = AddProcessCommandType::New();
  m_AddProcessCommand->SetCallbackFunction(this, &CommandLineLauncher::LinkWatchers);
}

CommandLineLauncher::~CommandLineLauncher()
{
  this->DeleteWatcherList();
  m_Application = nullptr;
  ApplicationRegistry::CleanRegistry();
}

void CommandLineLauncher::DeleteWatcherList()
{
  for (unsigned int i = 0; i < m_WatcherList.size(); i++)
    {
    delete m_WatcherList[i];
    m_WatcherList[i] = nullptr;
    }
  m_WatcherList.clear();
}


bool CommandLineLauncher::Load(const std::vector<std::string> &vexp)
{
  m_VExpression = vexp;
  return this->Load();
}


bool CommandLineLauncher::Load()
{
  if (this->CheckParametersPrefix() == false)
    {
    std::cerr << "ERROR: Parameters are set using \"-\", not \"--\"." << std::endl;
    return false;
    }

  if (this->CheckUnicity() == false)
    {
    std::cerr << "ERROR: At least one parameter is not unique in the expression." << std::endl;
    return false;
    }

  if (this->LoadPath() == false)
    {
    if (m_Parser->GetPathsAsString(m_VExpression).size() != 0)
      {
      std::cerr << "ERROR: At least one specified path within \"" << m_Parser->GetPathsAsString(m_VExpression)
                << "\" is invalid or doesn't exist." << std::endl;
      return false;
      }
    }

  return this->LoadApplication();
}

bool CommandLineLauncher::Execute()
{
  if (this->BeforeExecute() == false)
    {
    return false;
    }

  if( m_Application->Execute() == 0 )
    {
    this->DisplayOutputParameters();
    return true;
    }
  else
    return false;
}

bool CommandLineLauncher::ExecuteAndWriteOutputNoCatch()
{
   if (this->BeforeExecute() == false)
      {
      return false;
      }
    if( m_Application->ExecuteAndWriteOutput() == 0 )
      {
      this->DisplayOutputParameters();
      }
    else
      {
      return false;
      }
    return true;
}

bool CommandLineLauncher::ExecuteAndWriteOutput()
{
  // If testenv is used, do not catch exceptions
  if (m_Parser->IsAttributExists("-testenv", m_VExpression) == true)
    {
    return ExecuteAndWriteOutputNoCatch();
    }
  else
    {
    try
      {
      return ExecuteAndWriteOutputNoCatch();
      }
    catch(otb::ApplicationException& err)
      {
      // These are thrown with otbAppLogFATAL, a macro which logs a user
      // friendly error message before throwing. So log exception details only
      // in debug.
      m_Application->GetLogger()->Debug("Caught otb::ApplicationException during application execution:\n");
      m_Application->GetLogger()->Debug(string(err.what()) + "\n");
      return false;
      }
    catch(otb::ImageFileReaderException& err)
      {
      m_Application->GetLogger()->Debug("Caught otb::ImageFileReaderException during application execution:\n");
      m_Application->GetLogger()->Debug(string(err.what()) + "\n");
      m_Application->GetLogger()->Fatal(err.GetDescription() + string("\n"));
      return false;
      }
    catch(itk::ExceptionObject& err)
      {
      m_Application->GetLogger()->Debug("Caught itk::ExceptionObject during application execution:\n");
      m_Application->GetLogger()->Debug(string(err.what()) + "\n");
      m_Application->GetLogger()->Fatal(string(err.GetDescription()) + "\n");
      return false;
      }
    catch(std::exception& err)
      {
      m_Application->GetLogger()->Fatal(std::string("Caught std::exception during application execution: ") + err.what() + "\n");
      return false;
      }
    catch(...)
      {
      m_Application->GetLogger()->Fatal("Caught unknown exception during application execution.\n");
      return false;
      }
    }
}

bool CommandLineLauncher::BeforeExecute()
{
  if (m_Application.IsNull())
    {
    std::cerr << "ERROR: No loaded application." << std::endl;
    return false;
    }

  // Check if there's keys in the expression if the application takes
  // at least 1 mandatory parameter
  const std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
  std::vector<std::string> keyList = m_Parser->GetKeyList( m_VExpression );

  if( appKeyList.size()!=0 && keyList.size()==0 )
    {
    std::cerr << "ERROR: Waiting for at least one parameter." << std::endl;
    std::cerr << std::endl;
    this->DisplayHelp();
    return false;
    }

  // if help is asked...
  if (m_Parser->IsAttributExists("-help", m_VExpression) == true)
    {
    std::vector<std::string> val;
    val = m_Parser->GetAttribut("-help", m_VExpression);

    if(val.empty())
      {      
      this->DisplayHelp(true);
      }
    else
      {
     
      
      for(std::vector<std::string>::const_iterator it = val.begin(); it!=val.end();++it)
        {
        Parameter::Pointer param = m_Application->GetParameterByKey(*it);
        if (param->GetRole() != Role_Output)
          {
          std::cerr << this->DisplayParameterHelp(param, *it,true)<<std::endl;
          }
        }
      }
    return false;
    }

  //display OTB version
  if (m_Parser->IsAttributExists("-version", m_VExpression) == true)
    {
    std::cerr << "This is the "<<m_Application->GetName() << " application, version " << OTB_VERSION_STRING <<std::endl;
    return false;
    }

  // if we want to load test environnement
  if (m_Parser->IsAttributExists("-testenv", m_VExpression) == true)
    {
    this->LoadTestEnv();
    }

  // Check the key validity (ie. exist in the application parameters)
  std::string unknownKey;
  if (this->CheckKeyValidity(unknownKey) == false)
    {
    std::cerr << "ERROR: Parameter -" << unknownKey <<" does not exist in the application." << std::endl;
    return false;
    }

  ParamResultType result = this->LoadParameters();

  if (result == MISSINGMANDATORYPARAMETER)
    {
    std::cerr << std::endl;
    this->DisplayHelp();
    return false;
    }
  else if (result != OKPARAM)
    {
    return false;
    }

  if (m_Application->IsDeprecated())
  {
    m_Application->GetLogger()->Warning("This application is deprecated and will be removed in a future OTB release.\n");
  }

  return true;
}

bool CommandLineLauncher::LoadPath()
{
  std::vector<std::string> pathList;
  // If users has set path...
  //if (m_Parser->GetPaths(pathList, m_Expression) == CommandLineParser::OK)
  if (m_Parser->GetPaths(pathList, m_VExpression) == CommandLineParser::OK)
    {
    for (unsigned i = 0; i < pathList.size(); i++)
      {
      ApplicationRegistry::AddApplicationPath(pathList[i]);
      }
    }
  else
    {
    return false;
    }

  return true;
}

bool CommandLineLauncher::LoadApplication()
{
  // Look for the module name
  std::string moduleName;
  if (m_Parser->GetModuleName(moduleName, m_VExpression) != CommandLineParser::OK)
    {
    std::cerr << "ERROR: Invalid module name: " << m_VExpression[0] << "." << std::endl;
    return false;
    }

  // Instantiate the application using the factory
  m_Application = ApplicationRegistry::CreateApplication(moduleName);

  if (m_Application.IsNull())
    {
    std::cerr << "ERROR: Could not find application \"" << moduleName << "\"" << std::endl;
    std::string modulePath = ApplicationRegistry::GetApplicationPath();
    std::cerr << "ERROR: Module search path: " << (modulePath.empty() ? "none (check OTB_APPLICATION_PATH)" : modulePath) << std::endl;

    std::vector<std::string> list = ApplicationRegistry::GetAvailableApplications();
    if (list.size() == 0)
      {
      std::cerr << "ERROR: Available modules: none." << std::endl;
      }
    else
      {
      std::cerr << "ERROR: Available modules:" << std::endl;
      for (std::vector<std::string>::const_iterator it = list.begin(); it != list.end(); ++it)
        {
        std::cerr << "\t" << *it << std::endl;
        }
      }
    return false;
    }
  else
    {
    // Attach log output to the Application logger
    m_Application->GetLogger()->AddLogOutput(m_LogOutput);

    // Add an observer to the AddedProcess event
    m_Application->AddObserver(AddProcessToWatchEvent(), m_AddProcessCommand.GetPointer());

    return true;
    }
}

CommandLineLauncher::ParamResultType CommandLineLauncher::LoadParameters()
{
  if (m_Application.IsNull())
    {
    itkExceptionMacro("No application loaded");
    }

  /* Check for inxml parameter. If exists Update all Parameters from xml and
   * check for user defined parameters for overriding those from XML
   */
  const char *inXMLKey =  "inxml";
  const char *attrib   = "-inxml";
  const bool paramInXMLExists(m_Parser->IsAttributExists(attrib, m_VExpression));
  if(paramInXMLExists)
    {
    std::vector<std::string> inXMLValues;
    inXMLValues = m_Parser->GetAttribut(attrib, m_VExpression);
    m_Application->SetParameterString(inXMLKey, inXMLValues[0]);
    m_Application->UpdateParameters();
    }

  // Check for the progress report parameter
  if (m_Parser->IsAttributExists("-progress", m_VExpression) == true)
    {
    std::vector<std::string> val = m_Parser->GetAttribut("-progress", m_VExpression);
    if (val.size() == 1 && (val[0] == "1" || val[0] == "true"))
      {
      m_ReportProgress = true;
      }
    else if (val.size() == 1 && (val[0] == "0" || val[0] == "false"))
      {
      m_ReportProgress = false;
      }
    else
      {
      std::cerr << "ERROR: Invalid value for parameter -progress. It must be 0, 1, false or true." << std::endl;
      return WRONGPARAMETERVALUE;
      }
    }

  const std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
  // Loop over each parameter key declared in the application
  // FIRST PASS : set parameter values
  for (unsigned int i = 0; i < appKeyList.size(); i++)
    {
    const std::string paramKey(appKeyList[i]);
    std::vector<std::string> values;

    ParameterType type = m_Application->GetParameterType(paramKey);

    const bool paramExists(m_Parser->IsAttributExists(std::string("-").append(paramKey), m_VExpression));

    // if param is a Group, don't do anything, ParamGroup don't have values
    if (type != ParameterType_Group)
      {
      // Get the attribute relative to this key as vector
      values = m_Parser->GetAttribut(std::string("-").append(paramKey), m_VExpression);

      // If the param does not exists in the cli, don't try to set a
      // value on it, an exception will be thrown later in this function
      if (paramExists)
        {
        // Check if there is a value associated to the attribute
        if ( values.empty() )
          {
          std::cerr << "ERROR: No value associated to parameter -" << paramKey << "." << std::endl;
          return INVALIDNUMBEROFVALUE;
          }

        if (type == ParameterType_InputVectorDataList ||
            type == ParameterType_InputImageList ||
            type == ParameterType_InputFilenameList ||
            type == ParameterType_StringList ||
            type == ParameterType_ListView)
          {
          // Multiple values parameters
          m_Application->SetParameterStringList(paramKey, values);
          }
        else if (type == ParameterType_Choice ||
                 type == ParameterType_Float ||
                 type == ParameterType_Int ||
                 type == ParameterType_Radius ||
                 type == ParameterType_Directory ||
                 type == ParameterType_String ||
                 type == ParameterType_InputFilename ||
                 type == ParameterType_OutputFilename ||
                 type == ParameterType_InputImage ||
                 type == ParameterType_OutputImage ||
                 type == ParameterType_InputVectorData ||
                 type == ParameterType_OutputVectorData ||
                 type == ParameterType_RAM ||
                 type == ParameterType_OutputProcessXML ||
                 type == ParameterType_Bool) // || type == ParameterType_InputProcessXML)
          {
          // Single value parameter
          m_Application->SetParameterString(paramKey, values[0]);

          if (type == ParameterType_OutputImage)
            {
            // Check if pixel type is given
            if (values.size() == 2)
              {
              ImagePixelType pixType = ImagePixelType_float;
              if ( !OutputImageParameter::ConvertStringToPixelType(values[1],pixType) )
                {
                std::cerr << "ERROR: Invalid output type for parameter -" <<
                  paramKey << ": " << values[1] << "." << std::endl;
                return WRONGPARAMETERVALUE;
                }
              m_Application->SetParameterOutputImagePixelType(paramKey, pixType);
              }
            else if (values.size() > 2)
              {
              std::cerr << "ERROR: Too many values for parameter -" <<
                paramKey << " (expected 2 or 1, got " << values.size() << ")."
                        << std::endl;
              return INVALIDNUMBEROFVALUE;
              }
            }
          }
        // Call the DoUpdateParameter to update dependent params
        m_Application->UpdateParameters();
        }
      }
    }

  // SECOND PASS : checks
  for (const auto & paramKey : appKeyList)
    {
    // Check for missing mandatory parameters
    if(!CheckMissingMandatoryParameter(paramKey))
      return MISSINGMANDATORYPARAMETER;

    // Check and warn unused parameters
    CheckUnusedParameter(paramKey);

    // Check output paths are valid
    if(!CheckOutputPathsValidity(paramKey))
      return WRONGPARAMETERVALUE;
    }

  return OKPARAM;
}

bool CommandLineLauncher::CheckOutputPathsValidity(const std::string & paramKey) const
{
  ParameterType type = m_Application->GetParameterType(paramKey);
  if (m_Application->HasValue(paramKey) &&
      type == ParameterType_OutputFilename)
    {
    std::string filename = m_Application->GetParameterString(paramKey);
    itksys::String path = itksys::SystemTools::GetFilenamePath(filename);
    if (path!="" && !itksys::SystemTools::FileIsDirectory(path))
      {
      std::cerr <<"ERROR: Directory doesn't exist : "<< path << std::endl;
      return false;
        }
      }
  return true;
}

bool CommandLineLauncher::CheckMissingMandatoryParameter(const std::string & paramKey) const
{
    if (m_Application->IsParameterMissing(paramKey))
      {
      std::cerr << "ERROR: Missing mandatory parameter -" << paramKey << "." << std::endl;
      return false;
      }
    return true;
}

void CommandLineLauncher::CheckUnusedParameter(const std::string & paramKey) const
{
  // Check for ignored parameters
    if(m_Application->HasUserValue(paramKey))
      {

      // Find the position of the next dot
      unsigned int start = 0;
      auto end = paramKey.find_first_of('.',start);

      // Until we reach en of key
      while(end != std::string::npos)
        {
        // Extract key until the current dot
        const std::string & key = paramKey.substr(0,end);

        // Find the corresponding parameter type
        ParameterType type = m_Application->GetParameterType(key);

        // In any case update the position of current and next dot
        start = end+1;
        end = paramKey.find_first_of('.',start);

        // If the current parameter (paramKey) has a choice in it
        if(type == ParameterType_Choice)
          {
          const std::string & value = m_Application->GetParameterString(key);

          // Check that this choice is active
          if(paramKey.find(value) == std::string::npos)
            {
            std::cerr<<"WARNING: Parameter -"<<paramKey<<" will be ignored because -"<<key<<" is "<<value<<"."<<std::endl;
            
            break;
            }
          }
        }
      }
}

void CommandLineLauncher::LinkWatchers(itk::Object * itkNotUsed(caller), const itk::EventObject & event)
{
  // Report the progress only if asked
  if (m_ReportProgress)
    {

    if (typeid(otb::Wrapper::AddProcessToWatchEvent) == typeid( event ))
      {
      const AddProcessToWatchEvent* eventToWatch = dynamic_cast<const AddProcessToWatchEvent*> (&event);

      auto watch = new StandardOneLineFilterWatcher<>(eventToWatch->GetProcess(),
                                                                              eventToWatch->GetProcessDescription());
      m_WatcherList.push_back(watch);
      }
    }
}

void CommandLineLauncher::DisplayHelp(bool longHelp)
{
  std::cerr<<std::endl;
  std::cerr << "This is the "<<m_Application->GetDocName() << " ("<<m_Application->GetName()<<") application, version " << OTB_VERSION_STRING <<std::endl<<std::endl;

  if (m_Application->IsDeprecated())
  {
    std::cerr << "WARNING: This application is deprecated, it will be removed in a future OTB release." << std::endl;
    std::cerr << std::endl;
  }

  std::cerr << m_Application->GetDescription() << std::endl;

  if(longHelp)
    { 
    std::cerr<<"Tags: ";

    std::vector<std::string> tags = m_Application->GetDocTags();
    
    for(std::vector<std::string>::const_iterator it = tags.begin(); it!=tags.end();++it)
      {
      std::cerr<<*it<<" ";
      }
    std::cerr<<std::endl;
  
    std::cerr<<std::endl;
    std::cerr<<m_Application->GetDocLongDescription() << std::endl;
    std::cerr<<std::endl;
    }
  else
    {
    std::string link = m_Application->GetDocLink();
    if (!link.empty())
      {
      std::cerr << "Complete documentation: " << link << " or -help" <<std::endl;
      std::cerr<<std::endl;
      }
    }

  std::cerr << "Parameters: " << std::endl;

  const std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
  const unsigned int nbOfParam = appKeyList.size();

  unsigned int maxKeySize = GetMaxKeySize();

  //// progress report parameter
  std::string bigKey = "progress";
  for(unsigned int i=0; i<maxKeySize-std::string("progress").size(); i++)
    bigKey.append(" ");

  for (unsigned int i = 0; i < nbOfParam; i++)
    {
    Parameter::Pointer param = m_Application->GetParameterByKey(appKeyList[i]);
    if (param->GetRole() != Role_Output)
      {
      std::cerr << this->DisplayParameterHelp(param, appKeyList[i]);
      }
    }

  std::cerr << "        -"<<bigKey<<" <boolean>        Report progress " << std::endl;
  bigKey = "help";
  for(unsigned int i=0; i<maxKeySize-std::string("help").size(); i++)
    bigKey.append(" ");
  std::cerr << "        -"<<bigKey<<" <string list>    Display long help (empty list), or help for given parameters keys" << std::endl;

  std::cerr<<std::endl;
  //std::string cl(m_Application->GetCLExample());


  std::cerr<<"Use -help param1 [... paramN] to see detailed documentation of those parameters."<<std::endl;
  std::cerr<<std::endl;
  
  std::cerr << "Examples: " << std::endl;
  std::cerr << m_Application->GetCLExample() << std::endl;

  if(longHelp)
    {
    std::cerr<<"Authors: "<<std::endl<<m_Application->GetDocAuthors() << std::endl;

    std::cerr<<std::endl;
    std::cerr<<"Limitations: "<<std::endl<<m_Application->GetDocLimitations() << std::endl;

    std::cerr<<std::endl;
    std::cerr<<"See also: "<<std::endl<<m_Application->GetDocSeeAlso() << std::endl;
    std::cerr<<std::endl;

    }  

}

void CommandLineLauncher::LoadTestEnv()
{
  //Set seed for rand and itk mersenne twister
  //srand(1);
  // itk::Statistics::MersenneTwisterRandomVariateGenerator::GetInstance()->SetSeed(121212);
}


std::string CommandLineLauncher::DisplayParameterHelp(const Parameter::Pointer & param, const std::string paramKey, bool longHelp)
{
  // Display the type the type
  ParameterType type = m_Application->GetParameterType(paramKey);

  std::string bigKey = paramKey;
  
  unsigned int maxKeySize = GetMaxKeySize();

  bool singleSelectionForListView = false;

  if(type == ParameterType_ListView)
    {
    ListViewParameter * tmp = static_cast<ListViewParameter *>(param.GetPointer());
    singleSelectionForListView = tmp->GetSingleSelection();
    }

  std::ostringstream oss;

  if( m_Application->IsParameterMissing(paramKey) )
    {
    oss << "MISSING ";
    }
  else
    {
    oss << "        ";
    }
  
  for(unsigned int i=0; i<maxKeySize-paramKey.size(); i++)
    bigKey.append(" ");

  oss<< "-" << bigKey << " ";

  // Display the type the parameter
  if (type == ParameterType_Radius || type == ParameterType_Int || type == ParameterType_RAM)
    {
    oss << "<int32>         ";
    }
  else if (type == ParameterType_Bool)
    {
    oss << "<boolean>       ";
    }
  else if (type == ParameterType_Float)
    {
    oss << "<float>         ";
    }
  else if (type == ParameterType_InputFilename || type == ParameterType_OutputFilename ||type == ParameterType_Directory || type == ParameterType_InputImage || type == ParameterType_OutputProcessXML || type == ParameterType_InputProcessXML ||
          type == ParameterType_InputVectorData || type == ParameterType_OutputVectorData || type == ParameterType_String || 
          type == ParameterType_Choice || (type == ParameterType_ListView && singleSelectionForListView))
    {
    oss << "<string>        ";
    }
  else if (type == ParameterType_OutputImage)
    {
    oss << "<string> [pixel]";
    }
  else if (type == ParameterType_Choice || (type == ParameterType_ListView && !singleSelectionForListView) || type == ParameterType_InputImageList ||
           type == ParameterType_InputVectorDataList || type == ParameterType_InputFilenameList ||
           type == ParameterType_StringList )
    {
    oss << "<string list>   ";
    }
  else if(type == ParameterType_Group)
    {
    oss<< "<group>         ";
    }
  else
    itkExceptionMacro("Not handled parameter type.");


  oss<< " " << m_Application->GetParameterName(paramKey) << " ";

  if (type == ParameterType_OutputImage)
    {
    OutputImageParameter* paramDown = dynamic_cast<OutputImageParameter*>(param.GetPointer());
    std::string defPixType("float");
    if (paramDown)
      {
      defPixType = OutputImageParameter::ConvertPixelTypeToString(paramDown->GetDefaultPixelType());
      }
    oss << " [pixel=uint8/uint16/int16/uint32/int32/float/double/cint16/cint32/cfloat/cdouble]";
    oss << " (default value is " << defPixType <<")";
    }

  if (type == ParameterType_Choice)
    {
    ChoiceParameter* paramDown = dynamic_cast<ChoiceParameter*>(param.GetPointer());
    if (paramDown)
      {
      std::vector<std::string> keys = paramDown->GetChoiceKeys();
      std::vector<std::string> names = paramDown->GetChoiceNames();

      oss << "[";
      for(unsigned int i=0; i<keys.size(); i++)
        {
        oss<<keys[i];
        if( i != keys.size()-1 )
          oss << "/";
        }

      oss << "]";
      }
    }

  if(type != ParameterType_Group)
    {

    if(m_Application->IsMandatory(paramKey))
      {
      oss<<" (mandatory";
      }
    else
      {
      oss<<" (optional";

      if(m_Application->IsParameterEnabled(paramKey))
        {
        oss<<", on by default";
        }
      else
        {
        oss<<", off by default";
        }
      }
    
    if(m_Application->HasValue(paramKey))
      {
      if ( m_Application->GetParameterAsString(paramKey).empty() )
        oss<<", no default value";
      else
        {
        oss<<", default value is "
           <<m_Application->GetParameterAsString(paramKey);
        }
      }
    oss<<")";
    }
  oss << std::endl;

  if(longHelp)
    {
    oss << "        ";
    for(unsigned int i=0; i<maxKeySize;++i)
      oss<<" ";
    oss<<"                   ";
    oss<<m_Application->GetParameterDescription(paramKey)<<std::endl;


    if (type == ParameterType_Choice)
      {
      std::vector<std::string> keys = dynamic_cast<ChoiceParameter*>(param.GetPointer())->GetChoiceKeys();
      std::vector<std::string> names = dynamic_cast<ChoiceParameter*>(param.GetPointer())->GetChoiceNames();
      
      auto keyIt = keys.begin();
      auto nameIt = names.begin();
      
      for( ; keyIt!=keys.end()&&nameIt!=names.end();++keyIt,++nameIt)
        {
        oss << "        ";
        for(unsigned int i=0; i<maxKeySize;++i)
          oss<<" ";
        oss<<"                   ";
        oss<<"- "<<*nameIt<<" ("<<*keyIt<<"): "<<m_Application->GetParameterDescription(paramKey+"."+(*keyIt))<<std::endl;
        }
      }
    }
  return oss.str();
}

bool CommandLineLauncher::CheckUnicity()
{
  bool res = true;
  // Extract expression keys
  //std::vector<std::string> keyList = m_Parser->GetKeyList(m_Expression);
  std::vector<std::string> keyList = m_Parser->GetKeyList(m_VExpression);

  // Check Unicity
  for (unsigned int i = 0; i < keyList.size(); i++)
    {
    std::vector<std::string> listTmp = keyList;
    const std::string keyRef = keyList[i];
    listTmp.erase(listTmp.begin() + i);
    for (unsigned int j = 0; j < listTmp.size(); j++)
      {
      if (keyRef == listTmp[j])
        {
        res = false;
        break;
        }
      }
    if (!res)
      break;
    }

  return res;
}

bool CommandLineLauncher::CheckParametersPrefix()
{
  // Check if the chain " --" appears in the args, could be a common mistake
  for (std::vector<std::string>::iterator it = m_VExpression.begin(); it != m_VExpression.end(); ++it)
    {
    if (it->compare(0, 2, "--") == 0 )
      {
      return false;
      }
    }
  return true;
}

bool CommandLineLauncher::CheckKeyValidity(std::string& refKey)
{
  bool res = true;
  // Extract expression keys
  std::vector<std::string> expKeyList = m_Parser->GetKeyList(m_VExpression);

  // Extract application keys
  std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
  appKeyList.push_back("help");
  appKeyList.push_back("progress");
  appKeyList.push_back("testenv");
  appKeyList.push_back("version");

  // Check if each key in the expression exists in the application
  for (unsigned int i = 0; i < expKeyList.size(); i++)
    {
    refKey = expKeyList[i];
    bool keyExist = false;
    for (unsigned int j = 0; j < appKeyList.size(); j++)
      {
      if (refKey == appKeyList[j])
        {
        keyExist = true;
        break;
        }
      }
    if (keyExist == false)
      {
      res = false;
      break;
      }
    }

  return res;
}

void CommandLineLauncher::DisplayOutputParameters()
{
  std::vector< std::pair<std::string, std::string> > paramList;
  paramList = m_Application->GetOutputParametersSumUp();
  if( paramList.size() == 0 )
    return;

  std::ostringstream oss;
  for( unsigned int i=0; i<paramList.size(); i++)
    {
    oss << paramList[i].first;
    oss << ": ";
    oss << paramList[i].second;
    oss << std::endl;
    }


  if ( m_Parser->IsAttributExists("-testenv", m_VExpression) )
    {
    std::vector<std::string> val = m_Parser->GetAttribut("-testenv", m_VExpression);
    if( val.size() == 1 )
      {
      std::ofstream ofs(val[0]);
      if (!ofs.is_open())
        {
        fprintf(stderr, "Error, can't open file");
        itkExceptionMacro( << "Error, can't open file "<<val[0]<<".");
        }
      ofs << oss.str();
      ofs.close();
      }
    }

  std::cout << "Output parameters value:" << std::endl;
  std::cout << oss.str() << std::endl;
}

unsigned int CommandLineLauncher::GetMaxKeySize() const
{
  const std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
  const unsigned int nbOfParam = appKeyList.size();

  unsigned int maxKeySize = std::string("progress").size();
  
  for (unsigned int i = 0; i < nbOfParam; i++)
    {
    if (m_Application->GetParameterRole(appKeyList[i]) != Role_Output)
      {
      if( maxKeySize < appKeyList[i].size() )
        maxKeySize = appKeyList[i].size();
      }
    }
  
  return maxKeySize;
}

}
}