###############################################################################
##
##  Copyright 2011-2013 Tavendo GmbH
##
##  Licensed 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.
##
###############################################################################

__all__ = ("lookupWsSupport",)


import re

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*.*")



# 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)


UA_DETECT_WS_SUPPORT_DB = {}

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 the support/nosupport

   :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 not UA_DETECT_WS_SUPPORT_DB.has_key(ua):
         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", ua, ws[0], ws[1], ws[2], msg

   return ws
