# -*- coding: utf-8 -*-

"""
oauthlib.utils
~~~~~~~~~~~~~~

This module contains utility methods used by various parts of the OAuth
spec.
"""

import string
import time
import urllib2
from random import getrandbits, choice

from oauthlib.common import quote, unquote

UNICODE_ASCII_CHARACTER_SET = (string.ascii_letters.decode('ascii') +
    string.digits.decode('ascii'))


def filter_params(target):
    """Decorator which filters params to remove non-oauth_* parameters

    Assumes the decorated method takes a params dict or list of tuples as its
    first argument.
    """
    def wrapper(params, *args, **kwargs):
        params = filter_oauth_params(params)
        return target(params, *args, **kwargs)

    wrapper.__doc__ = target.__doc__
    return wrapper


def filter_oauth_params(params):
    """Removes all non oauth parameters from a dict or a list of params."""
    is_oauth = lambda kv: kv[0].startswith(u"oauth_")
    if isinstance(params, dict):
        return filter(is_oauth, params.items())
    else:
        return filter(is_oauth, params)


def generate_timestamp():
    """Get seconds since epoch (UTC).

    Per `section 3.3`_ of the spec.

    .. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
    """
    return unicode(int(time.time()))


def generate_nonce():
    """Generate pseudorandom nonce that is unlikely to repeat.

    Per `section 3.3`_ of the spec.

    A random 64-bit number is appended to the epoch timestamp for both
    randomness and to decrease the likelihood of collisions.

    .. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
    """
    return unicode(getrandbits(64)) + generate_timestamp()


def generate_token(length=20, chars=UNICODE_ASCII_CHARACTER_SET):
    """Generates a generic OAuth token

    According to `section 2`_ of the spec, the method of token
    construction is undefined. This implementation is simply a random selection
    of `length` choices from `chars`.

    Credit to Ignacio Vazquez-Abrams for his excellent `Stackoverflow answer`_

    .. _`Stackoverflow answer` : http://stackoverflow.com/questions/2257441/
        python-random-string-generation-with-upper-case-letters-and-digits

    """
    return u''.join(choice(chars) for x in range(length))


def escape(u):
    """Escape a unicode string in an OAuth-compatible fashion.

    Per `section 3.6`_ of the spec.

    .. _`section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6

    """
    if not isinstance(u, unicode):
        raise ValueError('Only unicode objects are escapable.')
    # Letters, digits, and the characters '_.-' are already treated as safe
    # by urllib.quote(). We need to add '~' to fully support rfc5849.
    return quote(u, safe='~')


def unescape(u):
    if not isinstance(u, unicode):
        raise ValueError('Only unicode objects are unescapable.')
    return unquote(u)


def urlencode(query):
    """Encode a sequence of two-element tuples or dictionary into a URL query string.

    Operates using an OAuth-safe escape() method, in contrast to urllib.urlencode.
    """
    # Convert dictionaries to list of tuples
    if isinstance(query, dict):
        query = query.items()
    return u"&".join([u'='.join([escape(k), escape(v)]) for k, v in query])


def parse_keqv_list(l):
    """A unicode-safe version of urllib2.parse_keqv_list"""
    encoded_list = [u.encode('utf-8') for u in l]
    encoded_parsed = urllib2.parse_keqv_list(encoded_list)
    return dict((k.decode('utf-8'),
        v.decode('utf-8')) for k,v in encoded_parsed.items())


def parse_http_list(u):
    """A unicode-safe version of urllib2.parse_http_list"""
    encoded_str = u.encode('utf-8')
    encoded_list = urllib2.parse_http_list(encoded_str)
    return [s.decode('utf-8') for s in encoded_list]


def parse_authorization_header(authorization_header):
    """Parse an OAuth authorization header into a list of 2-tuples"""
    auth_scheme = u'OAuth '
    if authorization_header.startswith(auth_scheme):
        authorization_header = authorization_header.replace(auth_scheme, u'', 1)
    items = parse_http_list(authorization_header)
    try:
        return parse_keqv_list(items).items()
    except ValueError:
        raise ValueError('Malformed authorization header')

