/*======================================================================

This file is part of the elastix software.

Copyright (c) University Medical Center Utrecht. All rights reserved.
See src/CopyrightElastix.txt or http://elastix.isi.uu.nl/legal.php for
details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.

======================================================================*/

#ifndef __itkParameterFileParser_h
#define __itkParameterFileParser_h

#include "itkObject.h"
#include "itkObjectFactory.h"
#include "itkMacro.h"

#include <map>
#include <string>
#include <vector>
#include <fstream>


namespace itk
{

/** \class ParameterFileParser
 *
 * \brief Implements functionality to read a parameter file.
 *
 * A parameter file is a text file that contains parameters and their values.
 * Parameters should be specified obeying certain rules.\n
 * 1) A single parameter should be on a single line\n
 * 2) A parameter should be specified between brackets: (...)\n
 * 3) Parameters are specified by a single name, followed by one or more
 *      values, all separated by spaces\n
 * 4) Values that are strings should be quoted using "\n
 * 5) Values that are numbers should be unquoted\n
 *
 * For example: \n
 * (ParameterName1 "string1" "string2")\n
 * (ParameterName2 3 5.8)\n
 * (ParameterName3 "true" "false" "true")\n
 *
 * The parameter file is read, and parameter name-value combinations are
 * stored in an std::map< std::string, std::vector<std:string> >, where the
 * string is the parameter name, and the vector of strings are the values.
 * Exceptions are raised in case:\n
 * - the parameter text file cannot be opened,\n
 * - rule 2 or 3 is not satisfied,\n
 * - the parameter name or value contains invalid characters.\n
 *
 * Here is an example on how to use this class:\n
 *
 * itk::ParameterFileParser::Pointer parser = itk::ParameterFileParser::New();
 * parser->SetParameterFileName( parameterFileName );
 * try
 * {
 *   parser->Initialize();
 * }
 * catch ( itk::ExceptionObject & e )
 * {
 *   ...
 * }
 *
 * The resulting map can be accessed via:\n
 *
 * parser->GetParameterMap();
 *
 * \sa itk::ParameterMapInterface
 */

class ParameterFileParser : public Object
{
public:

  /** Standard ITK typedefs. */
  typedef ParameterFileParser         Self;
  typedef Object                      Superclass;
  typedef SmartPointer< Self >        Pointer;
  typedef SmartPointer< const Self >  ConstPointer;

  /** Method for creation through the object factory. */
  itkNewMacro( Self );

  /** Run-time type information (and related methods). */
  itkTypeMacro( ParameterFileParser, Object );

  /** Typedefs. */
  typedef std::vector< std::string >      ParameterValuesType;
  typedef std::map<
    std::string,
    ParameterValuesType >                 ParameterMapType;

  /** Set the name of the file containing the parameters. */
  itkSetStringMacro( ParameterFileName );
  itkGetStringMacro( ParameterFileName );

  /** Return the parameter map. */
  virtual const ParameterMapType & GetParameterMap( void ) const;

  /** Read the parameters in the parameter map. */
  void ReadParameterFile( void );

  /** Read the parameter file and return the content as a string.
   * Useful for printing the content.
   */
  std::string ReturnParameterFileAsString( void );

protected:
  ParameterFileParser();
  virtual ~ParameterFileParser();

private:
  ParameterFileParser(const Self&); //purposely not implemented
  void operator=(const Self&); //purposely not implemented

  /** Performs the following checks:
   * - Is a filename is given
   * - Does the file exist
   * - Is a text file, i.e. does it end with .txt
   * If one of these conditions fail, an exception is thrown.
   */
  void BasicFileChecking( void ) const;

  /** Checks a line.
   * - Returns  true if it is a valid line: containing a parameter.
   * - Returns false if it is a valid line: empty or comment.
   * - Throws an exception if it is not a valid line.
   */
  bool CheckLine( const std::string & line, std::string & lineOut ) const;

  /** Fills m_ParameterMap with valid entries. */
  void GetParameterFromLine( const std::string & fullLine,
    const std::string & line );

  /** Splits a line in parameter name and values. */
  void SplitLine( const std::string & fullLine, const std::string & line,
    std::vector<std::string> & splittedLine ) const;

  /** Uniform way to throw exceptions when the parameter file appears to be
   * invalid.
   */
  void ThrowException( const std::string & line, const std::string & hint ) const;

  /** Member variables. */
  std::string       m_ParameterFileName;
  std::ifstream     m_ParameterFile;
  ParameterMapType  m_ParameterMap;

}; // end class ParameterFileParser

} // end of namespace itk

#endif // end __itkParameterFileParser_h
