# Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
from urwid import (
    LineBox,
    Text,
    )

from subiquitycore.view import BaseView
from subiquitycore.ui.buttons import (
    cancel_btn,
    danger_btn,
    ok_btn,
    other_btn,
    )
from subiquitycore.ui.container import Columns, ListBox, Pile
from subiquitycore.ui.form import Toggleable
from subiquitycore.ui.spinner import Spinner
from subiquitycore.ui.utils import button_pile, Padding, rewrap
from subiquitycore.ui.stretchy import Stretchy
from subiquitycore.ui.width import widget_width


log = logging.getLogger("subiquity.views.installprogress")


class MyLineBox(LineBox):
    def format_title(self, title):
        if title:
            return [" ", title, " "]
        else:
            return ""


class ProgressView(BaseView):

    title = _("Install progress")

    def __init__(self, controller):
        self.controller = controller
        self.ongoing = {}  # context -> line containing a spinner

        self.reboot_btn = Toggleable(ok_btn(
            _("Reboot Now"), on_press=self.reboot))
        self.view_error_btn = cancel_btn(
            _("View error report"), on_press=self.view_error)
        self.view_log_btn = other_btn(
            _("View full log"), on_press=self.view_log)
        self.continue_btn = other_btn(
            _("Continue"), on_press=self.continue_)

        self.event_listbox = ListBox()
        self.event_linebox = MyLineBox(self.event_listbox)
        self.event_buttons = button_pile([self.view_log_btn])
        event_body = [
            ('weight', 1, Padding.center_79(self.event_linebox, min_width=76)),
            ('pack', Text("")),
            ('pack', self.event_buttons),
            ('pack', Text("")),
        ]
        self.event_pile = Pile(event_body)

        self.log_listbox = ListBox()
        log_linebox = MyLineBox(self.log_listbox, _("Full installer output"))
        log_body = [
            ('weight', 1, log_linebox),
            ('pack', button_pile([other_btn(_("Close"),
                                  on_press=self.close_log)])),
            ]
        self.log_pile = Pile(log_body)

        super().__init__(self.event_pile)

    def _add_line(self, lb, line):
        lb = lb.base_widget
        walker = lb.body
        at_end = len(walker) == 0 or lb.focus_position == len(walker) - 1
        walker.append(line)
        if at_end:
            lb.set_focus(len(walker) - 1)
            lb.set_focus_valign('bottom')

    def event_start(self, context, message):
        self.event_finish(context.parent)
        walker = self.event_listbox.base_widget.body
        indent = context.full_name().count('/') - 2
        if context.get('is-install-context'):
            indent -= 1
        spinner = Spinner(self.controller.app.aio_loop)
        spinner.start()
        new_line = Columns([
            ('pack', Text('  ' * indent + message)),
            ('pack', spinner),
            ], dividechars=1)
        self.ongoing[context] = len(walker)
        self._add_line(self.event_listbox, new_line)

    def event_finish(self, context):
        index = self.ongoing.pop(context, None)
        if index is None:
            return
        walker = self.event_listbox.base_widget.body
        spinner = walker[index][1]
        spinner.stop()
        walker[index] = walker[index][0]

    def finish_all(self):
        for context in self.ongoing.copy():
            self.event_finish(context)

    def add_log_line(self, text):
        self._add_line(self.log_listbox, Text(text))

    def set_status(self, text):
        self.event_linebox.set_title(text)

    def _set_button_width(self):
        w = 14
        for b, o in self.event_buttons.original_widget.contents:
            w = max(widget_width(b), w)
        self.event_buttons.width = self.event_buttons.min_width = w

    def _set_buttons(self, buttons):
        p = self.event_buttons.original_widget
        p.contents[:] = [(b, p.options('pack')) for b in buttons]
        self._set_button_width()

    def update_running(self):
        self.reboot_btn.base_widget.set_label(_("Cancel update and reboot"))
        self._set_button_width()

    def update_done(self):
        self.reboot_btn.base_widget.set_label(_("Reboot"))
        self._set_button_width()

    def show_complete(self):
        btns = [self.view_log_btn, self.reboot_btn]
        self._set_buttons(btns)
        self.event_buttons.base_widget.focus_position = 1
        self.event_pile.base_widget.focus_position = 2

    def show_continue(self):
        btns = [self.continue_btn, self.reboot_btn]
        self._set_buttons(btns)
        self.event_buttons.base_widget.focus_position = 0
        self.event_pile.base_widget.focus_position = 2

    def continue_(self, sender=None):
        self.controller.app.next_screen()

    def hide_continue(self):
        btns = [self.view_log_btn]
        self._set_buttons(btns)
        self.event_buttons.base_widget.focus_position = 0
        self.event_pile.base_widget.focus_position = 2

    def show_error(self, crash_report):
        btns = [self.view_log_btn, self.view_error_btn, self.reboot_btn]
        self._set_buttons(btns)
        self.event_buttons.base_widget.focus_position = 1
        self.event_pile.base_widget.focus_position = 2
        self.crash_report = crash_report
        self.controller.app.show_error_report(crash_report)

    def reboot(self, btn):
        self.reboot_btn.base_widget.set_label(_("Rebooting..."))
        self.reboot_btn.enabled = False
        self.event_buttons.original_widget._select_first_selectable()
        self.controller.click_reboot()
        self._set_button_width()

    def view_error(self, btn):
        self.controller.app.show_error_report(self.crash_report)

    def view_log(self, btn):
        self._w = self.log_pile

    def close_log(self, btn):
        self._w = self.event_pile


confirmation_text = _("""\
Selecting Continue below will begin the installation process and
result in the loss of data on the disks selected to be formatted.

You will not be able to return to this or a previous screen once
the installation has started.

Are you sure you want to continue?""")


class InstallConfirmation(Stretchy):
    def __init__(self, parent, app):
        self.parent = parent
        self.app = app
        widgets = [
            Text(rewrap(_(confirmation_text))),
            Text(""),
            button_pile([
                cancel_btn(_("No"), on_press=self.cancel),
                danger_btn(_("Continue"), on_press=self.ok)]),
            ]
        super().__init__(
            _("Confirm destructive action"),
            widgets,
            stretchy_index=0,
            focus_index=2)

    def ok(self, sender):
        self.app.confirm_install()
        self.app.remove_global_overlay(self)
        if isinstance(self.app.ui.body, ProgressView):
            self.app.ui.body.hide_continue()
        self.app.next_screen()

    def cancel(self, sender):
        self.app.remove_global_overlay(self)
        if isinstance(self.app.ui.body, ProgressView):
            self.app.ui.body.show_continue()


running_text = _("""\
The installer running on {tty} is currently installing the system.

You can wait for this to complete or switch to a shell.
""")


class InstallRunning(Stretchy):
    def __init__(self, parent, app, tty):
        self.parent = parent
        self.app = app
        self.btn = Toggleable(other_btn(
                _("Switch to a shell"), on_press=self._debug_shell))
        self.btn.enabled = False
        self.app.aio_loop.call_later(0.5, self._enable)
        widgets = [
            Text(rewrap(_(running_text).format(tty=tty))),
            Text(''),
            button_pile([self.btn]),
            ]
        super().__init__(
            _(""),
            widgets,
            stretchy_index=0,
            focus_index=2)

    def _enable(self):
        self.btn.enabled = True

    def _debug_shell(self, sender):
        self.app.debug_shell()
