# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#

"""Curses browser interface class ."""

import math
import curses
from glances.outputs.glances_curses import _GlancesCurses

from glances.logger import logger
from glances.timer import Timer


class GlancesCursesBrowser(_GlancesCurses):
    """Class for the Glances curse client browser."""

    def __init__(self, args=None):
        """Init the father class."""
        super(GlancesCursesBrowser, self).__init__(args=args)

        _colors_list = {
            'UNKNOWN': self.no_color,
            'SNMP': self.default_color2,
            'ONLINE': self.default_color2,
            'OFFLINE': self.ifCRITICAL_color2,
            'PROTECTED': self.ifWARNING_color2,
        }
        self.colors_list.update(_colors_list)

        # First time scan tag
        # Used to display a specific message when the browser is started
        self.first_scan = True

        # Init refresh time
        self.__refresh_time = args.time

        # Init the cursor position for the client browser
        self.cursor_position = 0

        # Active Glances server number
        self._active_server = None

        self._current_page = 0
        self._page_max = 0
        self._page_max_lines = 0

        self.is_end = False
        self._revesed_sorting = False
        self._stats_list = None

    @property
    def active_server(self):
        """Return the active server or None if it's the browser list."""
        return self._active_server

    @active_server.setter
    def active_server(self, index):
        """Set the active server or None if no server selected."""
        self._active_server = index

    @property
    def cursor(self):
        """Get the cursor position."""
        return self.cursor_position

    @cursor.setter
    def cursor(self, position):
        """Set the cursor position."""
        self.cursor_position = position

    def get_pagelines(self, stats):
        if self._current_page == self._page_max - 1:
            page_lines = len(stats) % self._page_max_lines
        else:
            page_lines = self._page_max_lines
        return page_lines

    def _get_status_count(self, stats):
        counts = {}
        for item in stats:
            color = item['status']
            counts[color] = counts.get(color, 0) + 1

        result = ''
        for key in counts.keys():
            result += key + ': ' + str(counts[key]) + ' '

        return result

    def _get_stats(self, stats):
        stats_list = None
        if self._stats_list is not None:
            stats_list = self._stats_list
            stats_list.sort(
                reverse=self._revesed_sorting,
                key=lambda x: {'UNKNOWN': 0, 'OFFLINE': 1, 'PROTECTED': 2, 'SNMP': 3, 'ONLINE': 4}.get(x['status'], 99),
            )
        else:
            stats_list = stats

        return stats_list

    def cursor_up(self, stats):
        """Set the cursor to position N-1 in the list."""
        if 0 <= self.cursor_position - 1:
            self.cursor_position -= 1
        else:
            if self._current_page - 1 < 0:
                self._current_page = self._page_max - 1
                self.cursor_position = (len(stats) - 1) % self._page_max_lines
            else:
                self._current_page -= 1
                self.cursor_position = self._page_max_lines - 1

    def cursor_down(self, stats):
        """Set the cursor to position N-1 in the list."""

        if self.cursor_position + 1 < self.get_pagelines(stats):
            self.cursor_position += 1
        else:
            if self._current_page + 1 < self._page_max:
                self._current_page += 1
            else:
                self._current_page = 0
            self.cursor_position = 0

    def cursor_pageup(self, stats):
        """Set prev page."""
        if self._current_page - 1 < 0:
            self._current_page = self._page_max - 1
        else:
            self._current_page -= 1
        self.cursor_position = 0

    def cursor_pagedown(self, stats):
        """Set next page."""
        if self._current_page + 1 < self._page_max:
            self._current_page += 1
        else:
            self._current_page = 0
        self.cursor_position = 0

    def __catch_key(self, stats):
        # Catch the browser pressed key
        self.pressedkey = self.get_key(self.term_window)
        refresh = False
        if self.pressedkey != -1:
            logger.debug("Key pressed. Code=%s" % self.pressedkey)

        # Actions...
        if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'):
            # 'ESC'|'q' > Quit
            self.end()
            logger.info("Stop Glances client browser")
            # sys.exit(0)
            self.is_end = True
        elif self.pressedkey == 10:
            # 'ENTER' > Run Glances on the selected server
            self.active_server = self._current_page * self._page_max_lines + self.cursor_position
            logger.debug("Server {}/{} selected".format(self.active_server, len(stats)))
        elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65:
            # 'UP' > Up in the server list
            self.cursor_up(stats)
            logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats)))
        elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66:
            # 'DOWN' > Down in the server list
            self.cursor_down(stats)
            logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats)))
        elif self.pressedkey == curses.KEY_PPAGE:
            # 'Page UP' > Prev page in the server list
            self.cursor_pageup(stats)
            logger.debug("PageUP: Server ({}/{}) pages.".format(self._current_page + 1, self._page_max))
        elif self.pressedkey == curses.KEY_NPAGE:
            # 'Page Down' > Next page in the server list
            self.cursor_pagedown(stats)
            logger.debug("PageDown: Server {}/{} pages".format(self._current_page + 1, self._page_max))
        elif self.pressedkey == ord('1'):
            self._stats_list = None
            refresh = True
        elif self.pressedkey == ord('2'):
            self._revesed_sorting = False
            self._stats_list = stats.copy()
            refresh = True
        elif self.pressedkey == ord('3'):
            self._revesed_sorting = True
            self._stats_list = stats.copy()
            refresh = True

        if refresh:
            self._current_page = 0
            self.cursor_position = 0
            self.flush(stats)

        # Return the key code
        return self.pressedkey

    def update(self, stats, duration=3, cs_status=None, return_to_browser=False):
        """Update the servers' list screen.

        Wait for __refresh_time sec / catch key every 100 ms.

        :param stats: Dict of dict with servers stats
        :param cs_status:
        :param duration:
        :param return_to_browser:
        """
        # Flush display
        logger.debug('Servers list: {}'.format(stats))
        self.flush(stats)

        # Wait
        exitkey = False
        countdown = Timer(self.__refresh_time)
        while not countdown.finished() and not exitkey:
            # Getkey
            pressedkey = self.__catch_key(stats)
            # Is it an exit or select server key ?
            exitkey = pressedkey == ord('\x1b') or pressedkey == ord('q') or pressedkey == 10
            if not exitkey and pressedkey > -1:
                # Redraw display
                self.flush(stats)
            # Wait 100ms...
            self.wait()

        return self.active_server

    def flush(self, stats):
        """Update the servers' list screen.

        :param stats: List of dict with servers stats
        """
        self.erase()
        self.display(stats)

    def display(self, stats, cs_status=None):
        """Display the servers list.

        :return: True if the stats have been displayed else False (no server available)
        """
        # Init the internal line/column for Glances Curses
        self.init_line_column()

        # Get the current screen size
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        stats_max = screen_y - 3
        stats_len = len(stats)

        self._page_max_lines = stats_max
        self._page_max = int(math.ceil(stats_len / stats_max))
        # Init position
        x = 0
        y = 0

        # Display top header
        if stats_len == 0:
            if self.first_scan and not self.args.disable_autodiscover:
                msg = 'Glances is scanning your network. Please wait...'
                self.first_scan = False
            else:
                msg = 'No Glances server available'
        elif len(stats) == 1:
            msg = 'One Glances server available'
        else:
            msg = '{} Glances servers available'.format(stats_len)
        if self.args.disable_autodiscover:
            msg += ' (auto discover is disabled)'
        if screen_y > 1:
            self.term_window.addnstr(y, x, msg, screen_x - x, self.colors_list['TITLE'])

            msg = '{}'.format(self._get_status_count(stats))
            self.term_window.addnstr(y + 1, x, msg, screen_x - x)

        if stats_len > stats_max and screen_y > 2:
            msg = '{} servers displayed.({}/{}) {}'.format(
                self.get_pagelines(stats), self._current_page + 1, self._page_max, self._get_status_count(stats)
            )
            self.term_window.addnstr(y + 1, x, msg, screen_x - x)

        if stats_len == 0:
            return False

        # Display the Glances server list
        # ================================

        # Table of table
        # Item description: [stats_id, column name, column size]
        column_def = [
            ['name', 'Name', 16],
            ['alias', None, None],
            ['load_min5', 'LOAD', 6],
            ['cpu_percent', 'CPU%', 5],
            ['mem_percent', 'MEM%', 5],
            ['status', 'STATUS', 9],
            ['ip', 'IP', 15],
            # ['port', 'PORT', 5],
            ['hr_name', 'OS', 16],
        ]
        y = 2

        # Display table header
        xc = x + 2
        for cpt, c in enumerate(column_def):
            if xc < screen_x and y < screen_y and c[1] is not None:
                self.term_window.addnstr(y, xc, c[1], screen_x - x, self.colors_list['BOLD'])
                xc += c[2] + self.space_between_column
        y += 1

        # If a servers has been deleted from the list...
        # ... and if the cursor is in the latest position
        if self.cursor > len(stats) - 1:
            # Set the cursor position to the latest item
            self.cursor = len(stats) - 1

        stats_list = self._get_stats(stats)
        start_line = self._page_max_lines * self._current_page
        end_line = start_line + self.get_pagelines(stats_list)
        current_page = stats_list[start_line:end_line]

        # Display table
        line = 0
        for v in current_page:
            # Limit the number of displayed server (see issue #1256)
            if line >= stats_max:
                continue
            # Get server stats
            server_stat = {}
            for c in column_def:
                try:
                    server_stat[c[0]] = v[c[0]]
                except KeyError as e:
                    logger.debug("Cannot grab stats {} from server (KeyError: {})".format(c[0], e))
                    server_stat[c[0]] = '?'
                # Display alias instead of name
                try:
                    if c[0] == 'alias' and v[c[0]] is not None:
                        server_stat['name'] = v[c[0]]
                except KeyError:
                    pass

            # Display line for server stats
            cpt = 0
            xc = x

            # Is the line selected ?
            if line == self.cursor:
                # Display cursor
                self.term_window.addnstr(y, xc, ">", screen_x - xc, self.colors_list['BOLD'])

            # Display the line
            xc += 2
            for c in column_def:
                if xc < screen_x and y < screen_y and c[1] is not None:
                    # Display server stats
                    self.term_window.addnstr(y, xc, format(server_stat[c[0]]), c[2], self.colors_list[v['status']])
                    xc += c[2] + self.space_between_column
                cpt += 1
            # Next line, next server...
            y += 1
            line += 1

        return True
