
import warnings
warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
import apt
import apt_pkg
import os
import os.path
import macros

from xml.etree.ElementTree import ElementTree

from gettext import gettext as _

class LanguagePackageStatus(object):
    def __init__(self, pkg_template):
        self.pkgname_template = pkg_template
        self.available = False
        self.installed = False
        self.doChange = False


# the language-support information
class LanguageInformation(object):
    def __init__(self):
        #FIXME:
        #needs a new structure:
        #languagePkgList[LANGCODE][tr|fn|in|wa]=[packages available for that language in that category]
        #@property for each category
        #@property for each LANGCODE
        self.language = None
        self.languageCode = None
        # langPack/support status 
        self.languagePkgList = {}
        self.languagePkgList["languagePack"] = LanguagePackageStatus("language-pack-%s")
        self.languagePkgList["languageSupportWritingAids"] = LanguagePackageStatus("language-support-writing-%s")
        self.languagePkgList["languageSupportInputMethods"] = LanguagePackageStatus("language-support-input-%s")
        self.languagePkgList["languageSupportFonts"] = LanguagePackageStatus("language-support-fonts-%s")
        
    @property
    def inconsistent(self):
        " returns True if only parts of the language support packages are installed "
        if (not self.notInstalled and not self.fullInstalled) : return True
        return False
    @property
    def fullInstalled(self):
        " return True if all of the available language support packages are installed "
        for pkg in self.languagePkgList.values() :
            if not pkg.available : continue
            if not ((pkg.installed and not pkg.doChange) or (not pkg.installed and pkg.doChange)) : return False
        return True
    @property
    def notInstalled(self):
        " return True if none of the available language support packages are installed "
        for pkg in self.languagePkgList.values() :
            if not pkg.available : continue
            if not ((not pkg.installed and not pkg.doChange) or (pkg.installed and pkg.doChange)) : return False
        return True
    @property
    def changes(self):
        " returns true if anything in the state of the language packs/support changes "
        for pkg in self.languagePkgList.values() :
            if (pkg.doChange) : return True
        return False
    def __str__(self):
        return "%s (%s)" % (self.language, self.languageCode)

# the pkgcache stuff
class ExceptionPkgCacheBroken(Exception):
    pass

