import html
import sublime
import sublime_plugin
from . import emmet_sublime as emmet
from . import syntax
from .utils import get_caret, go_to_pos
from .telemetry import track_action

previews_by_buffer = {}
phantoms_by_buffer = {}
phantom_key = 'emmet_tag_preview'
max_preview_len = 100


def show_tag_preview(view: sublime.View, pt: int, text: str, dest: int):
    "Displays given tag preview at `pt` location"
    buffer_id = view.buffer_id()
    if buffer_id not in phantoms_by_buffer:
        phantom_set = sublime.PhantomSet(view, phantom_key)
        phantoms_by_buffer[buffer_id] = phantom_set
    else:
        phantom_set = phantoms_by_buffer[buffer_id]

    r = sublime.Region(pt, pt)
    nav = lambda href: go_to_pos(view, int(href))
    phantoms = [sublime.Phantom(r, phantom_content(text, dest), sublime.LAYOUT_INLINE, on_navigate=nav)]
    phantom_set.update(phantoms)


def hide_tag_preview(view: sublime.View):
    "Hides tag preview in given view"
    buffer_id = view.buffer_id()

    if buffer_id in phantoms_by_buffer:
        del phantoms_by_buffer[buffer_id]
        view.erase_phantoms(phantom_key)


def phantom_content(content: str, dest: int):
    "Returns contents for phantom preview"
    return """
    <body>
        <style>
            body {
                background-color: color(var(--accent) alpha(0.3));
                color: var(--foreground);
                border-radius: 3px;
                padding: 0px 3px;
                opacity: 0.2;
                font-size: 1rem;
            }
            a {
                text-decoration: none;
                color: var(--foreground);
            }
        </style>
        <div class="main"><a href="%d">%s</a></div>
    </body>
    """ % (dest, html.escape(content, False))


class EmmetGoToTagPair(sublime_plugin.TextCommand):
    def run(self, edit: sublime.Edit):
        caret = get_caret(self.view)
        if self.view.substr(caret) == '<':
            caret += 1

        syntax_name = syntax.from_pos(self.view, caret)
        if syntax.is_html(syntax_name):
            ctx = emmet.get_tag_context(self.view, caret, syntax.is_xml(syntax_name))
            if ctx and 'open' in ctx and 'close' in ctx:
                open_tag = ctx['open']
                close_tag = ctx['close']
                pos = close_tag.begin() if open_tag.contains(caret) else open_tag.begin()
                go_to_pos(self.view, pos)

        track_action('Go to Tag Pair')


class EmmetHideTagPreview(sublime_plugin.TextCommand):
    def run(self, edit: sublime.Edit):
        buffer_id = self.view.buffer_id()
        if buffer_id in previews_by_buffer:
            pt, visible = previews_by_buffer[buffer_id]
            if visible:
                previews_by_buffer[buffer_id] = (pt, False)


def allow_preview(fn):
    "Method decorator for running action callbacks for in allowed tag preview context"
    def wrapper(self, view):
        if not view.settings().get('is_widget') and emmet.get_settings('tag_preview'):
            fn(self, view)
    return wrapper

class PreviewTagPair(sublime_plugin.EventListener):
    def on_query_context(self, view: sublime.View, key: str, *args):
        if key == 'emmet_tag_preview':
            buffer_id = view.buffer_id()
            if buffer_id in previews_by_buffer:
                return previews_by_buffer[buffer_id][1]
        return None

    @allow_preview
    def on_selection_modified_async(self, view: sublime.View):
        caret = get_caret(view)
        syntax_name = syntax.from_pos(view, caret)
        buffer_id = view.buffer_id()

        if syntax.is_html(syntax_name):
            ctx = emmet.get_tag_context(view, caret, syntax.is_xml(syntax_name))
            if ctx and 'close' in ctx and ctx['close'].contains(caret) and \
                not view.visible_region().contains(ctx['open']):
                pos = ctx['close'].b

                # Do not display preview if user forcibly hides it with Esc key
                # for current location
                if buffer_id in previews_by_buffer:
                    pt = previews_by_buffer[buffer_id][0]
                    if pt == pos:
                        return

                preview = view.substr(ctx['open'])
                if len(preview) > max_preview_len:
                    preview = '%s...' % preview[0:max_preview_len]
                show_tag_preview(view, pos, preview, ctx['open'].a)
                previews_by_buffer[buffer_id] = (pos, True)
                return

        hide_tag_preview(view)
        if buffer_id in previews_by_buffer:
            del previews_by_buffer[buffer_id]