# configscreen.py - Copyright (C) 2009 Red Hat, Inc.
# Written by Darryl L. Pierce <dpierce@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301, USA.  A copy of the GNU General Public License is
# also available at http://www.gnu.org/copyleft/gpl.html.

from snack import *

import _snack
import traceback

BACK_BUTTON   = "back"
NEXT_BUTTON   = "next"
CANCEL_BUTTON = "cancel"
FINISH_BUTTON = "finish"

class ConfigScreen:
    '''
    Enables the creation of navigable, multi-paged configuration screens.

    colorset - The colors to be used for items displayed. This is a dictionary
               where the key value specifies the widget, and the value is an
               array of two values: the foreground color and the background
               color, respectively. See the COLORS file for details on what
               colors, and widget names, are available
    '''

    def __init__(self, title, colorset = None):
        self.__title = title
        self.__current_page = 1
        self.__finished = False
        self.__colorset = colorset

    def get_title(self):
        """
        Returns the title to be displayed for this application.
        """
        return self.__title

    def set_finished(self):
        self.__finished = True

    def get_elements_for_page(self, screen, page):
        return []

    def page_has_back(self, page):
        return False

    def page_has_next(self, page):
        return False

    def page_has_finish(self, page):
        return False

    def get_back_page(self, page):
        if page > 1: return page - 1
        return page

    def go_back(self):
        self.__current_page = self.get_back_page(self.__current_page)

    def get_next_page(self, page):
        return page + 1

    def go_next(self):
        self.__current_page = self.get_next_page(self.__current_page)

    def validate_input(self, page, errors):
        return True

    def process_input(self, page):
        return

    def get_page_list(self):
        return []

    def get_current_page(self):
        0

    def use_compact_buttons(self):
        """
        If this returns True, then compact buttons will be used
        on all displays.

        The default is to use normal buttons.
        """
        False

    def get_window_position(self):
        """
        Returns the desired window position for the application.
        """
        return (0, 0)

    def use_full_screen(self):
        """
        Returns whether the application should use full screen windows.

        Default: False
        """
        False

    def start(self):
        active = True
        while active and (self.__finished == False):
            screen = SnackScreen()
            # apply any colorsets that were provided.
            if self.__colorset is not None:
                for item in self.__colorset.keys():
                    colors = self.__colorset.get(item)
                    screen.setColor(item, colors[0], colors[1])
            elements = self.get_elements_for_page(screen, self.__current_page)
            # TODO: need to set the form height to the number of elements on the page
            gridform = GridForm(screen, self.get_title(), 4, 1)

            # Here you would put the list of elements
            # and programmatically set the indicator as
            # they're rendered
            content = Grid(1, len(elements) + 2) # extra = button bar + padding row
            pages = self.get_page_list()
            if len(pages) > 0:
                leftmenu = Grid(2, len(pages))
                current_element = 0
                for page in pages:
                    leftmenu.setField(Label(page), 0, current_element, anchorLeft = 1)
                    indicator = " "
                    if current_element == self.__current_page - 1:
                        indicator = "<-"
                    leftmenu.setField(Label(indicator), 1, current_element)
                    current_element += 1
                gridform.add(leftmenu, 1, 0,
                             anchorTop = 1, anchorLeft = 1,
                             growx = 0)

            current_element = 0
            for element in elements:
                # set the title of the page
                content.setField(element, 0, current_element, anchorLeft = 1)
                current_element += 1
            (fullwidth, fullheight) = _snack.size()
            if self.use_full_screen():
                content.setField(Label(""), 0, current_element,
                                 padding = ((fullwidth / 2) - 2, 0,
                                            (fullwidth / 2) - 2, 0),
                                 growx = 1)
            current_element += 1

            # create the navigation buttons
            buttons = []
            compact_buttons = 0
            if self.use_compact_buttons():
                compact_buttons = 1
            if self.page_has_back(self.__current_page):
                buttons.append(["Back", BACK_BUTTON, "F11"])
            if self.page_has_next(self.__current_page):
                buttons.append(["Next", NEXT_BUTTON, "F12"])
            if self.page_has_finish(self.__current_page):
                buttons.append(["Finish", FINISH_BUTTON, "F10"])
            buttons.append(["Cancel", CANCEL_BUTTON, "ESC"])
            buttonbar = ButtonBar(screen, buttons, compact = compact_buttons)
            content.setField(buttonbar, 0, current_element, anchorLeft = 1, growx = 0)
            if self.use_full_screen():
                gridform.add(Label(""), 0, 0,
                             padding = (0, (fullheight / 2) - 2,
                                        0, (fullheight / 2) - 2),
                             growy = 1)
                gridform.add(Label("  "), 2, 0, anchorTop = 1)
            gridform.add(content, 3, 0, anchorTop = 1)
            current_element += 1
            try:
                # get the position for the main window
                if self.use_full_screen():
                    (top, left) = (1, 1)
                else:
                    (top, left) = (None, None)
                result = gridform.runOnce(top, left)
                pressed = buttonbar.buttonPressed(result)
                if pressed == BACK_BUTTON:
                    self.go_back()
                elif pressed == NEXT_BUTTON or pressed == FINISH_BUTTON:
                    errors = []
                    if self.validate_input(self.__current_page, errors):
                        self.process_input(self.__current_page)
                        self.go_next()
                    else:
                        error_text = ""
                        for error in errors:
                            error_text += "%s\n" % error
                            ButtonChoiceWindow(screen,
                                               "There Were Errors",
                                               error_text,
                                               buttons = ["OK"])
                elif pressed == CANCEL_BUTTON:
                    active = False
            except Exception, error:
                ButtonChoiceWindow(screen,
                                   "An Exception Has Occurred",
                                   str(error) + "\n" + traceback.format_exc(),
                                   buttons = ["OK"])
            screen.popWindow()
            screen.finish()

class SelectionListConfigScreen(ConfigScreen):
    '''
    Provides a class that can be used to give the user an initial list.
    The system then branches based on the selection the user makes.

    The developer will still need to override the get_elements_for_page method
    in order to map the core selection to the appropriate page.
    '''

    def __init__(self, title, colorset = None):
        ConfigScreen.__init__(self, title, colorset)

    def get_screen_options(self):
        """
        Returns the list of elements to be displayed on the screen. Each element
        is a pair of elements, the first being the value returned for a selected
        option, and the second being the text to display for an option.
        """
        print "Called the parent instead."
        return []

    def get_list_page(self, screen):
        options = self.get_screen_options()
        result = None

        if len(options) > 0:
            self.__has_options = True
            self.__option_list = Listbox(0)
            for option in options:
                self.__option_list.append(option[0], option[1])
            result = [self.__option_list]
        else:
            self.__has_options = False
            grid = Grid(1, 1)
            grid.setField(Label("There are no options available."), 0, 0)
            result = [grid]
        return result

    def get_selected_option(self):
        return self.__option_list.current()

    def has_selectable_options(self):
        return self.__has_options
