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

from elementmaker import E
from gettext import gettext as _

from book_list.theme import get_color
from dom import build_rule, clear, svgicon, create_keyframes, set_css, change_icon_image, add_extra_css, ensure_id

# Button {{{


def create_button(text, icon=None, action=None, tooltip=None, highlight=False, download_filename=None, class_=''):
    ic = ''
    if icon:
        ic = svgicon(icon)
        if text:
            text = '\xa0' + text
    cls = ('calibre-push-button ' + (class_ or '')).strip()
    if highlight:
        cls = (cls + ' calibre-primary-button').strip()
    ans = E.a(ic, E.span(text), class_=cls, href='javascript: void(0)', role='button', title=tooltip or '')
    if download_filename and v'"download" in ans':
        ans.setAttribute('download', download_filename)
    if action is not None:
        if jstype(action) is 'string':
            ans.setAttribute('href', action)
        else:
            ans.addEventListener('click', def (event): event.preventDefault(), action(event);)
    return ans

create_button.style += r'''
@media (max-width: 420px) {
  #modal-container .button-box { flex-direction: column; align-items: stretch; }
  #modal-container .button-box > a.calibre-push-button { width: 100%; flex: 1 1 auto; justify-content: center; }
  #modal-container .button-box > a.calibre-push-button:only-of-type { margin-left: 0; }
}
'''
# }}}

# Pager Widget {{{


def create_pager_widget(on_prev, on_next):
    def create_pager_button(is_prev, label, icon, onclick):
        a = E.a(svgicon(icon), E.span(label), href='javascript:void(0)', class_='calibre-push-button')
        if not is_prev:  # next button should have icon on the right
            a.insertBefore(a.firstChild.nextSibling, a.firstChild)
        a.addEventListener('click', def (evt):
            evt.preventDefault()
            onclick()
        )
        return a

    prev_btn = create_pager_button(True, _('Prev'), 'chevron-left', on_prev)
    next_btn = create_pager_button(False, _('Next'), 'chevron-right', on_next)
    buttons_container = E.div(prev_btn, E.div(), next_btn, class_='bottom-row')
    top_row = E.div(class_='top-row hidden')
    middle_row = E.div(class_='middle-row hidden')

    container = E.div(
        top_row,
        E.hr(),
        middle_row,
        E.hr(),
        buttons_container,
        class_='calibre-pager-widget calibre-group-box',
    )

    def update_container_visibility():
        container.style.display = 'none' if (
            buttons_container.classList.contains('hidden')
            and top_row.classList.contains('hidden')
            and middle_row.classList.contains('hidden')
        ) else ''

    def set_enabled(has_prev, has_next):
        prev_btn.setAttribute('aria-disabled', 'false' if has_prev else 'true')
        next_btn.setAttribute('aria-disabled', 'false' if has_next else 'true')
        if has_prev or has_next:
            buttons_container.classList.remove('hidden')
        else:
            buttons_container.classList.add('hidden')
        update_container_visibility()

    def set_visible(which, visible):
        if visible:
            which.classList.remove('hidden')
        else:
            which.classList.add('hidden')
        update_container_visibility()

    return {
        'container': container, 'count_msg': buttons_container.firstChild.nextSibling,
        'prev_btn': prev_btn, 'next_btn': next_btn, 'set_enabled': set_enabled,
        'top_row': top_row, 'middle_row': middle_row,
        'set_top_row_visibility': def (visible): set_visible(top_row, visible);,
        'set_middle_row_visibility': def (visible): set_visible(middle_row, visible);,
    }


create_pager_widget.style = '''
div.calibre-pager-widget {
    display: flex;
    flex-direction: column;

    & > hr {
        margin-top: 0.9ex; margin-bottom: 0.9ex;
    }

    & > .top-row {
        display: flex;
        overflow: hidden;
        gap: 0.75rem;
    }

    & > .middle-row {
        display: flex;
        overflow: hidden;
        flex-wrap: wrap;
        gap: 0.75rem;
    }

    & > .bottom-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 0.75rem;
        flex-wrap: wrap;
        min-width: 0;
        width: 100%;
        box-sizing: border-box;

        & > :first-child, & > :last-child {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            flex: 1 1 auto;
            min-width: 0;
            max-width: 100%;
        }

        & > :first-child { justify-content: flex-start; }
        & > :last-child { justify-content: flex-end; }
        & > :nth-child(2) {
            text-align: center;
            display: block;
            text-overflow: ellipsis;
        }
    }

    & > .hidden {
        display: none;
        & + hr { display: none; }
    }
    & > hr:has(+ .hidden) { display: none; }

}
'''

# }}}


