# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Libcloud Python 2.x and 3.x compatibility layer
# Some methods below are taken from Django PYK3 port which is licensed under 3
# clause BSD license
# https://bitbucket.org/loewis/django-3k

# pylint: disable=import-error

from __future__ import absolute_import

import sys
import types

DEFAULT_LXML = False

try:
    if DEFAULT_LXML:
        from lxml import etree as ET
    else:
        from xml.etree import ElementTree as ET
except ImportError:
    from xml.etree import ElementTree as ET

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
PY2_pre_279 = PY2 and sys.version_info < (2, 7, 9)

PY2 = False
PY27 = False
PY3 = False
PY37 = False

if sys.version_info >= (2, 0) and sys.version_info < (3, 0):
    PY2 = True

if sys.version_info >= (2, 7) and sys.version_info < (2, 8):
    PY27 = True

if sys.version_info >= (3, 0):
    PY3 = True

if sys.version_info >= (3, 7):
    PY37 = True

if PY2_pre_279:
    try:
        from backports.ssl_match_hostname import match_hostname, CertificateError  # NOQA
    except ImportError:
        import warnings
        warnings.warn("Missing backports.ssl_match_hostname package")
else:
    # ssl module in Python >= 3.2 includes match hostname function
    from ssl import match_hostname, CertificateError  # NOQA

if PY3:
    import http.client as httplib
    from io import StringIO
    import urllib
    import urllib as urllib2
    # pylint: disable=no-name-in-module
    import urllib.parse as urlparse
    import xmlrpc.client as xmlrpclib

    from urllib.parse import quote as _urlquote
    from urllib.parse import unquote as urlunquote
    from urllib.parse import urlencode as urlencode
    from os.path import relpath

    from imp import reload

    from builtins import bytes
    from builtins import next

    parse_qs = urlparse.parse_qs
    parse_qsl = urlparse.parse_qsl

    basestring = str

    def method_type(callable, instance, klass):
        return types.MethodType(callable, instance or klass())

    def b(s):
        if isinstance(s, str):
            return s.encode('utf-8')
        elif isinstance(s, bytes):
            return s
        elif isinstance(s, int):
            return bytes([s])
        else:
            raise TypeError("Invalid argument %r for b()" % (s,))

    def ensure_string(s):
        if isinstance(s, str):
            return s
        elif isinstance(s, bytes):
            return s.decode('utf-8')
        else:
            raise TypeError("Invalid argument %r for ensure_string()" % (s,))

    def byte(n):
        # assume n is a Latin-1 string of length 1
        return ord(n)

    _real_unicode = str
    u = str

    def bchr(s):
        """Take an integer and make a 1-character byte string."""
        return bytes([s])

    def dictvalues(d):
        return list(d.values())

    def tostring(node):
        return ET.tostring(node, encoding='unicode')

    if PY37:
        def urlquote(s, safe='/'):
            res = _urlquote(s, safe)
            if '~' not in safe:
                res = res.replace('~', '%7E')
            return res
    else:
        urlquote = _urlquote

    def hexadigits(s):
        # s needs to be a byte string.
        return [format(x, "x") for x in s]

else:
    import httplib  # NOQA
    from StringIO import StringIO  # NOQA
    import urllib  # NOQA
    import urllib2  # NOQA
    import urlparse  # NOQA
    import xmlrpclib  # NOQA
    from urllib import quote as _urlquote  # NOQA
    from urllib import unquote as urlunquote  # NOQA
    from urllib import urlencode as urlencode  # NOQA

    from __builtin__ import reload  # NOQA

    parse_qs = urlparse.parse_qs
    parse_qsl = urlparse.parse_qsl

    from os.path import relpath  # NOQA

    # Save the real value of unicode because urlquote needs it to tell the
    # difference between a unicode string and a byte string.
    _real_unicode = unicode
    basestring = unicode = str

    method_type = types.MethodType

    b = bytes = ensure_string = str

    def byte(n):
        return n

    u = unicode

    def bchr(s):
        """Take an integer and make a 1-character byte string."""
        return chr(s)

    _default_value_next = object()

    def next(iterator, default=_default_value_next):
        try:
            return iterator.next()
        except StopIteration:
            if default is _default_value_next:
                raise
            return default

    def dictvalues(d):
        return d.values()

    tostring = ET.tostring

    def urlquote(s, safe='/'):
        if isinstance(s, _real_unicode):
            # Pretend to be py3 by encoding the URI automatically.
            s = s.encode('utf8')
        return _urlquote(s, safe)

    def hexadigits(s):
        # s needs to be a string.
        return [x.encode("hex") for x in s]
