"""
Provides:
    PassthroughOptionParser:
        A subclass of :class:`optparse.OptionParser` that captures unknown options
        into its ``passthrough_options`` attribute.
    PassthroughTask:
        A subclass of :class:`paver.tasks.Task` that supplies unknown options
        as the `passthrough_options` argument to the decorated function
"""

from optparse import BadOptionError, OptionParser

import paver.tasks
from mock import patch


class PassthroughOptionParser(OptionParser):
    """
    An :class:`optparse.OptionParser` which captures any unknown options into
    the ``passthrough_options`` attribute. Handles both "--long-options" and
    "-s" short options.
    """
    def __init__(self, *args, **kwargs):
        self.passthrough_options = []

        # N.B. OptionParser is an old-style class, which is why
        # this isn't using super()
        OptionParser.__init__(self, *args, **kwargs)

    def _process_long_opt(self, rargs, values):
        # This is a copy of the OptionParser._process_long_opt method,
        # modified to capture arguments that aren't understood

        arg = rargs.pop(0)

        # Value explicitly attached to arg?  Pretend it's the next
        # argument.

        if "=" in arg:
            (opt, next_arg) = arg.split("=", 1)
            rargs.insert(0, next_arg)
            had_explicit_value = True
        else:
            opt = arg
            had_explicit_value = False

        try:
            opt = self._match_long_opt(opt)
        except BadOptionError:
            self.passthrough_options.append(arg)
            if had_explicit_value:
                rargs.pop(0)
            return

        option = self._long_opt[opt]
        if option.takes_value():
            nargs = option.nargs

            if len(rargs) < nargs:
                if nargs == 1:
                    self.error("%s option requires an argument" % opt)
                else:
                    self.error("%s option requires %d arguments"
                               % (opt, nargs))
            elif nargs == 1:
                value = rargs.pop(0)
            else:
                value = tuple(rargs[0:nargs])
                del rargs[0:nargs]

        elif had_explicit_value:
            self.error("%s option does not take a value" % opt)

        else:
            value = None

        option.process(opt, value, values, self)

    def _process_short_opts(self, rargs, values):
        arg = rargs.pop(0)
        stop = False
        i = 1

        passthrough_opts = []

        for char in arg[1:]:
            opt = "-" + char
            option = self._short_opt.get(opt)
            i += 1                      # we have consumed a character

            if not option:
                passthrough_opts.append(char)
                continue

            if option.takes_value():
                # Any characters left in arg?  Pretend they're the
                # next arg, and stop consuming characters of arg.

                if i < len(arg):
                    rargs.insert(0, arg[i:])
                    stop = True

                nargs = option.nargs
                if len(rargs) < nargs:
                    if nargs == 1:
                        self.error("%s option requires an argument" % opt)
                    else:
                        self.error("%s option requires %d arguments"
                                   % (opt, nargs))

                elif nargs == 1:
                    value = rargs.pop(0)
                else:
                    value = tuple(rargs[0:nargs])
                    del rargs[0:nargs]

            else:                       # option doesn't take a value
                value = None

            option.process(opt, value, values, self)

            if stop:
                break

        if passthrough_opts:
            self.passthrough_options.append('-{}'.format("".join(passthrough_opts)))


class PassthroughTask(paver.tasks.Task):
    """
    A :class:`paver.tasks.Task` subclass that supplies any options that it doesn't
    understand to the task function as the ``passthrough_options`` argument.
    """

    @property
    def parser(self):
        with patch.object(paver.tasks.optparse, 'OptionParser', PassthroughOptionParser):
            return super(PassthroughTask, self).parser

    def __call__(self, *args, **kwargs):
        paver.tasks.environment.passthrough_options = self._parser.passthrough_options  # pylint: disable=no-member
        try:
            return super(PassthroughTask, self).__call__(*args, **kwargs)
        finally:
            del paver.tasks.environment.passthrough_options