# Search {{{
def create_search_widget(onsearch, value='', placeholder='', tooltip='', live_searching=False):
    box = E.div(
        data_component='search_box',
        style='width: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 0.65rem'
    )
    search = E.input(
        type='search', value=value, placeholder=placeholder, title=tooltip or placeholder, autocomplete='off',
        style='width: 0; flex-grow: 1; min-width: 6em',
    )
    search.addEventListener('input' if live_searching else 'search', def (): onsearch(this.value);)
    box.appendChild(search)
    sb = E.a(
        svgicon('search'),
        class_='calibre-icon-button',
        style='margin-left: auto',
        href='javascript:void(0)',
        title=_('Search'),
        onmousedown=def (evt): evt.preventDefault();,
        onclick=def (evt):
            evt.preventDefault()
            onsearch(this.previousSibling.value)
    )
    box.appendChild(sb)
    return box
# }}}


# Select {{{
def create_select(title, current_value, **options):
    sel = E.select(title=title)
    for value, text in Object.entries(options):
        sel.appendChild(E.option(text, value=value))
    sel.value = current_value

    container = E.div(
        sel, E.span(svgicon('angle-down')),
        data_component='select',
    )
    return container, sel

create_select.style = '''
div[data-component=select] {
    display: inline-flex;
    position: relative;
    align-items: center;
    flex-shrink: 0;

    & > select {
        appearance: none;
        margin: 0;
        padding: 0.35rem 2.0em 0.35rem 0.65rem;
        border-radius: 999px;
        border: var(--calibre-border-style-subtle);
        background-color: var(--calibre-color-window-background);
        color: var(--calibre-color-window-foreground);
        align-self: center;
        box-sizing: border-box;

        &:hover {
            background-color: var(--calibre-color-primary-light);
            border: var(--calibre-border-style);
        }
        &:focus {
            outline: none;
            border: var(--calibre-border-style-subtle);
        }

        & > option {
            background-color: var(--calibre-color-window-background);
            color: var(--calibre-color-input-foreground);
            &:checked { outline-color: var(--calibre-color-primary); }
            &:hover {
                background-color: var(--calibre-color-window-background);
                color: var(--calibre-color-primary);
            }
        }
    }
    & > span {
        position: absolute;
        right: 0.65em;
        top: 50%;
        transform: translateY(-50%);
        pointer-events: none;
        opacity: 0.75;
        color: currentColor;
        display: inline-flex;
        align-items: center;
        justify-content: center;

        & > svg {
            width: 1em;
            height: 1em;
            flex-shrink: 0;
        }
    }
}
'''
# }}}


# Spinner {{{
def create_spinner(height, width):
    ans = svgicon('cog', height, width)
    ans.classList.add('spin')
    return ans

create_spinner.style = build_rule('.spin', animation='spin 2s infinite linear')
create_spinner.style += create_keyframes('spin', 'from { transform: rotate(0deg); } to { transform: rotate(359deg);}')

# }}}

# Breadcrumbs {{{


class Breadcrumbs:
    STYLE_RULES = build_rule(
        '.calibre-breadcrumbs',
        user_select='none', white_space='nowrap', background_color=get_color('window-background2'),
        z_index='-1', border_radius='10px', margin='1ex 1em', margin_bottom='0'
    )

    STYLE_RULES += build_rule(
        '.calibre-breadcrumbs > li',
        cursor='pointer', display='inline-block', line_height='26px', margin='0 9px 0 -10px',
        padding='0.5ex 1rem', position='relative'
    )

    STYLE_RULES += build_rule('.calibre-breadcrumbs > li:hover', color=get_color('window-hover-foreground'))

    STYLE_RULES += build_rule('.calibre-breadcrumbs > li:active', color=get_color('window-hover-foreground'), transform='scale(1.5)')

    STYLE_RULES += build_rule(
        '.calibre-breadcrumbs > li:before, .calibre-breadcrumbs > li:after',
        border_right='2px solid currentColor', content='""', display='block', height='50%',
        position='absolute', left='0', top='0', right='0', transform='skewX(45deg)'
    )

    STYLE_RULES += build_rule(
        '.calibre-breadcrumbs > li:after',
        bottom='0', top='auto', transform='skewX(-45deg)'
    )

    STYLE_RULES += build_rule(
        '.calibre-breadcrumbs > li:last-of-type:before, .calibre-breadcrumbs > li:last-of-type:after',
        display='none')

    def __init__(self, container):
        self.container_id = ensure_id(container, 'calibre-breadcrumbs-')
        container.classList.add('calibre-breadcrumbs')
        clear(container)

    @property
    def container(self):
        return document.getElementById(self.container_id)

    def reset(self):
        clear(self.container)

    def add_crumb(self, callback):
        li = E.li()
        if callback:
            li.addEventListener('click', callback)
        self.container.appendChild(li)
        return li
# }}}

# Simple Tree {{{


