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

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

from ajax import encode_query_component
from book_list.item_list import build_list, create_item
from dom import ensure_id, set_css
from modals import error_dialog
from read_book.globals import current_book, runtime, ui_operations
from read_book.prefs.head_foot import format_pos
from read_book.toc import get_border_nodes, get_toc_maps
from widgets import create_button


def create_goto_list(onclick, current_position_data, page_list, show_sub_panel=None):
    ans = E.div()
    items = []
    location_text = format_pos(current_position_data.progress_frac, current_position_data.book_length) + ' :: '
    if current_position_data.cfi:
        location_text += current_position_data.cfi
    landmarks = current_book().manifest.landmarks
    toc = current_book().manifest.toc
    id_map = get_toc_maps(toc)[1]
    before, after = get_border_nodes(toc, id_map)
    if after:
        items.push(create_item(_('Next section'), icon='caret-right', subtitle=after.title, action=onclick.bind(None, after.dest, after.frag)))
    if before:
        items.push(create_item(_('Previous section'), icon='caret-left', subtitle=before.title, action=onclick.bind(None, before.dest, before.frag)))
    items.push(create_item(_('Book start'), action=onclick.bind(None, def (view): view.goto_doc_boundary(True);)))
    items.push(create_item(_('Book end'), action=onclick.bind(None, def (view): view.goto_doc_boundary(False);)))

    def panel_action(name, fallback_fn):
        # Use show_sub_panel when available so the goto panel stays in the
        # stack and closing the sub-panel returns here; otherwise fall back
        # to the regular onclick which closes the overlay first.
        if show_sub_panel:
            return def (): show_sub_panel(name)
        return onclick.bind(None, fallback_fn)

    items.push(create_item(_('Metadata'), subtitle=_('Details about this book'),
        action=panel_action('metadata', def (view): view.overlay.show_metadata();)))
    if not runtime.is_standalone_viewer:
        items.push(create_item(_('Book page in library'), subtitle=_('The page for this book in the calibre library'), action=onclick.bind(None, def (view):
            view.open_book_page()
        )))
    items.push(create_item(_('Location'), subtitle=location_text,
        action=panel_action('location', def (view): view.overlay.show_ask_for_location();)))
    if page_list and page_list.length > 0:
        items.push(create_item(_('Page number'), subtitle=_('Typically the page number from a paper edition of this book'),
            action=panel_action('page_list', def (view): view.overlay.show_page_list(page_list);)))
    for l in landmarks:
        items.push(create_item(l.title, action=onclick.bind(None, l.dest, l.frag)))
    build_list(ans, items)
    return ans


def get_next_section(forward):
    toc = current_book().manifest.toc
    id_map = get_toc_maps(toc)[1]
    before, after = get_border_nodes(toc, id_map)
    return after if forward else before


def create_goto_panel(current_position_data, book, container, onclick, show_sub_panel=None):
    panel = create_goto_list(onclick, current_position_data, book.manifest.page_list, show_sub_panel)
    set_css(container, display='flex', flex_direction='column')
    set_css(panel, flex_grow='10')
    container.appendChild(panel)


def create_page_list_overlay(book, overlay, container):
    list_container = E.div()
    pl = overlay.view.current_pagelist_items

    def goto(x):
        overlay.view.goto_pagelist_item(x)
        overlay.hide()

    def goto_matching_page(text):
        for x in page_list:
            if x.pagenum is text:
                goto(x)
                return
        q = text.toLowerCase()
        for x in page_list:
            if x.pagenum.toLowerCase() is q:
                goto(x)
                return
        error_dialog(_('No such page'), _('No page {!r} exists').format(text))

    if pl and pl.length > 0:
        if pl.length is 1:
            text = _('Currently on page: {}').format(pl[0].pagenum)
        else:
            text = _('Currently on pages: {}').format(pl[0].pagenum + ' - ' + pl[1].pagenum)
        container.appendChild(E.div(style='margin: 1em', text))
    search_input = E.div(
        E.div(
            E.input(type='text', placeholder=_('Enter a page number'), onkeydown=def (ev):
                    if ev.key is 'Enter':
                        text = ev.target.value
                        if text:
                            goto_matching_page(text)
            ),
        ), style='margin: 1em',
    )
    container.appendChild(search_input)
    container.appendChild(list_container)
    page_list = book.manifest.page_list or []
    items = []

    for x in page_list:
        items.push(create_item(x.pagenum, action=goto.bind(None, x),
    ))
    build_list(list_container, items)


