# -*- coding: utf-8; Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*-

# Copyright (C) 2011 Stephane Graber
# Author: Stephane Graber <stgraber@ubuntu.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; either version 2 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 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 St, Fifth Floor, Boston, MA  02110-1301  USA

import os, apt, apt_pkg
from gi.repository import Gtk as gtk

from ubiquity.plugin import *
from ubiquity import i18n
from ubiquity import misc
from ubiquity import install_misc
import debconf

NAME = 'edubuntu-packages'
AFTER = 'edubuntu-addon'
WEIGHT = 10
OEM = False

class PackagesModel(gtk.TreeStore):
    (
        COLUMN_INSTALLED,
        COLUMN_NAME,
        COLUMN_DESCRIPTION
    ) = list(range(3))

    def __init__(self):
        gtk.TreeStore.__init__(self, bool, str, str)
        with misc.raised_privileges():
            self.populate_tree()

    def populate_tree(self):
        src_pkg = "edubuntu-meta"
        meta_blacklist = ['edubuntu-server','edubuntu-desktop-kde','edubuntu-fonts']
        package_blacklist = ['unity-2d']
        relation_blacklist = ['Depends']

        # Initialize APT
        apt_pkg.init_config()
        apt_pkg.init_system()

        cache = apt_pkg.Cache(apt.progress.base.OpProgress())
        try:
            srcpkgrecords=apt_pkg.SourceRecords()
            srcpkgrecords.lookup(src_pkg)
        except SystemError:
            # Probably on a system without deb-src entries
            binaries=[]
            for p in apt.Cache():
                if p.candidate:
                    if p.candidate.source_name == src_pkg:
                        binaries.append(p.name)

            srcpkgrecords=type("SourceRecords", (object, ), {'binaries':binaries})()

        pkgrecords=apt_pkg.PackageRecords(cache)

        ubiquity_blacklist=install_misc.query_recorded_removed()[1]
        ubiquity_whitelist=install_misc.query_recorded_installed()

        processed = []
        for pkg in sorted(cache.packages, key=lambda package: package.name):
            if pkg.name in srcpkgrecords.binaries and not pkg.name in meta_blacklist and not pkg.name in processed:
                pkgrecords.lookup(pkg.version_list[0].file_list[0])
                piter=self.append(None,(True,pkg.name,pkgrecords.short_desc))
                for dep_type in pkg.version_list[0].depends_list:
                    if dep_type in relation_blacklist:
                        continue

                    for bin_pkg in sorted(pkg.version_list[0].depends_list[dep_type], key=lambda package: package[0].target_pkg.name):
                        if bin_pkg[0].target_pkg.name in package_blacklist:
                            continue

                        # Don't list inter meta package dependencies unless it's a meta package we don't list
                        if bin_pkg[0].target_pkg.name in (set(srcpkgrecords.binaries) - set(meta_blacklist)):
                            continue

                        try:
                            pkgrecords.lookup(bin_pkg[0].target_pkg.version_list[0].file_list[0])
                        except IndexError:
                            continue

                        if (bin_pkg[0].target_pkg.current_state == apt_pkg.CURSTATE_INSTALLED and bin_pkg[0].target_pkg.name not in ubiquity_blacklist) or bin_pkg[0].target_pkg.name in ubiquity_whitelist:
                            self.append(piter,(True,bin_pkg[0].target_pkg.name,pkgrecords.short_desc))
                        else:
                            self.append(piter,(False,bin_pkg[0].target_pkg.name,pkgrecords.short_desc))
                processed.append(pkg.name)

class PageBase(PluginUI):
    pass

