#ifndef COMMAND_H
#define COMMAND_H

/*********************************************************************
 * This header file includes data structure for option reading
 * This is a cornerstone data structure of mini shell
 * which aims to store command-line options for parser
 *
 * This data structue is design to read-in the command line options 
 * which are organized as follows:
 *
 *   <command_name> --<command_option_1> <command_option_1_value>
 *   --<command_option_2> ...
 *
 * This is not only used by each command available in the shell
 * but also the interface of the shell, such as interactive mode
 ********************************************************************/
#include <string>
#include <map>
#include <vector>

#include "vtr_vector.h"
#include "vtr_range.h"
#include "command_fwd.h"

/* Begin namespace openfpga */
namespace openfpga {

/*********************************************************************
 * Supported date types of value which is followed by a command-line option
 ********************************************************************/
enum e_option_value_type {
  OPT_INT,
  OPT_FLOAT,
  OPT_STRING,
  NUM_OPT_VALUE_TYPES
};

/*********************************************************************
 * Data structure to stores all the information related to a command 
 * to be defined in the mini shell
 * This data structure should NOT contain any parsing results!
 * It should be a read-only once created!
 *
 * An example of how to use
 * -----------------------
 * // Create a new command with a name of 'read_file'
 * // This command can be called as 
 * //   read_file --file <file_name>
 * //   read_file -f <file_name>
 * //   read_file --help
 * //   read_file -h
 * Command cmd("read_file");
 *
 * // Add a mandatory option 'file' to the command
 * CommandOptionId opt_file = cmd.add_option("file", true, "Input file path");
 * // Add a short name 'f' for 'file' option
 * cmd.set_option_short_name(opt_file, "f");
 * // The option require a string value which is the file path
 * cmd.set_option_require_value(opt_file, OPT_STRING);
 *
 * // Add an non-mandatory option 'help' to the command
 * // which does not require a value
 * CommandOptionId opt_help = cmd.add_option("help", false, "Show help desk");
 * // Add a short name 'h' for 'help' option
 * cmd.set_option_short_name(opt_help, "h");
 *
 * // Run a command parser and store the results in CommandContext
 * CommandContext cmd_context(cmd);
 * if (false == parse_command(cmd_opts, cmd, cmd_context)) {
 * // Parse failed. Print the help desk
 *   print_command_options(cmd);
 * } else {
 * // Parse succeed. Let user to confirm selected options
 *   print_command_context(cmd, cmd_context);
 * }
 *
 * Note:
 * When adding an option name, please do NOT add any dash at the beginning!!!
 ********************************************************************/
class Command {
  public: /* Types */
    typedef vtr::vector<CommandOptionId, CommandOptionId>::const_iterator command_option_iterator;
    /* Create range */
    typedef vtr::Range<command_option_iterator> command_option_range;
  public: /* Constructor */
    Command(const char* name);
  public: /* Public accessors */
    std::string name() const;
    command_option_range options() const;
    /* Find all the options that are mandatory */
    std::vector<CommandOptionId> required_options() const;
    /* Find all the options that require a value */
    std::vector<CommandOptionId> require_value_options() const;
    CommandOptionId option(const std::string& name) const;
    CommandOptionId short_option(const std::string& name) const;
    std::string option_name(const CommandOptionId& option_id) const;
    std::string option_short_name(const CommandOptionId& option_id) const;
    bool option_required(const CommandOptionId& option_id) const;
    bool option_require_value(const CommandOptionId& option_id) const;
    e_option_value_type option_require_value_type(const CommandOptionId& option_id) const;
    std::string option_description(const CommandOptionId& option_id) const;
  public: /* Public mutators */
    CommandOptionId add_option(const char* name,
                               const bool& option_required,
                               const char* description); 
    bool set_option_short_name(const CommandOptionId& option_id, const char* short_name);
    void set_option_require_value(const CommandOptionId& option_id,
                                  const e_option_value_type& option_require_value_type);
  public: /* Public validators */
    bool valid_option_id(const CommandOptionId& option_id) const;
  private: /* Internal data */ 
    /* The name of the command */
    std::string name_;

    vtr::vector<CommandOptionId, CommandOptionId> option_ids_;  

    /* Information about the options available in this command */
    /* Name of command line option to appear in the user interface 
     * Regular names are typically with a prefix of double dash '--'  
     * Short names are typically with a prefix of single dash '-'  
     */
    vtr::vector<CommandOptionId, std::string> option_names_;  
    vtr::vector<CommandOptionId, std::string> option_short_names_;  

    /* If the option is manadatory when parsing */
    vtr::vector<CommandOptionId, bool> option_required_;  

    /* Data type of the option values to be converted
     * If the option does NOT require a value, this will be an invalid type 
     */
    vtr::vector<CommandOptionId, e_option_value_type> option_require_value_types_; 

    /* Description of the option, this is going to be printed out in the help desk */
    vtr::vector<CommandOptionId, std::string> option_description_;  

    /* Fast name look-up */
    std::map<std::string, CommandOptionId> option_name2ids_;
    std::map<std::string, CommandOptionId> option_short_name2ids_;
};

} /* End namespace openfpga */

#endif