###############################################################################
#
# The MIT License (MIT)
#
# Copyright (c) Tavendo GmbH
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
###############################################################################

import re

__all__ = ("lookupWsSupport",)

UA_FIREFOX = re.compile(".*Firefox/(\d*).*")
UA_CHROME = re.compile(".*Chrome/(\d*).*")
UA_CHROMEFRAME = re.compile(".*chromeframe/(\d*).*")
UA_WEBKIT = re.compile(".*AppleWebKit/([0-9+\.]*)\w*.*")
UA_WEBOS = re.compile(".*webos/([0-9+\.]*)\w*.*")
UA_HPWEBOS = re.compile(".*hpwOS/([0-9+\.]*)\w*.*")

UA_DETECT_WS_SUPPORT_DB = {}


# Chrome =============================================================

# Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11


# Chrome Frame =======================================================

# IE6 on Windows with Chrome Frame
# Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; chromeframe/11.0.660.0)


# Firefox ============================================================

# Windows 7 64 Bit
# Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0a2) Gecko/20120227 Firefox/12.0a2


# Android ============================================================

# Firefox Mobile
# Mozilla/5.0 (Android; Linux armv7l; rv:10.0.2) Gecko/20120215 Firefox/10.0.2 Fennec/10.0.2

# Chrome for Android (on ICS)
# Mozilla/5.0 (Linux; U; Android-4.0.3; en-us; Galaxy Nexus Build/IML74K) AppleWebKit/535.7 (KHTML, like Gecko) CrMo/16.0.912.75 Mobile Safari/535.7

# Android builtin browser

# Samsung Galaxy Tab 1
# Mozilla/5.0 (Linux; U; Android 2.2; de-de; GT-P1000 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

# Samsung Galaxy S
# Mozilla/5.0 (Linux; U; Android 2.3.3; de-de; GT-I9000 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

# Samsung Galaxy Note
# Mozilla/5.0 (Linux; U; Android 2.3.6; de-de; GT-N7000 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

# Samsung Galaxy ACE (no Flash since ARM)
# Mozilla/5.0 (Linux; U; Android 2.2.1; de-de; GT-S5830 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1


# WebOS ==============================================================

# HP Touchpad
# Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0
# => Qt-WebKit, Hixie-76, Flash


# Safari =============================================================

# iPod Touch, iOS 4.2.1
# Mozilla/5.0 (iPod; U; CPU iPhone OS 4_2_1 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5
# => Hixie-76

# MacBook Pro, OSX 10.5.8, Safari 5.0.6
# Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.50.2 (KHTML, like Gecko) Version/5.0.6 Safari/533.22.3
# => Hixie-76

# RFC6455
# Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7
# Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.24+ (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10

# Hixie-76
# Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.53.11 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10

# Hixie-76
# Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.50.2 (KHTML, like Gecko) Version/5.0.6 Safari/533.22.3


# Opera ==============================================================

# Windows 7 32-Bit
# Opera/9.80 (Windows NT 6.1; U; de) Presto/2.10.229 Version/11.61

# Windows 7 64-Bit
# Opera/9.80 (Windows NT 6.1; WOW64; U; de) Presto/2.10.229 Version/11.62

# Samsung Galaxy S
# Opera/9.80 (Android 2.3.3; Linux; Opera Mobi/ADR-1202231246; U; de) Presto/2.10.254 Version/12.00

# Samsung Galaxy Tab 1
# Opera/9.80 (Android 2.2; Linux; Opera Tablet/ADR-1203051631; U; de) Presto/2.10.254 Version/12.00

# Samsung Galaxy ACE:
# Opera/9.80 (Android 2.2.1; Linux; Opera Mobi/ADR-1203051631; U; de) Presto/2.10.254 Version/12.00

# Nokia N8, Symbian S60 5th Ed., S60 Bell
# Opera/9.80 (S60; SymbOS; Opera Mobi/SYB-1111151949; U; de) Presto/2.9.201 Version/11.50