class LanguageSelectorPkgCache(apt.Cache):

    BLACKLIST = "/usr/share/language-selector/data/blacklist"
    LANGCODE_TO_LOCALE = "/usr/share/language-selector/data/langcode2locale"
    PACKAGE_DEPENDS = "/usr/share/language-selector/data/pkg_depends"

    def __init__(self, localeinfo, progress):
        apt.Cache.__init__(self, progress)
        if self._depcache.BrokenCount > 0:
            raise ExceptionPkgCacheBroken()
        self._localeinfo = localeinfo
        # keep the lists 
        self.to_inst = []
        self.to_rm = []

        # packages that need special translation packs (not covered by
        # the normal langpacks)
        self.langpack_locales = {}
        self.pkg_translations = {}
        self.pkg_writing = {}
        self.multilang = {} # packages which service multiple languages (e.g. openoffice.org-hyphenation)
        filter_list = {}
        blacklist = []
        
        for l in open(self.BLACKLIST):
            l = l.strip()
            if not l.startswith('#'):
                blacklist.append(l)
        
        for l in open(self.LANGCODE_TO_LOCALE):
            try:
                l = l.rstrip()
                if ':' in l:
                    (pkgcode, locale) = l.split(':')
                else:
                    pkgcode = l
                    locale = l
            except ValueError:
                continue
            self.langpack_locales[locale] = pkgcode
        
        for l in open(self.PACKAGE_DEPENDS):
            if l.startswith('#'):
                continue
            try:
                l = l.rstrip()
                # sort out comments
                if l.find('#') >= 0:
                    continue
                (c, lc, k, v) = l.split(':')
            except ValueError:
                continue
            if (c == 'tr' and lc == ''):
                filter_list[v] = k
            elif (c == 'wa' and lc != ''):
                if '|' in lc:
                    if not v in self.multilang:
                        self.multilang[v] = []
                    for l in lc.split('|'):
                        if not l in self.pkg_writing:
                            self.pkg_writing[l] = []
                        self.pkg_writing[l].append(("%s" % k, "%s" % v))
                        self.multilang[v].append(l)
                else:
                    if not lc in self.pkg_writing:
                        self.pkg_writing[lc] = []
                    self.pkg_writing[lc].append(("%s" % k, "%s" % v))

        # get list of all packages available on the system and filter them
        for item in self.keys():
            if item in blacklist: 
                continue
            for x in filter_list.keys():
                if item.startswith(x) and not item.endswith('-base'):
                    # parse language code
                    langcode = item.replace(x, '')
                    #print "%s\t%s" % (item, langcode)
                    if langcode == 'zh':
                        # special case: zh langpack split
                        for langcode in ['zh-hans', 'zh-hant']:
                            if not langcode in self.pkg_translations:
                                self.pkg_translations[langcode] = []
                            self.pkg_translations[langcode].append(("%s" % filter_list[x], "%s" % item))
                    elif langcode in self.langpack_locales.values():
                        # langcode == pkgcode
                        if not langcode in self.pkg_translations:
                            self.pkg_translations[langcode] = []
                        self.pkg_translations[langcode].append(("%s" % filter_list[x], "%s" % item))
                        #print self.pkg_translations[langcode]
                    else:
                        # need to scan for LL-CC and LL-VARIANT codes
                        for locale in self.langpack_locales.keys():
                            if '_' in locale or '@' in locale:
                                if '@' in locale:
                                    (locale, variant) = locale.split('@')
                                else:
                                    variant = ''
                                (lcode, ccode) = locale.split('_')
                                if langcode in ["%s-%s" % (lcode, ccode.lower()),
                                                "%s%s" % (lcode, ccode.lower()),
                                                "%s-%s" % (lcode, variant),
                                                "%s%s" % (lcode, variant),
                                                "%s-latn" % lcode,
                                                "%slatn" % lcode,
                                                "%s-%s-%s" % (lcode, ccode.lower(), variant),
                                                "%s%s%s" % (lcode, ccode.lower(), variant)]:
                                    # match found, get matching pkgcode
                                    langcode = self.langpack_locales[locale]
                                    if not langcode in self.pkg_translations:
                                        self.pkg_translations[langcode] = []
                                    self.pkg_translations[langcode].append(("%s" % filter_list[x], "%s" % item))
                                    #print self.pkg_translations[langcode]
                                    break
         
    @property
    def havePackageLists(self):
        " verify that a network package lists exists "
        for metaindex in self._list.List:
            for indexfile in metaindex.IndexFiles:
                if indexfile.ArchiveURI("").startswith("cdrom:"):
                    continue
                if indexfile.ArchiveURI("").startswith("http://security.ubuntu.com"):
                    continue
                if indexfile.Label != "Debian Package Index":
                    continue
                if indexfile.Exists and indexfile.HasPackages:
                    return True
        return False

    def clear(self):
        """ clear the selections """
        self._depcache.Init()
        
    def getChangesList(self):
        to_inst = []
        to_rm = []
        for pkg in self.getChanges():
            if pkg.markedInstall or pkg.markedUpgrade:
                to_inst.append(pkg.name)
            if pkg.markedDelete:
                to_rm.append(pkg.name)
        return (to_inst,to_rm)

    def _getPkgList(self, languageCode):
        """ helper that returns the list of needed pkgs for the language """
        # normal langpack+support first
        pkg_list = ["language-support-input-%s"%languageCode,\
                    "language-support-writing-%s"%languageCode,\
                    "language-support-fonts-%s"%languageCode,\
                    "language-pack-%s"%languageCode]
        # see what additional pkgs are needed
        #for (pkg, translation) in self.pkg_translations[languageCode]:
        #    if pkg in self and self[pkg].isInstalled:
        #        pkg_list.append(translation)
        return pkg_list
        
    def tryChangeDetails(self, li):
        " change the status of the support details (fonts, input methods) "
        #print li
        for item in li.languagePkgList.values():
            if item.doChange:
                try:
                    if item.installed:
                        self[item.pkgname_template % li.languageCode].markDelete()
                    else:
                        self[item.pkgname_template % li.languageCode].markInstall()
                except SystemError:
                    pass
        # Check for additional translation packages
        item = li.languagePkgList["languagePack"]
        if ((item.installed and not item.doChange) or (item.available and not item.installed and item.doChange)):
            if li.languageCode in self.pkg_translations:
                for (pkg, translation) in self.pkg_translations[li.languageCode]:
                    if pkg in self and \
                       (self[pkg].isInstalled or \
                       self[pkg].markedInstall or \
                       self[pkg].markedUpgrade) and \
                       translation in self and \
                       ((not self[translation].isInstalled and \
                       not self[translation].markedInstall and \
                       not self[translation].markedUpgrade) or \
                       self[translation].markedDelete):
                        self[translation].markInstall()
                        #print ("Will pull: %s" % translation)
        elif ((not item.installed and not item.doChange) or (item.installed and item.doChange)) :
            if li.languageCode in self.pkg_translations:
                for (pkg, translation) in self.pkg_translations[li.languageCode]:
                    if translation in self and \
                       (self[translation].isInstalled or \
                       self[translation].markedInstall or \
                       self[translation].markedUpgrade):
                           self[translation].markDelete()
                           #print ("Will remove: %s" % translation)

        # Check for additional writing aids packages
        item = li.languagePkgList["languageSupportWritingAids"]
        if ((item.installed and not item.doChange) or (item.available and not item.installed and item.doChange)):
            if li.languageCode in self.pkg_writing:
                for (pkg, pull_pkg) in self.pkg_writing[li.languageCode]:
                    if not pull_pkg in self:
                        continue
                    if '|' in pkg:
                        # multiple dependencies, if one of them is installed, pull the pull_pkg
                        for p in pkg.split('|'):
                            if p in self and \
                               (self[p].isInstalled or \
                               self[p].markedInstall or \
                               self[p].markedUpgrade) and \
                               ((not self[pull_pkg].isInstalled and \
                               not self[pull_pkg].markedInstall and \
                               not self[pull_pkg].markedUpgrade) or \
                               self[pull_pkg].markedDelete):
                                self[pull_pkg].markInstall()
                                #print ("Will pull: %s" % pull_pkg)
                    else:
                        if pkg in self and \
                           (self[pkg].isInstalled or \
                           self[pkg].markedInstall or \
                           self[pkg].markedUpgrade) and \
                           ((not self[pull_pkg].isInstalled and \
                           not self[pull_pkg].markedInstall and \
                           not self[pull_pkg].markedUpgrade) or \
                           self[pull_pkg].markedDelete):
                            self[pull_pkg].markInstall()
                            #print ("Will pull: %s" % pull_pkg)
        elif ((not item.installed and not item.doChange) or (item.installed and item.doChange)) :
            if li.languageCode in self.pkg_writing:
                for (pkg, pull_pkg) in self.pkg_writing[li.languageCode]:
                    if not pull_pkg in self:
                        continue
                    lcount = 0
                    pcount = 0
                    if pull_pkg in self.multilang:
                        # package serves multiple languages.
                        # check if other languages on this system still need this package.
                        for l in self.multilang[pull_pkg]:
                            p = "language-support-writing-%s" % l
                            if p in self and \
                               (self[p].isInstalled or \
                               self[p].markedInstall or \
                               self[p].markedUpgrade) and \
                               not self[p].markedDelete:
                                lcount = lcount+1
                    if '|' in pkg:
                        # multiple dependencies, if at least one of them is installed, keep the pull_pkg
                        # only remove pull_pkg if none of the dependencies are installed anymore
                        for p in pkg.split('|'):
                            if p in self and \
                               (self[p].isInstalled or \
                               self[p].markedInstall or \
                               self[p].markedUpgrade) and \
                               not self[p].markedDelete:
                                pcount = pcount+1
                    if pcount == 0  and lcount == 0 and \
                        (self[pull_pkg].isInstalled or \
                        self[pull_pkg].markedInstall or \
                        self[pull_pkg].markedUpgrade):
                        self[pull_pkg].markDelete()
                        #print ("Will remove: %s" % pull_pkg)
        return

    def tryInstallLanguage(self, languageCode):
        """ mark the given language for install """
        to_inst = []
        for name in self._getPkgList(languageCode):
            if name in self:
                try:
                    self[name].markInstall()
                    to_inst.append(name)
                except SystemError:
                    pass
                try:
                    self[name].markInstall()
                    to_inst.append(name)
                except SystemError:
                    pass

    def tryRemoveLanguage(self, languageCode):
        """ mark the given language for remove """
        to_rm = []
        for name in self._getPkgList(languageCode):
            if name in self:
                try:
                    # purge
                    self[name].markDelete(True)
                    to_rm.append(name)
                except SystemError:
                    pass
    
    def getLanguageInformation(self):
        """ returns a list with language packs/support packages """
        res = []
        for (code,lang) in self._localeinfo._lang.items():
            if code == 'zh':
                continue
            li = LanguageInformation()
            li.languageCode = code
            li.language = lang
            for langpkg_status in li.languagePkgList.values() :
                pkgname = langpkg_status.pkgname_template % code
                langpkg_status.available = pkgname in self
                if langpkg_status.available:
                    langpkg_status.installed = self[pkgname].isInstalled
            if len(filter(lambda s: s.available, li.languagePkgList.values())) > 0:
                res.append(li)
        return res


if __name__ == "__main__":

    from LocaleInfo import LocaleInfo
    datadir = "/usr/share/language-selector"
    li = LocaleInfo("languagelist", datadir)

    lc = LanguageSelectorPkgCache(li,apt.progress.OpProgress())
    print "available language information"
    print ", ".join(["%s" %x for x in lc.getLanguageInformation()])

    print "Trying to install 'zh'"
    lc.tryInstallLanguage("zh")
    print lc.getChangesList()

    print "Trying to remove it again"
    lc.tryRemoveLanguage("zh")
    print lc.getChangesList()
