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

from elementmaker import E
from gettext import gettext as _

from ajax import absolute_path, encode_query
from book_list.constants import book_list_container_id, read_book_container_id, cs_top_bar_host_id
from book_list.globals import get_current_query, get_session_data
from book_list.library_data import current_library_id
from dom import clear
from modals import close_all_modals
from read_book.globals import ui_operations
from utils import (
    encode_query_with_path, parse_url_params, request_full_screen,
    safe_set_inner_html
)

mode_handlers = {}
default_mode_handler = None
read_book_mode = 'read_book'


def set_mode_handler(mode, handler):
    mode_handlers[mode] = handler


def set_default_mode_handler(handler):
    nonlocal default_mode_handler
    default_mode_handler = handler


def update_window_title(subtitle, title='calibre', sep=' :: '):
    extra = (sep + subtitle) if subtitle else ''
    document.title = title + extra


def is_reading_book():
    cq = get_current_query()
    return cq and cq.mode is read_book_mode


def apply_mode():
    divid = read_book_container_id if is_reading_book() else book_list_container_id
    for div in document.getElementById(divid).parentNode.childNodes:
        div.style.display = 'block' if div.id is divid else 'none'
    if is_reading_book():
        host = document.getElementById(cs_top_bar_host_id)
        if host:
            clear(host)


def apply_url(ignore_handler):
    # Ensure the current history entry has a state marker, so back() works
    # reliably even after a page refresh.
    try:
        if not (window.history.state and window.history.state.cs_url_state?):
            window.history.replaceState({'cs_url_state': True, 'cs_idx': 0}, '', window.location.href)
    except Exception:
        pass
    close_all_modals()  # needed to close any error dialogs, etc when clicking back or forward in the browser or using push_state() to go to a new page
    prev = get_current_query()
    data = parse_url_params()
    data.mode = data.mode or 'book_list'
    if prev?.mode is read_book_mode and data.mode is not read_book_mode:
        # Ensure read-aloud/TTS is stopped when leaving reader mode from
        # global navigation paths that do not explicitly call close_book().
        try:
            if ui_operations.close_book:
                ui_operations.close_book()
        except Exception:
            pass
    get_current_query(data)
    apply_mode()
    # Allow the main UI (e.g. sidebar) to update highlighting when URL changes
    try:
        cb = window.cs_update_sidebar_active
    except Exception:
        cb = None
    if cb:
        try:
            cb()
        except Exception:
            pass
    if not ignore_handler:
        handler = mode_handlers[data.mode] or default_mode_handler
        handler(data)


def request_full_screen_if_wanted():
    opt = get_session_data().get('fullscreen_when_opening')
    has_touch = v'"ontouchstart" in window'
    at_left = window.screenLeft is 0
    likely_wants_fullscreen = has_touch and at_left
    if (opt is 'auto' and likely_wants_fullscreen) or opt is 'always':
        # Note that full screen requests only succeed if they are in response to a
        # user action like clicking/tapping a button
        request_full_screen()


def open_book(book_id, fmt, library_id=None, replace=False, extra_query=None):
    request_full_screen_if_wanted()
    library_id = library_id or current_library_id()
    q = {'book_id': book_id, 'fmt': fmt, 'library_id': library_id}
    if extra_query and jstype(extra_query) is 'object':
        Object.assign(q, extra_query)
    push_state(q, replace=replace, mode=read_book_mode)


def open_book_url_in_library(library_id, book_id, fmt, extra_query):
    ans = absolute_path('')
    q = {'book_id': book_id, 'fmt': fmt, 'mode': read_book_mode}
    if library_id:
        q.library_id = library_id
    if extra_query:
        Object.assign(q, extra_query)
    return ans + encode_query(q, '#')


def search_url_in_library(library_id, search, vl, sort):
    ans = absolute_path('')
    q = {'mode': 'book_list', 'search': search, 'panel': 'book_list'}
    if library_id:
        q.library_id = library_id
    if vl:
        q.vl = vl
    if sort:
        q.sort = sort
    return ans + encode_query(q, '#')


def open_book_url(book_id, fmt, extra_query):
    return open_book_url_in_library(current_library_id(), book_id, fmt, extra_query)


def show_note(field, item_id, item_value, replace=False, library_id=None, close_action='back', panel='show_note'):
    lid = library_id or current_library_id()
    q = {'field': field, 'item': item_value + '', 'item_id': (item_id or '') + '', 'panel': panel}
    if panel is 'show_note':
        q.close_action = close_action
    if lid:
        q.library_id = lid
    push_state(q, replace=replace)


def push_state(query, replace=False, mode='book_list', call_handler=True):
    query = {k: query[k] for k in query if query[k]}
    if mode is not 'book_list':
        query.mode = mode
    query = encode_query_with_path(query)
    try:
        cur = window.history.state or {}
        cur_idx = cur.cs_idx or 0
        if replace:
            window.history.replaceState({'cs_url_state': True, 'cs_idx': cur_idx}, '', query)
        else:
            window.history.pushState({'cs_url_state': True, 'cs_idx': cur_idx + 1}, '', query)
    except Exception:
        # Fallback to old behavior if history state is unavailable for some reason
        if replace:
            window.history.replaceState(None, '', query)
        else:
            window.history.pushState(None, '', query)
    apply_url(not call_handler)


def on_pop_state(ev):
    apply_url()


def back():
    try:
        idx = (window.history.state and window.history.state.cs_idx) or 0
    except Exception:
        idx = 0
    if idx > 0:
        window.history.back()
    else:
        cq = get_current_query()
        handler = mode_handlers[cq.mode] or default_mode_handler
        if handler.back_from_current:
            q = handler.back_from_current(cq)
        else:
            q = {}
        push_state(q, replace=True)


def home(replace=False):
    push_state({})


def report_a_load_failure(container, msg, error_html):
    err = E.div()
    safe_set_inner_html(err, error_html)
    container.appendChild(E.div(
        style='margin: 1ex 1em',
        E.div(msg),
        err,
        E.div(
            style='margin-top: 1em; border-top: solid 1px currentColor; padding-top: 1ex;',
            E.a(onclick=def (): home(replace=True);, href='javascript: void(0)', class_='blue-link', _('Go back to the home page')))
        ),
    )
