# -*- coding: utf-8 -*-
"""
    Copyright (C) 2017 Sebastian Golasch (plugin.video.netflix)
    Copyright (C) 2020 Stefano Gottardo - @CastagnaIT (original implementation module)
    Miscellaneous utility functions related to the device

    SPDX-License-Identifier: MIT
    See LICENSES/MIT.md for more information.
"""
import xbmc

from resources.lib.globals import G
from resources.lib.utils.esn import WidevineForceSecLev
from resources.lib.utils.logging import LOG


def select_port(service):
    """Select an unused port on the host machine for a server and store it in the settings"""
    port = select_unused_port()
    G.LOCAL_DB.set_value(f'{service.lower()}_service_port', port)
    LOG.info('[{}] Picked Port: {}', service, port)
    return port


def select_unused_port():
    """
    Helper function to select an unused port on the host machine

    :return: int - Free port
    """
    import socket
    from contextlib import closing
    # pylint: disable=no-member
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
        sock.bind(('127.0.0.1', 0))
        _, port = sock.getsockname()
        return port


def get_system_platform():
    if not hasattr(get_system_platform, 'cached'):
        platform = "unknown"
        if xbmc.getCondVisibility('system.platform.linux') and not xbmc.getCondVisibility('system.platform.android'):
            platform = "linux"
        elif xbmc.getCondVisibility('system.platform.linux') and xbmc.getCondVisibility('system.platform.android'):
            platform = "android"
        elif xbmc.getCondVisibility('system.platform.uwp'):
            platform = "uwp"
        elif xbmc.getCondVisibility('system.platform.windows'):
            platform = "windows"
        elif xbmc.getCondVisibility('system.platform.osx'):
            platform = "osx"
        elif xbmc.getCondVisibility('system.platform.ios'):
            platform = "ios"
        elif xbmc.getCondVisibility('system.platform.tvos'):
            platform = "tvos"
        get_system_platform.cached = platform
    return get_system_platform.cached


def get_machine():
    """Get machine architecture"""
    from platform import machine
    try:
        return machine()
    except Exception:  # pylint: disable=broad-except
        # Due to OS restrictions on 'ios' and 'tvos' this generate an exception
        # See python limits in the wiki development page
        # Fallback with a generic arm
        return 'arm'


def is_device_4k_capable():
    """Check if the device is 4k capable"""
    # Currently only on android is it possible to use 4K
    if get_system_platform() == 'android':
        from resources.lib.database.db_utils import TABLE_SESSION
        # Check if the drm has security level L1
        wv_force_sec_lev = G.LOCAL_DB.get_value('widevine_force_seclev',
                                                WidevineForceSecLev.DISABLED,
                                                table=TABLE_SESSION)
        is_l3_forced = wv_force_sec_lev != WidevineForceSecLev.DISABLED
        is_drm_l1_security_level = (G.LOCAL_DB.get_value('drm_security_level', '', table=TABLE_SESSION) == 'L1'
                                    and not is_l3_forced)
        # Check if HDCP level is 2.2 or up
        hdcp_level = get_hdcp_level()
        hdcp_4k_capable = hdcp_level and hdcp_level >= 2.2
        return bool(is_drm_l1_security_level and hdcp_4k_capable)
    return False


def get_hdcp_level():
    """Get the HDCP level when exist else None"""
    from re import findall
    from resources.lib.database.db_utils import TABLE_SESSION
    drm_hdcp_level = findall('\\d+\\.\\d+', G.LOCAL_DB.get_value('drm_hdcp_level', '', table=TABLE_SESSION))
    return float(drm_hdcp_level[0]) if drm_hdcp_level else None


def get_user_agent(enable_android_mediaflag_fix=False):
    """
    Determines the user agent string for the current platform.
    Needed to retrieve a valid ESN (except for Android, where the ESN can be generated locally)

    :returns: str -- User agent string
    """
    system = get_system_platform()
    if enable_android_mediaflag_fix and system == 'android' and is_device_4k_capable():
        # The UA affects not only the ESNs in the login, but also the video details,
        # so the UAs seem refer to exactly to these conditions: https://help.netflix.com/en/node/23742
        # This workaround is needed because currently we do not login through the netflix native android API,
        # but redirect everything through the website APIs, and the website APIs do not really support android.
        # Then on android usually we use the 'arm' UA which refers to chrome os, but this is limited to 1080P, so the
        # labels on the 4K devices appears wrong (in the Kodi skin the 4K videos have 1080P media flags instead of 4K),
        # the Windows UA is not limited, so we can use it to get the right video media flags.
        system = 'windows'

    chrome_version = 'Chrome/96.0.4664.77'
    base = 'Mozilla/5.0 '
    base += '%PL% '
    base += 'AppleWebKit/537.36 (KHTML, like Gecko) '
    base += '%CH_VER% Safari/537.36'.replace('%CH_VER%', chrome_version)

    if system in ['osx', 'ios', 'tvos']:
        return base.replace('%PL%', '(Macintosh; Intel Mac OS X 10_15_5)')
    if system in ['windows', 'uwp']:
        return base.replace('%PL%', '(Windows NT 10.0; Win64; x64)')
    # ARM based Linux
    machine_arch = get_machine()
    if machine_arch.startswith('arm'):
        # Last number is the platform version of Chrome OS
        return base.replace('%PL%', '(X11; CrOS armv7l 14324.33.0)')
    if machine_arch.startswith('aarch'):
        # Last number is the platform version of Chrome OS
        return base.replace('%PL%', '(X11; CrOS aarch64 14324.33.0)')
    # x86 Linux
    return base.replace('%PL%', '(X11; Linux x86_64)')


def is_internet_connected():
    """
    Check internet status
    :return: True if connected
    """
    if not xbmc.getCondVisibility('System.InternetState'):
        # Double check when Kodi say that it is not connected
        # i'm not sure the InfoLabel will work properly when Kodi was started a few seconds ago
        # using getInfoLabel instead of getCondVisibility often return delayed results..
        return _check_internet()
    return True


def _check_internet():
    """
    Checks via socket if the internet works (in about 0,7sec with no timeout error)
    :return: True if connected
    """
    import socket
    for timeout in [1, 1]:
        try:
            socket.setdefaulttimeout(timeout)
            host = socket.gethostbyname("www.google.com")
            s = socket.create_connection((host, 80), timeout)
            s.close()
            return True
        except Exception:  # pylint: disable=broad-except
            # Error when is not reachable
            pass
    return False
