# coding: utf-8
from __future__ import absolute_import, division, print_function

from collections import Iterable

import six


class UnsetType(object):
    def __repr__(self):
        return 'UNSET'


UNSET = UnsetType()
del UnsetType


class Infinity(object):

    def __repr__(self):
        return "Infinity"

    def __hash__(self):
        return hash(repr(self))

    def __lt__(self, other):
        return False

    def __le__(self, other):
        return False

    def __eq__(self, other):
        return isinstance(other, self.__class__)

    def __ne__(self, other):
        return not isinstance(other, self.__class__)

    def __gt__(self, other):
        return True

    def __ge__(self, other):
        return True

    def __neg__(self):
        return NegativeInfinity


Infinity = Infinity()


class NegativeInfinity(object):

    def __repr__(self):
        return "-Infinity"

    def __hash__(self):
        return hash(repr(self))

    def __lt__(self, other):
        return True

    def __le__(self, other):
        return True

    def __eq__(self, other):
        return isinstance(other, self.__class__)

    def __ne__(self, other):
        return not isinstance(other, self.__class__)

    def __gt__(self, other):
        return False

    def __ge__(self, other):
        return False

    def __neg__(self):
        return Infinity


NegativeInfinity = NegativeInfinity()


def fixup_module_metadata(module_name, namespace):
    def fix_one(obj):
        mod = getattr(obj, "__module__", None)
        if mod is not None and mod.startswith("outcome."):
            obj.__module__ = module_name
            if isinstance(obj, type):
                for attr_value in obj.__dict__.values():
                    fix_one(attr_value)

    for objname in namespace["__all__"]:
        obj = namespace[objname]
        fix_one(obj)


def force_tuple(n):
    if isinstance(n, six.string_types):
        raise TypeError('Expected tuple or int.')
    if not isinstance(n, Iterable):
        return n,
    if not isinstance(n, tuple):
        return tuple(n)
    return n


def doc_signature(signature):
    def decorator(fn):
        fn.__parverdoc_signature__ = signature
        return fn
    return decorator


def kwonly_args(kws, required, withdefaults=(), leftovers=False):
    """

    Based on the snippet by Eric Snow
    http://code.activestate.com/recipes/577940

    SPDX-License-Identifier: MIT
    """

    if hasattr(withdefaults, "items"):
        # allows for OrderedDict to be passed
        withdefaults = withdefaults.items()

    kwonly = []

    # extract the required keyword-only arguments
    missing = []
    for name in required:
        if name not in kws:
            missing.append(name)
        else:
            kwonly.append(kws.pop(name))

    # validate required keyword-only arguments
    if missing:
        if len(missing) > 2:
            end = "s: %s, and %s" % (", ".join(missing[:-1]), missing[-1])
        elif len(missing) == 2:
            end = "s: %s and %s" % tuple(missing)
        else:
            end = ": %s" % tuple(missing)

        msg = "missing %s required keyword-only argument%s"
        raise TypeError(msg % (len(missing), end))

    # handle the withdefaults
    for name, value in withdefaults:
        if name not in kws:
            kwonly.append(value)
        else:
            kwonly.append(kws.pop(name))

    # handle any leftovers
    if not leftovers and kws:
        msg = "got an unexpected keyword argument '%s'"
        raise TypeError(msg % (kws.keys()[0]))

    return [kws] + kwonly