def _lookupWsSupport(ua):
    # Internet Explorer
    ##
    # FIXME: handle Windows Phone
    ##
    if ua.find("MSIE") >= 0:
        # IE10 has native support
        if ua.find("MSIE 10") >= 0:
            # native Hybi-10+
            return True, False, True

        # first, check for Google Chrome Frame
        # http://www.chromium.org/developers/how-tos/chrome-frame-getting-started/understanding-chrome-frame-user-agent
        if ua.find("chromeframe") >= 0:

            r = UA_CHROMEFRAME.match(ua)
            try:
                v = int(r.groups()[0])
                if v >= 14:
                    # native Hybi-10+
                    return True, False, True
            except:
                # detection problem
                return False, False, False

        # Flash fallback
        if ua.find("MSIE 8") >= 0 or ua.find("MSIE 9") >= 0:
            return True, True, True

        # unsupported
        return False, False, True

    # iOS
    ##
    if ua.find("iPhone") >= 0 or ua.find("iPad") >= 0 or ua.find("iPod") >= 0:
        # native Hixie76 (as of March 2012), no Flash, no alternative browsers
        return True, False, True

    # Android
    ##
    if ua.find("Android") >= 0:

        # Firefox Mobile
        ##
        if ua.find("Firefox") >= 0:
            # Hybi-10+ for FF Mobile 8+
            return True, False, True

        # Opera Mobile
        ##
        if ua.find("Opera") >= 0:
            # Hixie76 for Opera 11+
            return True, False, True

        # Chrome for Android
        ##
        if ua.find("CrMo") >= 0:
            # http://code.google.com/chrome/mobile/docs/faq.html
            return True, False, True

        # Android builtin Browser (ooold WebKit)
        ##
        if ua.find("AppleWebKit") >= 0:

            # Though we return WS = True, and Flash = True here, when the device has no actual Flash support, that
            # will get later detected in JS. This applies to i.e. ARMv6 devices like Samsung Galaxy ACE

            # builtin browser, only works via Flash
            return True, True, True

        # detection problem
        return False, False, False

    # webOS
    ##
    if ua.find("hpwOS") >= 0 or ua.find("webos") >= 0:
        try:
            if ua.find("hpwOS") >= 0:
                vv = [int(x) for x in UA_HPWEBOS.match(ua).groups()[0].split('.')]
                if vv[0] >= 3:
                    return True, False, True
            elif ua.find("webos") >= 0:
                vv = [int(x) for x in UA_WEBOS.match(ua).groups()[0].split('.')]
                if vv[0] >= 2:
                    return True, False, True
        except:
            # detection problem
            return False, False, False
        else:
            # unsupported
            return False, False, True

    # Opera
    ##
    if ua.find("Opera") >= 0:
        # Opera 11+ has Hixie76 (needs to be manually activated though)
        return True, False, True

    # Firefox
    ##
    if ua.find("Firefox") >= 0:
        r = UA_FIREFOX.match(ua)
        try:
            v = int(r.groups()[0])
            if v >= 7:
                # native Hybi-10+
                return True, False, True
            elif v >= 3:
                # works with Flash bridge
                return True, True, True
            else:
                # unsupported
                return False, False, True
        except:
            # detection problem
            return False, False, False

    # Safari
    ##
    if ua.find("Safari") >= 0 and not ua.find("Chrome") >= 0:

        # rely on at least Hixie76
        return True, False, True

    # Chrome
    ##
    if ua.find("Chrome") >= 0:
        r = UA_CHROME.match(ua)
        try:
            v = int(r.groups()[0])
            if v >= 14:
                # native Hybi-10+
                return True, False, True
            elif v >= 4:
                # works with Flash bridge
                return True, True, True
            else:
                # unsupported
                return False, False, True
        except:
            # detection problem
            return False, False, False

    # detection problem
    return False, False, False


def lookupWsSupport(ua, debug=True):
    """
    Lookup if browser supports WebSocket (Hixie76, Hybi10+, RFC6455) natively,
    and if not, whether the `web-socket-js <https://github.com/gimite/web-socket-js>`__
    Flash bridge works to polyfill that.

    Returns a tuple of booleans ``(ws_supported, needs_flash, detected)`` where

       * ``ws_supported``: WebSocket is supported
       * ``needs_flash``: Flash Bridge is needed for support
       * ``detected`` the code has explicitly mapped support

    :param ua: The browser user agent string as sent in the HTTP header, e.g. provided as `flask.request.user_agent.string` in Flask.
    :type ua: str

    :returns: tuple -- A tuple ``(ws_supported, needs_flash, detected)``.
    """
    ws = _lookupWsSupport(ua)
    if debug:
        if ua not in UA_DETECT_WS_SUPPORT_DB:
            UA_DETECT_WS_SUPPORT_DB[ua] = ws

        if not ws[2]:
            msg = "UNDETECTED"
        elif ws[0]:
            msg = "SUPPORTED"
        elif not ws[0]:
            msg = "UNSUPPORTED"
        else:
            msg = "ERROR"

        print("DETECT_WS_SUPPORT: %s %s %s %s %s" % (ua, ws[0], ws[1], ws[2], msg))

    return ws
