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

import traceback
from elementmaker import E
from gettext import gettext as _, install

from ajax import ajax
from book_list.book_details import set_panel_handlers as book_details_init
from book_list.constants import book_list_container_id, read_book_container_id, cs_top_bar_host_id, INIT_ENDPOINT
from book_list.convert_book import set_panel_handlers as convert_book_init
from book_list.edit_metadata import set_panel_handlers as edit_metadata_init
from book_list.fts import set_panel_handlers as fts_init
from book_list.globals import get_db, set_session_data
from book_list.home import set_panel_handlers as home_init
from book_list.home import update_recently_read_by_user
from book_list.library_data import fetch_init_data, update_library_data, url_books_query
from book_list.local_books import set_panel_handlers as local_books_init
from book_list.nav_list import set_panel_handlers as nav_list_init
from book_list.router import update_window_title, set_default_mode_handler, apply_url, set_mode_handler, on_pop_state
from book_list.show_note import set_panel_handlers as show_note_init
from book_list.sidebar import create_sidebar
from book_list.theme import get_color, get_font_family, css_for_variables, apply_book_list_theme, bind_book_list_color_scheme_listener
from book_list.ui import apply_url_state as book_list_mode_handler
from book_list.ui_settings import set_panel_handlers as ui_settings_init
from book_list.views import set_panel_handlers as views_init
from dom import get_widget_css
from modals import create_modal_container, error_dialog
from popups import install_event_filters
from read_book.ui import ReadUI
from session import get_interface_data, UserSessionData, update_interface_data, get_translations
from utils import safe_set_inner_html


def remove_initial_progress_bar():
    p = document.getElementById('page_load_progress')
    if p:
        p.parentNode.removeChild(p)


source_map_set = False


def source_map_received(data, end_type, xhr, ev):
    if end_type is 'abort':
        return
    if end_type is 'load' and xhr.responseType is 'json':
        traceback.set_source_map_data(xhr.response)
    error_object = traceback.unserialize_error_for_formatting(data.error)
    details = traceback.format_exception(error_object).join('')
    error_dialog(data.title, data.msg, details)


def load_source_map(data):
    nonlocal source_map_set
    xhr = ajax('index.js.map', source_map_received.bind(None, data))
    xhr.responseType = 'json'
    xhr.send()
    source_map_set = True


def onerror(msg, script_url, line_number, column_number, error_object):
    if error_object is None:
        # This happens for cross-domain errors (probably javascript injected
        # into the browser via extensions/ userscripts and the like). It also
        # happens all the time when using Chrom on Safari, so ignore this
        # type of error
        console.log(f'Unhandled error from external javascript, ignoring: {msg} {script_url} {line_number}')
        return
    console.log(error_object)
    fname = script_url.rpartition('/')[-1] or script_url
    msg = msg + '<br><span style="font-size:smaller">' + 'Error at {}:{}:{}'.format(fname, line_number, column_number or '') + '</span>'
    title = _('Unhandled error')
    if source_map_set:
        details = traceback.format_exception(error_object).join('')
        error_dialog(title, msg, details)
    else:
        load_source_map({'title': title, 'msg': msg, 'error': traceback.serialize_error_for_formatting(error_object)})


def init_ui():
    document.body.style.fontFamily = get_font_family()
    install_event_filters()
    set_default_mode_handler(book_list_mode_handler)
    window.onerror = onerror
    setTimeout(def ():
            window.onpopstate = on_pop_state
        , 0)  # We do this after event loop ticks over to avoid catching popstate events that some browsers send on page load

    translations = get_translations()
    install(translations)
    remove_initial_progress_bar()
    document.head.appendChild(E.style(css_for_variables() + '\n\n' + get_widget_css()))
    apply_book_list_theme()
    bind_book_list_color_scheme_listener()
    root, left = create_sidebar()

    shell_top = E.div(class_='cs-shell-top')
    shell_top.appendChild(E.div(id=cs_top_bar_host_id))
    main_row = E.div(class_='cs-main')
    main_body = E.div(class_='cs-main-body')
    main_body.appendChild(E.div(id=book_list_container_id, style='display: none'))
    main_body.appendChild(E.div(id=read_book_container_id, style='display: none'))
    main_row.appendChild(left)
    main_row.appendChild(main_body)
    root.appendChild(shell_top)
    root.appendChild(main_row)
    document.body.appendChild(root)

    create_modal_container()
    update_window_title()
    read_ui = ReadUI()
    get_db(read_ui.db)
    book_details_init()
    convert_book_init()
    local_books_init()
    edit_metadata_init()
    fts_init()
    home_init()
    nav_list_init()
    show_note_init()
    ui_settings_init()
    views_init()

    set_mode_handler('read_book', read_ui.apply_url_state.bind(read_ui))

    apply_url()


def install_data_and_init_ui(raw_data):
    data = JSON.parse(raw_data)
    update_interface_data(data)
    update_recently_read_by_user(data.recently_read_by_user)
    interface_data = get_interface_data()
    sd = UserSessionData(interface_data.username, interface_data.user_session_data)
    set_session_data(sd)
    update_library_data(data)
    if data.translations?:
        get_translations(data.translations)
    init_ui()


def on_data_loaded(end_type, xhr, ev):
    remove_initial_progress_bar()
    if end_type is 'load':
        install_data_and_init_ui(xhr.responseText)
    else:
        p = E.p(style='color:{}; font-weight: bold; font-size:1.5em'.format(get_color('window-error-foreground')))
        if xhr.status is 401:
            msg = _('You are not authorized to view this site')
        else:
            msg = xhr.error_html
        safe_set_inner_html(p, msg)
        document.body.appendChild(p)


def on_data_load_progress(loaded, total):
    p = document.querySelector('#page_load_progress > progress')
    if p:
        p.max = total
        p.value = loaded


def load_interface_data():
    idata = get_interface_data()
    query = {}
    if not idata.is_default:
        temp = UserSessionData(None, {})  # So that settings for anonymous users are preserved
        query = url_books_query(temp)
    ajax(INIT_ENDPOINT, on_data_loaded, on_data_load_progress, query=query).send()


def do_update_interface_data():
    t = get_translations()
    thash = t?.hash
    url = 'interface-data/update'
    if thash:
        url += '/' + thash
    ajax(url, def (end_type, xhr, ev):
        if end_type is 'load':
            data = JSON.parse(xhr.responseText)
            update_interface_data(data)
            update_recently_read_by_user(data.recently_read_by_user)
            if data.translations?:
                get_translations(data.translations)
                install(data.translations)
        else:
            update_recently_read_by_user()
    ).send()


@no_prune
def main():
    if document.createElement('iframe').srcdoc is undefined:
        document.body.innerHTML = '<p style="margin:1.5em; max-width: 40em">' + _(
            'You are using a browser that does not have the iframe srcdoc attribute.'
        ' This is not supported. Use a better browser such as <b>Google Chrome</b> or <b>Mozilla'
        ' Firefox</b>, instead. You can also use the simplified <a href="mobile">/mobile</a>'
        ' interface if you do not have access to a modern browser.')
        return
    interface_data = get_interface_data()
    if interface_data.is_default or not interface_data.library_map:
        load_interface_data()
    else:
        sd = UserSessionData(interface_data.username, interface_data.user_session_data)
        set_session_data(sd)
        do_update_interface_data()
        fetch_init_data()
        init_ui()
