/***************************************************************************
                          tnlParameterContainer.cpp  -  description
                             -------------------
    begin                : 2007/06/15
    copyright            : (C) 2007 by Tomas Oberhuber
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <ctype.h>
#include <cstring>
#include <stdio.h>
#include "tnlParameterContainer.h"

bool matob( const char* value, bool& ret_val )
{
   if( strcasecmp( value, "yes" ) == 0 ||
       strcasecmp( value, "true" ) == 0  )
   {
      ret_val =  true;
      return true;
   }
   if( strcasecmp( value, "no" ) == 0 ||
       strcasecmp( value, "false" ) == 0  )
   {
      ret_val = false;
      return true;
   }
   return false;
}
//--------------------------------------------------------------------------
tnlParameterContainer :: tnlParameterContainer()
{
}
//--------------------------------------------------------------------------
bool tnlParameterContainer :: AddParameter( const char* name,
                                            const char* value )
{
   return parameters. Append( new tnlParameter< tnlString >( name, GetParameterType( tnlString() ). getString(), tnlString( value ) ) );
}
//--------------------------------------------------------------------------
bool tnlParameterContainer :: SetParameter( const char* name,
                                                  const char* value )
{
   int i;
   for( i = 0; i < parameters. getSize(); i ++ )
   {
      if( parameters[ i ] -> name == name )
      {
         if( parameters[ i ] -> type == getParameterType< tnlString >() )
         {
            ( ( tnlParameter< tnlString > * ) parameters[ i ] ) -> value. setString( value );
            return true;
         }
         else
         {
            cerr << "Parameter " << name << " already exists with different type " 
                 << parameters[ i ] -> type << " not " 
                 << GetParameterType( value ) << endl;
            abort();
            return false;
         }
      }
   }
   return AddParameter( name, value );
};
//--------------------------------------------------------------------------
bool tnlParameterContainer :: CheckParameter( const char* name ) const
{
   int i;
   const int parameters_num = parameters. getSize();
   for( i = 0; i < parameters_num; i ++ )
      if( parameters[ i ] -> name == name ) return true;
   return false;
}
//--------------------------------------------------------------------------
tnlParameterContainer :: ~tnlParameterContainer()
{
   parameters. DeepEraseAll();
}
//--------------------------------------------------------------------------
void tnlParameterContainer :: MPIBcast( int root, MPI_Comm mpi_comm )
{
#ifdef HAVE_MPI
   int i;
   int size = parameters. getSize();
   :: MPIBcast( size, 1, root, mpi_comm ); 
   for( i = 0; i < size; i ++ )
   {
      if( MPIGetRank() == root )
      {
         tnlParameterBase* param = parameters[ i ];
         param -> type. MPIBcast( root, MPI_COMM_WORLD );
         param -> name. MPIBcast( root, MPI_COMM_WORLD );
         if( param -> type == "mString" )
         {
            ( ( tnlParameter< tnlString >* ) param ) -> value. MPIBcast( root, mpi_comm );
         }
         if( param -> type == "bool" )
         {
            :: MPIBcast( ( ( tnlParameter< bool >* ) param ) -> value, 1, root, mpi_comm );
         }
         if( param -> type == "int" )
         {
            :: MPIBcast( ( ( tnlParameter< int >* ) param ) -> value, 1, root, mpi_comm );
         }
         if( param -> type == "double" )
         {
            :: MPIBcast( ( ( tnlParameter< double >* ) param ) -> value, 1, root, mpi_comm );
         }
      }
      else
      {
         tnlString param_type, param_name;
         param_type. MPIBcast( root, MPI_COMM_WORLD );
         param_name. MPIBcast( root, MPI_COMM_WORLD );
         if( param_type == "mString" )
         {
            tnlString val;
            val. MPIBcast( root, mpi_comm );
            AddParameter< tnlString >( param_name. getString(),
                                     val );           
         }
         if( param_type == "bool" )
         {
            bool val;
            :: MPIBcast( val, 1, root, mpi_comm );
            AddParameter< bool >( param_name. getString(),
                                  val );           
         }
         if( param_type == "int" )
         {
            int val;
            :: MPIBcast( val, 1, root, mpi_comm );
            AddParameter< int >( param_name. getString(),
                                 val );           
         }
         if( param_type == "double" )
         {
            double val;
            :: MPIBcast( val, 1, root, mpi_comm );
            AddParameter< double >( param_name. getString(),
                                    val );           
         }

      }
   }
#endif
}
//--------------------------------------------------------------------------
bool ParseCommandLine( int argc, char* argv[], 
                       const tnlConfigDescription& config_description,
                       tnlParameterContainer& parameters )
{
   int i;
   bool parse_error( false );
   for( i = 1; i < argc; i ++ )
   {
      const char* _option = argv[ i ];
      if( _option[ 0 ] != '-' )
      {
         cerr << "Unknown option " << _option << ". Options must have prefix '--' or '-'." << endl;
         parse_error = true;
         continue;
      }
      
      const char* option = _option + 2;
      const tnlConfigEntryType* entry_type;
      if( ! ( entry_type = config_description. GetEntryType( option ) ) )
      {
         cerr << "Unknown parameter " << option << "." << endl;
         parse_error = true;
      }
      else 
      {
         const char* value = argv[ ++ i ];
         if( ! value )
         {
            cerr << "Missing value for the parameter " << option << "." << endl;
            return false;
         }
         const tnlString& basic_type_name = entry_type -> basic_type;
         if( entry_type -> list_entry )
         {
            tnlList< tnlString >* string_list( 0 );
            tnlList< bool >* bool_list( 0 );
            tnlList< int >* integer_list( 0 );
            tnlList< double >* real_list( 0 );

            if( basic_type_name == "string" )
               string_list = new tnlList< tnlString >;
            if( basic_type_name == "bool" )
               bool_list = new tnlList< bool >;
            if( basic_type_name == "integer" )
               integer_list = new tnlList< int >;
            if( basic_type_name == "real" )
               real_list = new tnlList< double >;
            
            while( i < argc && ( ( argv[ i ] )[ 0 ] != '-' || ( atof( argv[ i ] ) < 0.0 && ( integer_list || real_list ) ) ) )
            {
               const char* value = argv[ i ++ ];
               if( string_list ) string_list -> Append( tnlString( value ) );
               if( bool_list )
               {
                  bool bool_val;
                  if( ! matob( value, bool_val ) )
                  {
                     cerr << "Yes/true or no/false is required for the parameter " << option << "." << endl;
                     parse_error = true;
                  }
                  else bool_list -> Append( bool_val );
               }
               if( integer_list ) integer_list -> Append( atoi( value ) );
               if( real_list ) real_list -> Append( atof( value ) );
            }
            if( string_list )
            {
               parameters. AddParameter< tnlList< tnlString > >( option, *string_list );
               delete string_list;
            }
            if( bool_list )
            {
               parameters. AddParameter< tnlList< bool > >( option, *bool_list );
               delete bool_list;
            }
            if( integer_list )
            {
               parameters. AddParameter< tnlList< int > >( option, *integer_list );
               delete integer_list;
            }
            if( real_list )
            {
               parameters. AddParameter< tnlList< double > >( option, *real_list );
               delete real_list;
            }
            if( i < argc ) i --;
            continue;
         }
         else
         {
            if( basic_type_name == "string" )
            {
                parameters. AddParameter< tnlString >( option, value );
                continue;
            }
            if( basic_type_name == "bool" )
            {
               bool bool_val;
               if( ! matob( value, bool_val ) )
               {
                  cerr << "Yes/true or no/false is required for the parameter " << option << "." << endl;
                  parse_error = true;
               }
               else parameters. AddParameter< bool >( option, bool_val );
               continue;
            }
            if( basic_type_name == "integer" )
            {
               /*if( ! isdigit( value ) )
               {
                  cerr << "Integer constant is required for the parameter " << option << "." << endl;
                  parse_error = true;
                  continue;
               }*/
               parameters. AddParameter< int >( option, atoi( value ) );
            }
            if( basic_type_name == "real" )
            {
               /*if( ! isdigit( value ) )
               {
                  cerr << "Real constant is required for the parameter " << option << "." << endl;
                  parse_error = true;
                  continue;
               }*/
               parameters. AddParameter< double >( option, atof( value ) );
            }
         }
      }
   }
   config_description. AddMissingEntries( parameters );
   if( ! config_description. CheckMissingEntries( parameters ) )
      return false;
   return ! parse_error;
}