class PageGtk(PageBase):
    plugin_title = 'ubiquity/text/edubuntu-packages_heading_label'
    removed_packages = set()
    removed_packages_recursive = set()
    installed_packages = set()

    def __init__(self, controller, *args, **kwargs):
        self.controller = controller
        try:
            builder = gtk.Builder()
            builder.add_from_file(os.path.join(os.environ['UBIQUITY_GLADE'],
                'edubuntu-packages.ui'))
            builder.connect_signals(self)
            self.controller.add_builder(builder)
            self.page = builder.get_object('edubuntu-packages_window')

            # Load required objects
            self.description = builder.get_object('description')
            self.tvPackages = builder.get_object('tvPackages')
            self.tvPackagesModel = PackagesModel()

            # Configure treeview
            self.tvPackages.set_model(self.tvPackagesModel)
            self.tvPackages.set_headers_visible(True)

            # Installed checkbox
            column = gtk.TreeViewColumn("Installed")
            cell = gtk.CellRendererToggle()
            cell.connect("toggled", self.on_toggled, self.tvPackagesModel)
            column.pack_start(cell, False)
            column.add_attribute(cell, "active", self.tvPackagesModel.COLUMN_INSTALLED)
            self.tvPackages.append_column(column)

            # Package name label
            column = gtk.TreeViewColumn("Name")
            cell = gtk.CellRendererText()
            column.pack_start(cell, False)
            column.add_attribute(cell, "text", self.tvPackagesModel.COLUMN_NAME)
            self.tvPackages.append_column(column)

            # Package description label
            column = gtk.TreeViewColumn("Description")
            cell = gtk.CellRendererText()
            column.pack_start(cell, False)
            column.add_attribute(cell, "text", self.tvPackagesModel.COLUMN_DESCRIPTION)
            self.tvPackages.append_column(column)

        except Exception as e:
            self.debug('Could not create edubuntu-packages page: %s', e)
            self.page = None
        self.plugin_widgets = self.page

    def on_toggled(self, toggle, path, model):
        piter = model.get_iter_from_string(path)
        install_state = not model.get_value(piter, model.COLUMN_INSTALLED)
        self.toggle(model, piter, install_state)

    def toggle(self, model, piter, install_state):
        if model.get_value(piter, model.COLUMN_INSTALLED) != install_state:
            model.set_value(piter, model.COLUMN_INSTALLED,install_state)
            self.set_state(model, piter, install_state)

        # Check if we are a meta package
        if model.iter_has_child(piter) == True:
            if install_state == True:
                # If we select a meta, select all children
                for i in range(model.iter_n_children(piter)):
                    child = model.iter_nth_child(piter, i)
                    if model.get_value(child, model.COLUMN_INSTALLED) != install_state:
                        self.toggle(model, child, install_state)
            else:
                # If we unselect a meta, unselect all children unless they appear multiple times
                for i in range(model.iter_n_children(piter)):
                    child = model.iter_nth_child(piter, i)
                    if self.get_number_occurrence(piter, model, model.get_value(child, model.COLUMN_NAME)) == 1:
                        if model.get_value(child, model.COLUMN_INSTALLED) != install_state:
                            self.toggle(model, child, install_state)
        else:
            # Check if all siblings share the same state, if so, apply to parent
            parent=model.iter_parent(piter)
            name=model.get_value(piter, model.COLUMN_NAME)

            score=0
            for i in range(model.iter_n_children(parent)):
                child = model.iter_nth_child(parent, i)
                if model.get_value(child, model.COLUMN_INSTALLED) == install_state:
                    score += 1

            if score == model.iter_n_children(parent):
                # All siblings share the same state, apply to parent
                if model.get_value(parent, model.COLUMN_INSTALLED) != install_state:
                    model.set_value(parent, model.COLUMN_INSTALLED,install_state)
                    self.set_state(model, parent, install_state)
            else:
                # Some siblings have a different state, unselect parent
                if model.get_value(parent, model.COLUMN_INSTALLED) != False:
                    model.set_value(parent, model.COLUMN_INSTALLED,False)
                    self.set_state(model, parent, False)

            # Get all identical package in other meta and switch state
            root = model.iter_parent(parent)
            for meta_index in range(model.iter_n_children(root)):
                meta = model.iter_nth_child(root, meta_index)
                for package_index in range(model.iter_n_children(meta)):
                    package = model.iter_nth_child(meta, package_index)
                    if model.get_value(package, model.COLUMN_NAME) == name:
                        if model.get_value(package, model.COLUMN_INSTALLED) != install_state:
                            self.toggle(model, package, install_state)

    def get_number_occurrence(self, piter, model, name):
        count = 0
        root = model.iter_parent(piter)
        for meta_index in range(model.iter_n_children(root)):
            meta = model.iter_nth_child(root, meta_index)
            for package_index in range(model.iter_n_children(meta)):
                package = model.iter_nth_child(meta, package_index)
                if model.get_value(package, model.COLUMN_NAME) == name:
                    count += 1
        return count

    def set_state(self, model, piter, install_state):
        name = model.get_value(piter, model.COLUMN_NAME)
        if install_state == False:
            if name in self.installed_packages:
                self.installed_packages.remove(name)
            else:
                self.removed_packages_recursive.add(name)
        else:
            if name in self.removed_packages_recursive:
                self.removed_packages_recursive.remove(name)
            else:
                self.installed_packages.add(name)


    def plugin_translate(self, lang):
        self.description.set_markup(self.controller.get_string('edubuntu-packages_description_label', lang))

        self.tvPackages.get_columns()[0].set_title(self.controller.get_string('edubuntu-packages_column_installed_title', lang))
        self.tvPackages.get_columns()[1].set_title(self.controller.get_string('edubuntu-packages_column_name_title', lang))
        self.tvPackages.get_columns()[2].set_title(self.controller.get_string('edubuntu-packages_column_description_title', lang))

class Page(Plugin):
    def Prepare(self):
        with misc.raised_privileges():
            self.ui.removed_packages=install_misc.query_recorded_removed()[0]
            self.ui.removed_packages_recursive=install_misc.query_recorded_removed()[1]
            self.ui.installed_packages=install_misc.query_recorded_installed()
        pass

    def ok_handler(self):
        with misc.raised_privileges():
            if os.path.exists('/var/lib/ubiquity/apt-removed'):
                os.remove('/var/lib/ubiquity/apt-removed')
            if os.path.exists('/var/lib/ubiquity/apt-installed'):
                os.remove('/var/lib/ubiquity/apt-installed')

            try:
                if self.db.get('oem-config/enable') == 'true':
                    # Ensure that we always have the required packages for OEM
                    self.ui.installed_packages.add('oem-config-gtk')
                    self.ui.installed_packages.add('oem-config-slideshow-ubuntu')
            except debconf.DebconfError:
                pass

            install_misc.record_removed(self.ui.removed_packages,False)
            install_misc.record_removed(self.ui.removed_packages_recursive,True)
            install_misc.record_installed(self.ui.installed_packages)
        Plugin.ok_handler(self)
