class TaskMaster(Gtk.Alignment):

    def __init__(self, activity):
        ''' Initialize the task list '''
        Gtk.Alignment.__init__(self)
        self.activity = activity

        self.set_size_request(Gdk.Screen.width() - style.GRID_CELL_SIZE, -1)

        cssProvider = Gtk.CssProvider()
        cssProvider.load_from_path('style.css')
        screen = Gdk.Screen.get_default()
        styleContext = Gtk.StyleContext()
        styleContext.add_provider_for_screen(screen, cssProvider,
                                             Gtk.STYLE_PROVIDER_PRIORITY_USER)

        self.button_was_pressed = True
        self.current_task = None
        self.keyname = None
        self.task_button = None
        self.progress_checked = False
        self.completed = False

        self._name = None
        self._email = None
        self._graphics = None
        self._first_time = True
        self.yes_task = None
        self.no_task = None
        self._task_list = tasks.get_tasks(self)
        self._uid = None
        self.returning_user = False

        self._assign_required()

        name = self.read_task_data(NAME_UID)
        email_address = self.read_task_data(EMAIL_UID)
        phone_number = self.read_task_data(PHONE_NUMBER_UID)
        school = self.read_task_data(SCHOOL_NAME)

        if name is None or email_address is None or phone_number is None or \
           school is None:
            self.current_task = 0
        else:
            self.current_task = self.uid_to_task_number(CONFIRMATION_TASK)
            self.returning_user = True

        self._graphics_grid = Gtk.Grid()
        self._graphics_grid.set_row_spacing(style.DEFAULT_SPACING)
        self._graphics_grid.set_column_spacing(style.DEFAULT_SPACING)

        self.set(xalign=0.5, yalign=0, xscale=0, yscale=0)
        self.add(self._graphics_grid)
        self._graphics_grid.show()

        self.activity.load_graphics_area(self)

        self._task_button_alignment = Gtk.Alignment.new(
            xalign=0.5, yalign=0.5, xscale=0, yscale=0)
        self._task_button_alignment.set_size_request(
            Gdk.Screen.width() - style.GRID_CELL_SIZE, -1)

        grid = Gtk.Grid()
        grid.set_row_spacing(style.DEFAULT_SPACING)
        grid.set_column_spacing(style.DEFAULT_SPACING)
        grid.set_column_homogeneous(True)

        self._refresh_button = Gtk.Button(_('Refresh'), name='refresh-button')
        self._refresh_button.connect('clicked', self._refresh_button_cb)
        left = Gtk.Alignment.new(xalign=0, yalign=0.5, xscale=0, yscale=0)
        left.add(self._refresh_button)
        self._refresh_button.hide()
        grid.attach(left, 0, 0, 1, 1)
        left.show()

        mid = Gtk.Alignment.new(xalign=0.5, yalign=0.5, xscale=0, yscale=0)
        yes_next_no_grid = Gtk.Grid()
        yes_next_no_grid.set_row_spacing(style.DEFAULT_SPACING)
        yes_next_no_grid.set_column_spacing(style.DEFAULT_SPACING)
        yes_next_no_grid.set_column_homogeneous(True)

        self._yes_button = Gtk.Button(_('Yes'), name='next-button')
        self._yes_button.connect('clicked', self.jump_to_task_cb, 'yes')
        yes_next_no_grid.attach(self._yes_button, 0, 0, 1, 1)
        self._yes_button.hide()

        self.task_button = Gtk.Button(_('Next'), name='next-button')
        self.task_button.connect('clicked', self._task_button_cb)
        yes_next_no_grid.attach(self.task_button, 1, 0, 1, 1)
        self.task_button.show()

        self._no_button = Gtk.Button(_('No'), name='next-button')
        self._no_button.connect('clicked', self.jump_to_task_cb, 'no')
        yes_next_no_grid.attach(self._no_button, 2, 0, 1, 1)
        self._no_button.hide()

        mid.add(yes_next_no_grid)
        yes_next_no_grid.show()
        grid.attach(mid, 1, 0, 1, 1)
        mid.show()

        right_grid = Gtk.Grid()

        self._my_turn_button = Gtk.Button(_('My Turn'), name='my-turn-button')
        self._my_turn_button.connect('clicked', self._my_turn_button_cb)
        right_grid.attach(self._my_turn_button, 0, 0, 1, 1)
        self._my_turn_button.hide()

        self._skip_button = Gtk.Button(_('Skip this section'),
                                       name='my-turn-button')
        self._skip_button.connect('clicked', self._skip_button_cb)
        right_grid.attach(self._skip_button, 1, 0, 1, 1)
        self._skip_button.hide()

        right = Gtk.Alignment.new(xalign=1.0, yalign=0.5, xscale=0, yscale=0)
        right.add(right_grid)
        right_grid.show()
        grid.attach(right, 2, 0, 1, 1)
        right.show()

        self._task_button_alignment.add(grid)
        grid.show()

        self.activity.load_button_area(self._task_button_alignment)
        self._task_button_alignment.show()

        self._progress_bar = None
        self._progress_bar_alignment = Gtk.Alignment.new(
            xalign=0.5, yalign=0.5, xscale=0, yscale=0)
        self._progress_bar_alignment.set_size_request(
            Gdk.Screen.width() - style.GRID_CELL_SIZE, -1)

        self.activity.load_progress_area(self._progress_bar_alignment)
        self._progress_bar_alignment.show()

    def keypress_cb(self, widget, event):
        self.keyname = Gdk.keyval_name(event.keyval)

    def task_master(self):
        ''' 'nough said. '''

        _logger.debug('Running step %d' % (self.current_task))
        self._destroy_graphics()
        self.activity.button_was_pressed = False
        if self.current_task < self._get_number_of_tasks():
            section_index, task_index = self.get_section_and_task_index()

            # Do we skip this step?
            task = self._task_list[section_index]['tasks'][task_index]
            while(task.is_completed() and task.skip_if_completed()):
                _logger.debug('Skipping task %d' % task_index)
                self.current_task += 1
                if self.current_task == self._get_number_of_tasks():
                    self.current_task = 0
                section_index, task_index = self.get_section_and_task_index()
                task = self._task_list[section_index]['tasks'][task_index]

            # Check to make sure all the requirements at met
            i = 0
            while not self.requirements_are_met(section_index, task_index):
                _logger.debug('Switching to a required task %d' %
                              self.current_task)
                section_index, task_index = self.get_section_and_task_index()
                i += 1
                if i > 10:
                    # Shouldn't happen but we want to avoid infinite loops
                    _logger.error('Breaking out of required task loop.')

            self._first_time = True
            self.completed = False
            self._run_task(section_index, task_index)
        else:
            self.show_page('completed.html')
            # Activity will close after this button is clicked
            _logger.debug('setting completed to True')
            self.completed = True
            self.task_button.set_label(_('Exit'))

    def show_page(self, url):
            self._destroy_graphics()
            graphics = Graphics()
            self._graphics = graphics
            url = os.path.join(self.get_bundle_path(), 'html-content', url)
            self._graphics.add_uri(
                'file://' + url + '?NAME=' + utils.get_safe_text(
                    self.read_task_data(NAME_UID).replace(',', ' ')))
            self._graphics.set_zoom_level(0.667)
            self._graphics_grid.attach(self._graphics, 0, 0, 1, 1)
            self._graphics.show()

    def enter_entered(self):
        ''' Enter was entered in a text entry '''
        self.button_was_pressed = True
        section_index, task_index = self.get_section_and_task_index()
        task = self._task_list[section_index]['tasks'][task_index]
        if task.after_button_press():
            self.current_task += 1
            if self.completed:
                GObject.idle_add(self.activity.close)
            else:
                self.task_master()
        else:
            self.task_button.set_sensitive(False)

    def _task_button_cb(self, button):
        ''' The button at the bottom of the page for each task: used to
            advance to the next task. '''
        self.button_was_pressed = True
        section_index, task_index = self.get_section_and_task_index()
        task = self._task_list[section_index]['tasks'][task_index]
        if task.after_button_press():
            if self.yes_task is not None:
                self.update_completion_percentage()
                self.jump_to_task_cb(None, 'yes')
            elif self.no_task is not None:
                self.update_completion_percentage()
                self.jump_to_task_cb(None, 'no')
            else:
                if self.completed:
                    GObject.idle_add(self.activity.close)
                else:
                    _logger.debug('in task_button_cb... incrementing task')
                    self.current_task += 1
                    self.task_master()

    def _my_turn_button_cb(self, button):
        ''' Take me to the Home Page and select favorites view. '''
        utils.goto_home_view()
        utils.select_favorites_view()

    def _skip_button_cb(self, button):
        ''' Jump to next section '''
        section_index, task_index = self.get_section_and_task_index()
        section_index += 1

        # Don't skip off the end
        if section_index == self.get_number_of_sections():
            section_index = 0

        # Don't skip to last section unless all requirements are met
        if section_index == self.get_number_of_sections() - 1 and \
           not self.requirements_are_met(section_index, 0, switch_task=False):
            section_index = 0

        task = self._task_list[section_index]['tasks'][0]
        self.current_task = self.uid_to_task_number(task.uid)
        self.task_master()

    def _refresh_button_cb(self, button):
        ''' Refresh the current page's graphics '''
        self._destroy_graphics()

        section_index, task_index = self.get_section_and_task_index()
        task = self._task_list[section_index]['tasks'][task_index]

        self._graphics, label = task.get_graphics()
        self._graphics_grid.attach(self._graphics, 0, 0, 1, 1)
        self._graphics.show()
        # self.task_button.show()

    def get_help_info(self):
        ''' Uses help from the Help activity '''
        if self.current_task is None:
            return (None, None)
        else:
            section_index, task_index = self.get_section_and_task_index()
            task = self._task_list[section_index]['tasks'][task_index]
            return task.get_help_info()

    def _run_task(self, section_index, task_index):
        '''To run a task, we need graphics to display, a test to call that
            returns True or False, and perhaps some data '''

        task = self._task_list[section_index]['tasks'][task_index]
        if self._first_time:
            self._uid = task.uid
            self._load_graphics()
            self._first_time = False

        GObject.timeout_add(task.get_pause_time(), self._test, task.test,
                            self._uid)

    def _test(self, test, uid):
        ''' Is the task complete? '''

        if test():
            if self.task_button is not None:
                self.task_button.set_sensitive(True)
        else:
            if self.task_button is not None:
                self.task_button.set_sensitive(False)
            section_index, task_index = self.get_section_and_task_index()
            self._run_task(section_index, task_index)

    def jump_to_task_cb(self, widget, flag):
        ''' Jump to task associated with uid '''
        section_index, task_index = self.get_section_and_task_index()
        task = self._task_list[section_index]['tasks'][task_index]

        self.button_was_pressed = True
        task.after_button_press()

        if flag == 'yes':
            uid = self.yes_task
        else:
            uid = self.no_task
        self.current_task = self.uid_to_task_number(uid)

        self.task_master()

    def _assign_required(self):
        ''' Add collectable tasks in each section to badge task. '''
        all_requirements = []
        for section in self._task_list:
            section_requirements = []
            for task in section['tasks']:
                if task.is_collectable():
                    section_requirements.append(task.uid)
                    all_requirements.append(task.uid)
        self._task_list[-1]['tasks'][-1].set_requires(all_requirements)

    def requirements_are_met(self, section_index, task_index,
                             switch_task=True):
        ''' Check to make sure all the requirements at met '''
        task = self._task_list[section_index]['tasks'][task_index]
        requires = task.get_requires()
        for uid in requires:
            # Don't restrict search to current section
            if not self.uid_to_task(uid, section=None).is_completed():
                if switch_task:
                    _logger.debug('Task %s requires task %s... switching' %
                                  (task.uid, uid))
                    self.current_task = self.uid_to_task_number(uid)
                    section_index, task_index = \
                        self.get_section_and_task_index()
                return False
        return True

    def reload_graphics(self):
        ''' When changing font size and zoom level, we regenerate the task
           graphic. '''
        self._destroy_graphics()
        self._load_graphics()
        self._progress_bar.show()
        section_index, task_index = self.get_section_and_task_index()
        task = self._task_list[section_index]['tasks'][task_index]
        self._uid = self.section_and_task_to_uid(section_index, task_index)
        self._test(task.test, self._uid)

    def _destroy_graphics(self):
        ''' Destroy the graphics from the previous task '''
        if self._graphics is not None:
            self._graphics.destroy()
            self._graphics = None

    def _load_graphics(self):
        ''' Load the graphics for a task and define the task button '''
        section_index, task_index = self.get_section_and_task_index()
        task = self._task_list[section_index]['tasks'][task_index]

        task.set_font_size(self.activity.font_size)
        task.set_zoom_level(self.activity.zoom_level)

        self.activity.reset_scrolled_window_adjustments()

        if self._graphics is not None:
            self._graphics.destroy()
        graphics, label = task.get_graphics()
        self._graphics = graphics

        self._graphics_grid.attach(self._graphics, 0, 0, 1, 1)
        self._graphics.show()

        self.yes_task, self.no_task = task.get_yes_no_tasks()
        if self.yes_task is not None and self.no_task is not None:
            self.task_button.hide()
            self._yes_button.show()
            self._no_button.show()
        elif self.task_button is not None:
            self.task_button.set_label(label)
            self.task_button.set_sensitive(False)
            self.task_button.show()
            self._yes_button.hide()
            self._no_button.hide()

        if task.get_refresh():
            self._refresh_button.show()
        else:
            self._refresh_button.hide()

        if task.get_my_turn():
            self._my_turn_button.show()
        else:
            self._my_turn_button.hide()

        if task.get_skip():
            self._skip_button.show()
        else:
            self._skip_button.hide()

        self._update_progress()

        task.grab_focus()

    def get_bundle_path(self):
        return self.activity.bundle_path

    def get_number_of_sections(self):
        return len(self._task_list)

    def get_section_name(self, section_index):
        return self._task_list[section_index]['name']

    def section_and_task_to_uid(self, section_index, task_index=0):
        section = self._task_list[section_index]
        if section_index < 0 or (section_index >
                                 self.get_number_of_sections() - 1):
            _logger.error('Bad section index %d' % (section_index))
            return self._task_list[0]['tasks'][0].uid
        elif task_index > len(section['tasks']) - 1 or task_index < 0:
            _logger.error('Bad task index %d:%d' % (section_index, task_index))
            return self._task_list[0]['tasks'][0].uid
        else:
            return section['tasks'][task_index].uid

    def uid_to_task_number(self, uid):
        i = 0
        for section in self._task_list:
            for task in section['tasks']:
                if task.uid == uid:
                    return i
                i += 1
        _logger.error('UID %s not found' % uid)
        return 0

    def get_section_and_task_index(self):
        count = 0
        for section_index, section in enumerate(self._task_list):
            for task_index in range(len(section['tasks'])):
                if count == self.current_task:
                    return section_index, task_index
                count += 1
        return -1, -1

    def _get_number_of_tasks_in_section(self, section_index):
        return len(self._task_list[section_index]['tasks'])

    def _get_number_of_tasks(self):
        count = 0
        for section in self._task_list:
            count += len(section['tasks'])
        return count

    def uid_to_task(self, uid, section=None):
        if section:
            for task in section['tasks']:
                if task.uid == uid:
                    return task
        else:
            for section in self._task_list:
                for task in section['tasks']:
                    if task.uid == uid:
                        return task
        _logger.error('UID %s not found' % uid)
        return self._task_list[0]['tasks'][0]

    def _prev_task_button_cb(self, button):
        section_index, task_index = self.get_section_and_task_index()
        if task_index == 0:
            return
        i = task_index
        while(i > 0):
            i -= 1
            if self.requirements_are_met(section_index, i,
                                         switch_task=False):
                self.current_task -= (task_index - i)
                break
        self.task_master()

    def _next_task_button_cb(self, button):
        section_index, task_index = self.get_section_and_task_index()
        tasks_in_section = self._get_number_of_tasks_in_section(section_index)
        if task_index > tasks_in_section - 1:
            return
        i = task_index + 1
        while(i < tasks_in_section - 1):
            if self.requirements_are_met(section_index, i,
                                         switch_task=False):
                self.current_task += (i - task_index)
                break
            i += 1
        self.task_master()

    def _look_for_next_task(self):
        section_index, task_index = self.get_section_and_task_index()
        tasks_in_section = self._get_number_of_tasks_in_section(section_index)
        if task_index > tasks_in_section - 1:
            return False
        i = task_index + 1
        while(i < tasks_in_section - 1):
            if self.requirements_are_met(section_index, i,
                                         switch_task=False):
                return True
            i += 1
        return False

    def _progress_button_cb(self, button, i):
        self.current_task = i
        self.task_master()

    def _update_progress(self):
        section_index, task_index = self.get_section_and_task_index()
        if section_index < 0:  # We haven't started yet
            return

        tasks_in_section = self._get_number_of_tasks_in_section(section_index)

        # If the task index is 0, then we need to create a new progress bar
        if task_index == 0 or self._progress_bar is None:
            if self._progress_bar is not None:
                self._progress_bar.destroy()

            buttons = []
            if tasks_in_section > 1:
                for i in range(tasks_in_section):
                    task = self._task_list[section_index]['tasks'][i]
                    tooltip = task.get_name()
                    buttons.append({'label': str(i + 1), 'tooltip': tooltip})

            if self._name is None:
                self._name = self.read_task_data(NAME_UID)
            if self._name is not None:
                self._name = self._name.replace(',', ' ')
            if self._email is None:
                self._email = self.read_task_data(EMAIL_UID)
            if self._name is not None and self._email is not None:
                name = '%s\n%s' % (self._name, self._email)
            elif self._name is not None:
                name = self._name
            else:
                name = ''

            uid = ' '

            self._progress_bar = ProgressBar(
                name,
                self._task_list[section_index]['name'],
                uid,
                buttons,
                self._prev_task_button_cb,
                self._next_task_button_cb,
                self._progress_button_cb)
            self._progress_bar_alignment.add(self._progress_bar)
            self._progress_bar.show()

        if tasks_in_section == 1:
            self._progress_bar.hide_prev_next_task_buttons()
        else:
            self._progress_bar.show_prev_next_task_buttons()

        # Set button sensitivity True for completed tasks and current task
        if task_index < tasks_in_section:
            for ti in range(tasks_in_section - 1):
                task = self._task_list[section_index]['tasks'][ti]
                if task.is_completed():
                    self._progress_bar.set_button_sensitive(ti, True)
                else:
                    self._progress_bar.set_button_sensitive(ti, False)
            # Current task
            if task_index < tasks_in_section:
                self._progress_bar.set_button_sensitive(task_index, True)

        if task_index > 0:
            self._progress_bar.prev_task_button.set_sensitive(True)
        else:
            self._progress_bar.prev_task_button.set_sensitive(False)

        if self._look_for_next_task():
            self._progress_bar.next_task_button.set_sensitive(True)
        else:
            self._progress_bar.next_task_button.set_sensitive(False)

    def read_task_data(self, uid):
        client = GConf.Client.get_default()
        data = client.get_string('/desktop/sugar/support/%s' % uid)
        return data

    def write_task_data(self, uid, data):
        client = GConf.Client.get_default()
        client.set_string('/desktop/sugar/support/%s' % uid, data)