def create_tree(root, populate_data, onclick):
    container = E.div(class_='simple-tree')
    set_css(container, overflow='auto')

    def toggle_node(li):
        if li.dataset.treeState is 'closed':
            li.dataset.treeState = 'open'
            li.lastChild.style.display = 'block'
            change_icon_image(li.firstChild.firstChild, 'caret-down')
        else:
            li.dataset.treeState = 'closed'
            li.lastChild.style.display = 'none'
            change_icon_image(li.firstChild.firstChild, 'caret-right')

    def bullet(icon):
        ans = svgicon(icon, '2ex', '2ex')
        ans.style.minWidth = '2ex'
        ans.style.minHeight = '2ex'
        return ans

    def process_node(node, parent_container, level):
        if node.children?.length:
            ul = E.div()
            parent_container.appendChild(ul)
            for child in node.children:
                icon = 'caret-right' if child.children?.length else None
                li = E.div(style='display:flex; flex-direction:column; margin: 1ex 1em; margin-left: {}em'.format(level + 1),
                    E.div(style='display:flex; align-items: center',
                        bullet(icon),
                        E.span('\xa0'),
                        E.a(
                            href='javascript: void(0)',
                            class_='tree-item-title',
                            onclick=def (event):
                                if onclick:
                                    if event.button is 0:
                                        event.preventDefault(), event.stopPropagation()
                                    onclick(event, event.currentTarget.parentNode.parentNode)
                        ),
                    ),
                    E.div(style='display:none', data_tree_subtree_container='1'),
                    data_tree_state='closed',
                )
                ul.appendChild(li)
                populate_data(child, li, li.firstChild.lastChild)
                if icon:
                    set_css(li.firstChild.firstChild, cursor='pointer')
                    li.firstChild.firstChild.addEventListener('click', def (event):
                        toggle_node(event.currentTarget.parentNode.parentNode)
                    )
                    process_node(child, li.lastChild, level + 1)

    if root:
        process_node(root, container, 0)
    return container


def find_text_in_tree(container, q):
    q = q.lower()
    last_match = container.querySelector('a[data-tree-last-match]')
    if last_match:
        last_match.parentNode.style.backgroundColor = 'transparent'
        last_match.parentNode.style.borderRadius = '0'
        lm = last_match.getAttribute('data-tree-last-match')
        last_match.removeAttribute('data-tree-last-match')
        if lm is not q:
            last_match = None
    if not q:
        return
    before = []
    seen = False
    ans = None

    for a in container.querySelectorAll('a.tree-item-title'):
        if a is last_match:
            seen = True
        else:
            if a.textContent.lower().indexOf(q) != -1:
                if seen:
                    ans = a
                    break
                if last_match is None:
                    ans = a
                    break
                before.push(a)
    if not ans and before.length:
        ans = before[0]
    ans = ans or last_match
    if ans:
        ans.dataset.treeLastMatch = q
    if ans:
        ans.parentNode.style.backgroundColor = get_color('tree-highlight-item')
        ans.parentNode.style.borderRadius = '5px'
        ans = ans.parentNode.parentNode
    return ans


def scroll_tree_item_into_view(item):
    p = item.parentNode?.parentNode
    while p and p.getAttribute('data-tree-subtree-container'):
        p.style.display = 'block'
        p = p.parentNode?.parentNode?.parentNode
    item.scrollIntoView()

# }}}

add_extra_css(def ():
    ans = 'a, button:focus { outline: none }; a, button::-moz-focus-inner { border: 0 }\n'
    ans += (
        '.simple-link { cursor: pointer } .simple-link:hover { color: HC }'
        ' .simple-link:active { transform: scale(1.5) }\n'
    ).replace('HC', get_color('window-hover-foreground'))
    ans += '''
        a.tree-item-title {
            color: currentColor;
            cursor: pointer;
            text-decoration: none;
            &:hover, &:active {
                color: var(--calibre-color-primary);
                text-decoration: none;
            }
        }
    '''
    # Use theme primary color for all "blue-link" links.
    ans += (
        '.blue-link { cursor: pointer; color: var(--calibre-color-primary) }'
        ' .blue-link:visited { color: var(--calibre-color-primary)}'
        ' .blue-link:hover { color: var(--calibre-color-primary) }'
        ' .blue-link:active { transform: scale(1.5) }\n'
    )
    ans += create_button.style + '\n'
    ans += create_pager_widget.style + '\n'
    ans += create_spinner.style + '\n'
    ans += Breadcrumbs.STYLE_RULES + '\n'
    ans += create_select.style + '\n'
    # Chrome hardcodes the size of these to 13.3px. In safari on some devices
    # the font size is smaller than the body font size and small enough to
    # trigger page zooms. So set it to 1em so they scale with body font size.
    ans += '''
        select, textarea, input[type="text"], input[type="password"],
        input[type="datetime"], input[type="datetime-local"],
        input[type="date"], input[type="month"], input[type="time"],
        input[type="week"], input[type="number"], input[type="email"],
        input[type="url"], input[type="search"] { font-size: 1em }
    '''
    return ans
)


def enable_escape_key(container, action):
    container.setAttribute('tabindex', '0')
    container.addEventListener('keydown', def (ev):
        if ev.key is 'Escape':
            ev.stopPropagation(), ev.preventDefault()
            action()
    , {'passive': False})
    container.focus()
