class CommandConstructor:
    """This class builds a string representing an Nmap command line from textual
    option descriptions such as 'Aggressive Options' or 'UDP Scan'
    (corresponding to -A and -sU respectively). The name-to-option mapping is
    done by the NmapOptions class. Options are stored in a dict that maps the
    option name to a tuple containing its arguments and "level." The level is
    the degree of repetition for options like -v that can be given more than
    once."""
    def __init__(self, options={}):
        """Initialize a command line using the given options. The options are
        given as a dict mapping option names to arguments."""
        self.options = {}
        self.option_profile = NmapOptions(Path.options)
        for k, v in options.items():
            self.add_option(k, v, False)

    def add_option(self, option_name, args=[], level=False):
        """Add an option to the command line. Only one of args and level can be
        defined. If both are defined, level takes precedence and args is
        ignored."""
        self.options[option_name] = (args, level)

    def remove_option(self, option_name):
        """Remove an option from the command line."""
        if option_name in self.options.keys():
            del (self.options[option_name])

    def get_command(self, target):
        """Return the contructed command line as a plain string."""
        splited = ['%s' % nmap_command_path]

        for option_name in self.options:
            option = self.option_profile.get_option(option_name)
            args, level = self.options[option_name]

            if type(args) in StringTypes:
                args = [args]

            if level:
                splited.append((option['option'] + ' ') * level)
            elif args:
                args = tuple(args)
                splited.append(option['option'] % args[0])
            else:
                splited.append(option['option'])

        splited.append(target)
        return ' '.join(splited)

    def get_options(self):
        """Return the options used in the command line, as a dict mapping
        options names to arguments. The level, if any, is discarded."""
        return dict([(k, v[0]) for k, v in self.options.items()])