// C++ Command line parameters parser.
//
// Copyright (C) Denis Shevchenko, 2010.
// shev.denis @ gmail.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// See http://www.opensource.org/licenses/mit-license.php
//
// http://clp-parser.sourceforge.net/

#ifndef CLPP_DETAIL_UNNAMED_PARAMETERS_HANDLER_HPP
#define CLPP_DETAIL_UNNAMED_PARAMETERS_HANDLER_HPP

#include "parameter.hpp"
#include "parameter_parts_extractor.hpp"

/// \namespace clpp
/// \brief Main namespace of library.
namespace clpp {

/// \namespace clpp::detail
/// \brief Details of realization.
namespace detail {

/// \class unnamed_parameters_handler
/// \brief Unnamed parameters handler.
///
/// Handle unnamed inputed parameters.
/// Assumed that unnamed parameter registered with 'order()' function.
class unnamed_parameters_handler {
public:
    unnamed_parameters_handler( const parameters&                     _registered_parameters
                                , const parameter_parts_extractor&    _extractor
                                , const std::string&                  _name_value_separator ) :
            registered_parameters( _registered_parameters )
            , extractor( _extractor )
            , name_value_separator( _name_value_separator ) {}
private:
    const parameters&                registered_parameters;
    const parameter_parts_extractor& extractor;
    const std::string&               name_value_separator;
public:
    void operator()( str_storage& inputed_parameters ) const {
        if ( no_registered_unnamed_parameters() ) {
            return;
        } else {}

        for ( size_t i = 0; i < inputed_parameters.size(); ++i ) {
            std::string& inputed_parameter = inputed_parameters[i];
            if ( this_is_unnamed_parameter( inputed_parameter ) ) {
                handle_unnamed_parameter( i, inputed_parameter );
            } else {}
        }
    }
private:
    bool no_registered_unnamed_parameters() const {
        return parameter::orders.empty();
    }

    bool this_is_unnamed_parameter( const std::string& inputed_parameter ) const {
        return !boost::contains( inputed_parameter, name_value_separator )
               && this_is_not_name( inputed_parameter );
    }

    bool this_is_not_name( const std::string& inputed_parameter ) const {
        parameter_const_it it = std::find( registered_parameters.begin()
                                           , registered_parameters.end()
                                           , inputed_parameter );
        return registered_parameters.end() == it;
    }

    void handle_unnamed_parameter( size_t inputed_parameter_index, std::string& inputed_parameter ) const {
        const size_t inputed_parameter_serial_number = inputed_parameter_index + 1;
        parameter_const_it it = std::find( registered_parameters.begin()
                                           , registered_parameters.end()
                                           , inputed_parameter_serial_number );
        if ( registered_parameters.end() != it ) {
            const std::string value_of_unnamed_parameter = inputed_parameter;
            inputed_parameter = it->short_name + name_value_separator + value_of_unnamed_parameter;
        } else {
            notify_about_missing_of_parameter_with_serial_number( inputed_parameter
                                                                  , inputed_parameter_serial_number );
        }
    }

    void notify_about_missing_of_parameter_with_serial_number( const std::string& inputed_parameter
                                                               , size_t serial_number ) const {
        o_stream what_happened;
        what_happened << lib_prefix()
                      << "You passed an unknown parameter '" << inputed_parameter << "' "
                      << "with order number " << serial_number << ", "
                      << "but there is no parameter expected at that position!";
        throw std::runtime_error( what_happened.str() );
    }
};

} // namespace detail
} // namespace clpp

#endif // CLPP_DETAIL_UNNAMED_PARAMETERS_HANDLER_HPP
