"""
Module defining the global info bar

This window is the one listing the current opened tabs in poezio.
The GlobalInfoBar can be either horizontal or vertical
(VerticalGlobalInfoBar).
"""
import curses
import itertools
import logging

from typing import List, Optional, TYPE_CHECKING

from poezio.config import config
from poezio.windows.base_wins import Win
from poezio.theming import get_theme, to_curses_attr, TextStyle
from poezio.common import unique_prefix_of
from poezio.colors import ccg_text_to_color

if TYPE_CHECKING:
    from poezio.core.core import Core

log = logging.getLogger(__name__)


class GlobalInfoBar(Win):
    __slots__ = ('core')

    def __init__(self, core: 'Core') -> None:
        Win.__init__(self)
        self.core = core

    def refresh(self) -> None:
        log.debug('Refresh: %s', self.__class__.__name__)
        self._win.erase()
        theme = get_theme()
        self.addstr(0, 0, "[",
                    to_curses_attr(theme.COLOR_INFORMATION_BAR))

        show_names = config.getbool('show_tab_names')
        show_nums = config.getbool('show_tab_numbers')
        use_nicks = config.getbool('use_tab_nicks')
        show_inactive = config.getbool('show_inactive_tabs')
        unique_prefix_tab_names = config.getbool('unique_prefix_tab_names')
        autocolor_tab_names = config.getbool('autocolor_tab_names')

        if unique_prefix_tab_names:
            unique_prefixes: List[Optional[str]] = [None] * len(self.core.tabs)
            sorted_tab_indices = sorted(
                (str(tab.name), i)
                for i, tab in enumerate(self.core.tabs)
            )
            prev_name = ""
            for (name, i), next_item in itertools.zip_longest(
                    sorted_tab_indices, sorted_tab_indices[1:]):
                # TODO: should this maybe use something smarter than .lower()?
                # something something stringprep?
                name = name.lower()
                prefix_prev = unique_prefix_of(name, prev_name)
                if next_item is not None:
                    prefix_next = unique_prefix_of(name, next_item[0].lower())
                else:
                    prefix_next = name[0]

                # to be unique, we have to use the longest prefix
                if len(prefix_next) > len(prefix_prev):
                    prefix = prefix_next
                else:
                    prefix = prefix_prev

                unique_prefixes[i] = prefix
                prev_name = name

        for nb, tab in enumerate(self.core.tabs):
            if not tab:
                continue
            color: TextStyle = tab.color
            if not show_inactive and color is theme.COLOR_TAB_NORMAL and (
                    tab.priority < 0):
                continue
            if autocolor_tab_names:
                # TODO: in case of private MUC conversations, we should try to
                # get hold of more information to make the colour the same as
                # the nickname colour in the MUC.
                fgcolor, bgcolor, *flags_list = color

                flags = ''.join(flags_list)

                # this is fugly, but I’m not sure how to improve it... since
                # apparently the state is only kept in the color -.-
                if (color == theme.COLOR_TAB_HIGHLIGHT or
                        color == theme.COLOR_TAB_PRIVATE):
                    fgcolor = ccg_text_to_color(theme.ccg_palette, tab.name)
                    bgcolor = -1
                    flags = theme.MODE_TAB_IMPORTANT
                elif color == theme.COLOR_TAB_NEW_MESSAGE:
                    fgcolor = ccg_text_to_color(theme.ccg_palette, tab.name)
                    bgcolor = -1
                    flags = theme.MODE_TAB_NORMAL

                color = (fgcolor, bgcolor, flags)
            try:
                if show_nums or not show_names:
                    self.addstr("%s" % str(nb), to_curses_attr(color))
                    if show_names:
                        self.addstr(' ', to_curses_attr(color))
                if show_names:
                    if unique_prefix_tab_names:
                        self.addstr(unique_prefixes[nb], to_curses_attr(color))
                    elif use_nicks:
                        self.addstr("%s" % str(tab.get_nick()),
                                    to_curses_attr(color))
                    else:
                        self.addstr("%s" % tab.name, to_curses_attr(color))
                self.addstr("|",
                            to_curses_attr(theme.COLOR_INFORMATION_BAR))
            except Exception:  # end of line
                break
        (y, x) = self._win.getyx()
        self.addstr(y, x - 1, '] ',
                    to_curses_attr(theme.COLOR_INFORMATION_BAR))
        (y, x) = self._win.getyx()
        remaining_size = self.width - x
        self.addnstr(' ' * remaining_size, remaining_size,
                     to_curses_attr(theme.COLOR_INFORMATION_BAR))
        self._refresh()


class VerticalGlobalInfoBar(Win):
    __slots__ = ('core')

    def __init__(self, core: 'Core', scr: curses.window) -> None:
        Win.__init__(self)
        self.core = core
        self._win = scr

    def refresh(self) -> None:
        height, width = self._win.getmaxyx()
        self._win.erase()
        sorted_tabs = [tab for tab in self.core.tabs if tab]
        theme = get_theme()
        if not config.getbool('show_inactive_tabs'):
            sorted_tabs = [
                tab for tab in sorted_tabs
                if (
                    tab.vertical_color != theme.COLOR_VERTICAL_TAB_NORMAL or
                    tab.priority > 0
                )
            ]
        nb_tabs = len(sorted_tabs)
        use_nicks = config.getbool('use_tab_nicks')
        if nb_tabs >= height:
            # TODO: As sorted_tabs filters out gap tabs this ensures pos is
            # always set, preventing UnboundLocalError. Now is this how this
            # should be fixed.
            pos = 0
            for y, tab in enumerate(sorted_tabs):
                if tab.vertical_color == theme.COLOR_VERTICAL_TAB_CURRENT:
                    pos = y
                    break
            # center the current tab as much as possible
            if pos < height // 2:
                sorted_tabs = sorted_tabs[:height]
            elif nb_tabs - pos <= height // 2:
                sorted_tabs = sorted_tabs[-height:]
            else:
                sorted_tabs = sorted_tabs[pos - height // 2:pos + height // 2]
        asc_sort = (config.getstr('vertical_tab_list_sort') == 'asc')
        for y, tab in enumerate(sorted_tabs):
            color = tab.vertical_color
            if asc_sort:
                y = height - y - 1
            self.addstr(y, 0, "%2d" % tab.nb,
                        to_curses_attr(theme.COLOR_VERTICAL_TAB_NUMBER))
            self.addstr('.')
            if use_nicks:
                self.addnstr("%s" % tab.get_nick(), width - 4,
                             to_curses_attr(color))
            else:
                self.addnstr("%s" % tab.name, width - 4, to_curses_attr(color))
        separator = to_curses_attr(theme.COLOR_VERTICAL_SEPARATOR)
        self._win.attron(separator)
        self._win.vline(0, width - 1, curses.ACS_VLINE, height)
        self._win.attroff(separator)
        self._refresh()
