# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals

from encodings import hexlify

from ajax import encode_query
from book_list.theme import get_font_family


is_ios = v'!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)'
ios_major_version = 0
if ! is_ios and v'!!navigator.platform' and window? and window.navigator.platform is 'MacIntel' and window.navigator.maxTouchPoints > 1:
    # iPad Safari in desktop mode https://stackoverflow.com/questions/57765958/how-to-detect-ipad-and-ipad-os-version-in-ios-13-and-up
    is_ios = True
if is_ios:
    v = v'navigator.appVersion.match(/OS (\d+)/)'
    if v and v[1]:
        ios_major_version = parseInt(v[1], 10) or 0


def default_context_menu_should_be_allowed(evt):
    if evt.target and evt.target.tagName and evt.target.tagName.toLowerCase() in ('input', 'textarea'):
        return True
    return False


def debounce(func, wait, immediate=False):
    # Returns a function, that, as long as it continues to be invoked, will not
    # be triggered. The function will be called after it stops being called for
    # wait milliseconds. If `immediate` is True, trigger the function on the
    # leading edge, instead of the trailing.
    timeout = None
    return def debounce_inner():  # noqa: unused-local
        nonlocal timeout
        context, args = this, arguments

        def later():
            nonlocal timeout
            timeout = None
            if not immediate:
                func.apply(context, args)
        call_now = immediate and not timeout
        window.clearTimeout(timeout)
        timeout = window.setTimeout(later, wait)
        if call_now:
            func.apply(context, args)

if Object.assign:
    copy_hash = def (obj):
        return Object.assign({}, obj)
else:
    copy_hash = def (obj):
        return {k: obj[k] for k in Object.keys(obj)}


def parse_url_params(url=None, allow_multiple=False):
    cache = parse_url_params.cache
    url = url or window.location.href
    if cache[url]:
        return copy_hash(parse_url_params.cache[url])
    qs = url.indexOf('#')
    ans = {}
    if qs < 0:
        cache[url] = ans
        return copy_hash(ans)
    q = url.slice(qs + 1, (url.length + 1))
    if not q:
        cache[url] = ans
        return copy_hash(ans)
    pairs = q.replace(/\+/g, ' ').split('&')
    for pair in pairs:
        key, val = pair.partition('=')[::2]
        key, val = decodeURIComponent(key), decodeURIComponent(val)
        if allow_multiple:
            if ans[key] is undefined:
                ans[key] = []
            ans[key].append(val)
        else:
            ans[key] = val
    cache[url] = ans
    return copy_hash(ans)
parse_url_params.cache = {}


def encode_query_with_path(query, path):
    path = path or window.location.pathname
    return path + encode_query(query, '#')


def full_screen_supported(elem):
    elem = elem or document.documentElement
    if elem.requestFullScreen or elem.webkitRequestFullScreen or elem.mozRequestFullScreen:
        return True
    return False


def request_full_screen(elem):
    elem = elem or document.documentElement
    options = {'navigationUI': 'hide'}
    if elem.requestFullScreen:
        elem.requestFullScreen(options)
    elif elem.webkitRequestFullScreen:
        elem.webkitRequestFullScreen()
    elif elem.mozRequestFullScreen:
        elem.mozRequestFullScreen()


def full_screen_element():
    return document.fullscreenElement or document.webkitFullscreenElement or document.mozFullScreenElement or document.msFullscreenElement


_roman = list(zip(
[1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
))


def roman(num):
    if num <= 0 or num >= 4000 or int(num) is not num:
        return num + ''
    result = []
    for d, r in _roman:
        while num >= d:
            result.append(r)
            num -= d
    return result.join('')


def fmt_sidx(val, fmt='{:.2f}', use_roman=True):
    if val is undefined or val is None or val is '':
        return '1'
    if int(val) is float(val):
        if use_roman:
            return roman(val)
        return int(val) + ''
    return fmt.format(float(val))


def rating_to_stars(value, allow_half_stars=False, star='★', half='⯨'):
    r = max(0, min(int(value or 0), 10))
    if allow_half_stars:
        ans = star.repeat(r // 2)
        if r % 2:
            ans += half
    else:
        ans = star.repeat(int(r / 2.0))
    return ans


def human_readable(size, sep=' '):
    if size <= 0:
        return f'0{sep}B'
    i = Math.floor(Math.log(size) / Math.log(1024))
    size = (size / Math.pow(1024, i)).toFixed(1)
    if size.endswith('.0'):
        size = size[:-2]
    suffix = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'][i]
    return size + sep + suffix


def document_height():
    html = document.documentElement
    return max(document.body.scrollHeight, document.body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)


def document_width():
    html = document.documentElement
    return max(document.body.scrollWidth, document.body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth)

_data_ns = None


def data_ns(name):
    nonlocal _data_ns
    if _data_ns is None:
        rand = Uint8Array(12)
        window.crypto.getRandomValues(rand)
        _data_ns = 'data-' + hexlify(rand) + '-'
    return _data_ns + name


def get_elem_data(elem, name, defval):
    ans = elem.getAttribute(data_ns(name))
    if ans is None:
        return defval ? None
    return JSON.parse(ans)


def set_elem_data(elem, name, val):
    elem.setAttribute(data_ns(name), JSON.stringify(val))


def username_key(username):
    return ('u' if username else 'n') + username


def html_escape(text):
    repl = {'&': '&amp;', '"': '&quot;', '<': '&lt;', '>': '&gt;'}
    return String.prototype.replace.call(text, /[&"<>]/g, def (c): return repl[c];)


def uniq(vals):
    # Remove all duplicates from vals, while preserving order
    ans = []
    seen = {}
    for x in vals:
        if not seen[x]:
            seen[x] = True
            ans.push(x)
    return ans


def conditional_timeout(elem_id, timeout, func):
    def ct_impl():
        elem = document.getElementById(elem_id)
        if elem:
            func.call(elem)
    window.setTimeout(ct_impl, timeout)


def simple_markup(html):
    html = (html or '').replace(/\uffff/g, '').replace(
        /<\s*(\/?[a-zA-Z1-6]+)[^>]*>/g, def (match, tag):
            tag = tag.toLowerCase()
            is_closing = '/' if tag[0] is '/' else ''
            if is_closing:
                tag = tag[1:]
            if simple_markup.allowed_tags.indexOf(tag) < 0:
                tag = 'span'
            return f'\uffff{is_closing}{tag}\uffff'
    )
    div = document.createElement('b')
    div.textContent = html
    html = div.innerHTML
    return html.replace(/\uffff(\/?[a-z1-6]+)\uffff/g, '<$1>')
simple_markup.allowed_tags = v"'a|b|i|br|hr|h1|h2|h3|h4|h5|h6|div|em|strong|span'.split('|')"


def safe_set_inner_html(elem, html):
    elem.innerHTML = simple_markup(html)
    return elem


def sandboxed_html(html, style, sandbox):
    ans = document.createElement('iframe')
    ans.setAttribute('sandbox', sandbox or '')
    ans.setAttribute('seamless', '')
    ans.style.width = '100%'
    html = html or ''
    css = 'html, body { margin: 0; padding: 0; font-family: __FONT__ } p:first-child { margin-top: 0; padding-top: 0; -webkit-margin-before: 0 }'.replace(
        '__FONT__',
        get_font_family()
    )
    css += style or ''
    final_html = f'<!DOCTYPE html><html><head><style>{css}</style></head><body>{html}</body></html>'
    ans.srcdoc = final_html
    return ans