def create_location_overlay(current_position_data, book, overlay, container):
    container_id = ensure_id(container)
    container.appendChild(E.div(style='margin: 0 1rem'))
    container = container.lastChild
    current_cfi = current_position_data.cfi
    calibre_book_url = book?.calibre_book_url

    def copy_button(text_to_copy):
        return create_button(_('Copy'), action=def ():
                src = document.querySelector(f'#{container_id} input')
                orig = src.value
                src.value = text_to_copy
                src.focus()
                src.select()
                try:
                    document.execCommand('copy')
                finally:
                    src.value = orig
            )

    def display_and_copy(label, text):
        container.appendChild(E.div(
            style='margin: 1rem; margin-bottom: calc(1rem - 1ex); display: flex; align-items: baseline; flex-wrap: wrap',
            E.div(style='flex-grow: 10; text-overflow: ellipsis; margin-bottom: 1ex',
                  label, ' ', E.span(text, style='font-size: smaller; font-family: monospace; overflow-wrap: anywhere')),
            copy_button(text)
        ))

    if current_cfi:
        display_and_copy(_('Current location:'), current_cfi)

    def goto_cfi(cfi):
        if ui_operations.goto_cfi(cfi):
            overlay.hide()
        else:
            error_dialog(_('No such location'), _(
                'No location {} found').format(cfi))

    def goto_ref(ref):
        ref = ref.replace(/,/g, '.')
        if ui_operations.goto_reference(ref):
            overlay.hide()
        else:
            error_dialog(_('No such reference'), _(
                'No reference {} found').format(ref))

    if current_position_data.book_length > 0:
        container.appendChild(
            E.div(style='margin: 1rem', _('Current position: {}').format(
                format_pos(current_position_data.progress_frac, current_position_data.book_length))))

    container.appendChild(
        E.div(style='margin: 1rem', _(
            'Type the position, location or reference below. For a reference type ref: followed by the reference:')))

    def goto_pos():
        src = document.querySelector(f'#{container_id} [name=newpos]').value
        if not src:
            return
        if src.indexOf('epubcfi(') is 0:
            return goto_cfi(src)
        if src.indexOf('ref:') is 0:
            return goto_ref(src[len('ref:'):])
        try:
            ui_operations.goto_book_position(float(src))
        except:
            error_dialog(_('Not a valid book position'), _(
                '{} is not a valid book position').format(src), traceback.format_exc())
        else:
            overlay.hide()

    container.appendChild(E.div(
        style='margin: 1rem;',
        E.div(
            style='display: flex; align-items: baseline; flex-wrap: wrap',
            E.label(_('Go to:'), style='margin-right: 1rem'),
            E.input(name='newpos', type='text', style='flex-grow: 10; margin-right: 1rem', onkeydown=def (ev):
                    if ev.key is 'Enter':
                        goto_pos()
            ), E.span(' '),
            create_button(_('Go'), action=goto_pos)
        )
    ))
    container.appendChild(
        E.div(style='margin: 1rem', _(
            'Note that using a position is not very robust, it is best to use either the location or reference.')))

    if calibre_book_url:
        if current_cfi:
            calibre_book_url += '?open_at=' + encode_query_component(current_cfi)
        display_and_copy(_('URL for this position:'), calibre_book_url)
    elif not runtime.is_standalone_viewer:
        display_and_copy(_('URL for this position:'), window.top.location.toString())
    window.setTimeout(def ():
        container = document.getElementById(container_id)
        if container:
            container.querySelector('[name=newpos]').focus()
    , 10)
