#/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Karesansui.
#
# Copyright (C) 2009 HDE, Inc.
#
# 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.
#

import os
import sys
import time
import re
import rpm

import installer.install
import installer.uninstall
from installer.trans import _, N_
from installer.install import InstallError
from installer.uninstall import UninstallError
from installer.const import *

global logfile
env = os._Environ(os.environ)
logfile = env.get("LOGFILE")

class RpmLib:

    def __init__(self, callback=None):
        global logfile

        self.callback = callback
        self.fd = 0
        if logfile is None:
            logfile = "/dev/stdout"
        self.logfile = logfile
        self.rpm_callback_fd = 0
        self.cnt = 0
        self.output = None

        self.ts = rpm.TransactionSet()
        if RPM_VERIFY is False:
            #self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
            self.ts.setVSFlags(-1)

    def __del__(self):
        try:
            if self.fd != 0:
                os.close(self.fd)
        except:
            pass

        del self.ts

    def __grab_stderr(self, flag):
        if flag:
            if not self.output:
                self.stderr = sys.stderr
                sys.stderr = os.fdopen(os.dup(2), "w")
                logf = open(self.logfile, "a")
                os.dup2(logf.fileno(), 2)
                logf.close()
        else:
            if self.output:
                os.dup2(sys.stderr.fileno(), 2)
                sys.stderr = self.stderr
                del self.stderr
                self.output.close()
                self.output = None

    def __run_callback(self, reason, bytes, total, key, user):

        logf = open(self.logfile, "a")
        if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:
            logf.write("Opening file. %s\n" % key)
            self.rpm_callback_fd = os.open(key, os.O_RDONLY)

            self.cnt = 0
            file =  os.path.basename(key)
            if self.callback: self.callback.start(text=_("Installing packages...")+"\n"+file, size=100)

            return self.rpm_callback_fd
        elif reason == rpm.RPMCALLBACK_INST_START:
            logf.write("Closing file. %s\n" % key)
            if self.callback: self.callback.end(100)
            os.close(self.rpm_callback_fd)
        elif reason == rpm.RPMCALLBACK_TRANS_START:
            pass
        elif reason == rpm.RPMCALLBACK_TRANS_PROGRESS:
            pass
        elif reason == rpm.RPMCALLBACK_TRANS_STOP:
            pass
        elif reason == rpm.RPMCALLBACK_INST_PROGRESS:
            if self.callback: self.callback.update(bytes*100/total)
            pass
        elif reason == rpm.RPMCALLBACK_UNINST_START:
            if self.callback: self.callback.start(text=_("Uninstalling packages...")+"\n"+key, size=100)
            logf.write("Uninstall start. %s\n" % key)
        elif reason == rpm.RPMCALLBACK_UNINST_PROGRESS:
            if self.callback: self.callback.update(bytes*100/total)
            pass
        elif reason == rpm.RPMCALLBACK_UNINST_STOP:
            logf.write("Uninstall stop. %s\n" % key)
            if self.callback: self.callback.end(100)
        elif reason == rpm.RPMCALLBACK_UNKNOWN:
            pass
        elif reason == rpm.RPMCALLBACK_CPIO_ERROR:
            pass
        elif reason == rpm.RPMCALLBACK_UNPACK_ERROR:
            pass
        logf.close()

    def __check_callback(self, ts, TagN, N, EVR, Flags):
        return 1

    def readHeader(self, filename):
        self.fd = os.open(filename, os.O_RDONLY)

        try:
            hdr = self.ts.hdrFromFdno(self.fd)
        except rpm.error, e:
            print str(e)
            hdr = None

        os.close(self.fd)
        return hdr

    def getFileInfo(self, filename):
        ret = {}

        hdr = self.readHeader(filename)
        if hdr is not None:
            ret["name"] = hdr[1000]
            ret["version"] = hdr[1001]
            ret["release"] = hdr[1002]
            ret["arch"] = hdr[1022]
        return ret

    def doQuery(self, name=None):
        ret = {}

        if type(name) == str:
            names = [name]
        else:
            names = name

        for _pkg in names:
            mi = self.ts.dbMatch()
            if mi:
                mi.pattern('name', rpm.RPMMIRE_REGEX, _pkg)
                for info in mi:
                    ret[info['name']] = {
                                          "name"       :info["name"],
                                          "version"    :info["version"],
                                          "release"    :info["release"],
                                          "arch"       :info["arch"],
                                          "vendor"     :info["vendor"],
                                          "packager"   :info["packager"],
                                          "installtime":info["installtime"],
                                        }
            del mi
        return ret


    def doInstallAll(self, filenames):

        install = []
        update = []

        if self.callback: self.callback.start(text=_("Checking package dependencies..."), size=len(filenames))
        cnt = 0
        for filename in filenames:
            if self.callback: self.callback.update(cnt)
            """

            info = self.getFileInfo(filename)
            try:
                self.doQuery("^%s$" % info["name"])[info["name"]]
                update.append(filename)
            except:
                #install.append(filename)
                update.append(filename)
                pass
            """
            update.append(filename)
            cnt = cnt + 1

        if len(install) > 0:
            installer.install.write_log(_("Install packages are %s") % " ".join(install))
            self.doInstall(install)

        if len(update) > 0:
            installer.install.write_log(_("Update packages are %s") % " ".join(update))
            self.doUpdate(update)


    def doInstall(self, filenames):

        for filename in filenames:
            hdr = self.readHeader(filename)
            self.ts.addInstall(hdr, filename, 'i')
            unresolved_dependencies = self.ts.check(self.__check_callback)

        if not unresolved_dependencies:
            self.ts.order()
            #self.__grab_stderr(True)
            errors = self.ts.run(self.__run_callback, 1)
            #self.__grab_stderr(False)
        else:
            errmsg = ""
            for k in unresolved_dependencies:
                errmsg += "\n" + _("%s is needed by %s") % (k[1][0], "-".join(k[0]),)
            raise InstallError(_("ERROR: Unresolved dependencies, transaction failed. \n%s") % errmsg)

    def doUpdate(self, filenames):

        for filename in filenames:
            hdr = self.readHeader(filename)
            self.ts.addInstall(hdr, filename, 'u')
            unresolved_dependencies = self.ts.check(self.__check_callback)

        if not unresolved_dependencies:
            self.ts.order()
            #self.__grab_stderr(True)
            errors = self.ts.run(self.__run_callback, 1)
            #self.__grab_stderr(False)
        else:
            errmsg = ""
            for k in unresolved_dependencies:
                errmsg += "\n" + _("%s is needed by %s") % (k[1][0], "-".join(k[0]),)
            raise InstallError(_("ERROR: Unresolved dependencies, transaction failed. \n%s") % errmsg)


    def doUninstallAll(self, packages):

        uninstall = []

        if self.callback: self.callback.start(text=_("Checking package dependencies..."), size=len(packages))
        cnt = 0
        for package in packages:
            if self.callback: self.callback.update(cnt)
            try:
                for k,v in self.doQuery(package).iteritems():
                    uninstall.append(k)
            except:
                pass

            cnt = cnt + 1

        if len(uninstall) > 0:
            installer.uninstall.write_log(_("Uninstall packages are %s") % " ".join(uninstall))
            self.doUninstall(uninstall)

    def doUninstall(self, packages):

        for package in packages:
            self.ts.addErase(package)
            unresolved_dependencies = self.ts.check(self.__check_callback)

        if not unresolved_dependencies:
            self.ts.order()
            self.__grab_stderr(True)
            errors = self.ts.run(self.__run_callback, 1)
            self.__grab_stderr(False)
        else:
            errmsg = ""
            for k in unresolved_dependencies:
                errmsg += "\n" + _("%s is needed by %s") % (k[1][0], "-".join(k[0]),)
            raise UninstallError(_("ERROR: Unresolved dependencies, transaction failed. \n%s") % errmsg)


    def doRebuildSRPM(self, srpm):
        from installer.utils import execute_command, r_chmod

        for _cmd in ["gcc","rpmbuild"]:
            (ret,res) = execute_command(["which",_cmd])
            if ret != 0:
                raise InstallError(_("no %s in (%s)") % (_cmd,env.get("PATH")))

        serial = str(os.getuid()) + "-" + str(time.time())
        rcfile    = "%s.rpmrc.%s"     % (RPMBUILD_TMPFILE_PREFIX, serial,)
        macrofile = "%s.rpmmacros.%s" % (RPMBUILD_TMPFILE_PREFIX, serial,)
        rpmdir    = "%s.rpm.%s"       % (RPMBUILD_TMPFILE_PREFIX, serial,)

        for _rpmrc in ["/usr/lib/rpm/rpmrc","/usr/lib/rpmrc","/usr/lib/rpm/redhat/rpmrc","/etc/rpmrc"]:
            (ret, res) = execute_command(["cp",_rpmrc,rcfile])
            if os.path.exists(rcfile):
                break
        f = open(rcfile, "a")
        f.write("macrofiles: /usr/lib/rpm/macros:/usr/lib/rpm/%{_target}/macros:/etc/rpm/macros.specspo:/etc/rpm/macros:/etc/rpm/%{_target}/macros:~/.rpmmacros:")
        f.write(macrofile + "\n")
        f.close()

        f = open(macrofile, "w")
        f.write("%_topdir "    + rpmdir + "\n")
        f.close()

        os.makedirs(rpmdir + "/RPMS/noarch/")
        os.makedirs(rpmdir + "/RPMS/i386/")
        os.makedirs(rpmdir + "/RPMS/i586/")
        os.makedirs(rpmdir + "/RPMS/i686/")
        os.makedirs(rpmdir + "/RPMS/x86_64/")
        os.makedirs(rpmdir + "/SRPMS/")
        os.makedirs(rpmdir + "/SPECS/")
        os.makedirs(rpmdir + "/SOURCES/")
        os.makedirs(rpmdir + "/BUILD/")
        r_chmod(rpmdir,0700)

        total_linenum = DEFAULT_BUILD_PKGS_LINENUM
        for _pkg, _linenum in BUILD_PKGS_LINENUMS.iteritems():
            rpm_regex = re.compile("%s-.*\.src\.rpm$" % _pkg)
            if rpm_regex.match(os.path.basename(srpm)):
                total_linenum = _linenum
                break

        if self.callback: self.callback.start(text=_("Compiling %s...") % os.path.basename(srpm), size=100)
        logf = open(self.logfile, "a")
        cmd = "rpmbuild --rebuild --rcfile %s %s" % (rcfile,srpm,)
        pp = os.popen('{ ' + cmd + '; } 2>&1', 'r')
        linenum = 0
        line = 1
        while line:
            line = pp.readline()
            logf.write(line)
            linenum = linenum + 1
            if linenum % 4 == 0:
                percent = int((linenum*100)/total_linenum)
                if percent > 100:
                    percent = 100
                    pass
                if self.callback: self.callback.update(percent)
                pass
            pass
        status = pp.close()
        execute_command(["rm", "-f",  rcfile])
        execute_command(["rm", "-f",  macrofile])
        if not status == None:
            logf.close()
            execute_command(["rm", "-rf", rpmdir])
            raise InstallError(_("Failed compiling %s.") % (srpm,))

        logf.close()
        if not os.path.exists(GENERATE_RPMDIR):
            os.makedirs(GENERATE_RPMDIR)
        (ret, res) = execute_command(["find", rpmdir+"/RPMS/", "-type", "f"])
        if ret == 0 and len(res) > 0:
            for aline in res:
                aline = aline.strip()
                execute_command(["mv", "-f", aline, GENERATE_RPMDIR])
        execute_command(["rm", "-rf", rpmdir])
        if self.callback: self.callback.update(100)

    def compareVersion(self, filename):

        fdno = os.open(filename, os.O_RDONLY)
        file_header = self.ts.hdrFromFdno(fdno)
        os.close(fdno)

        rpm_name = file_header[rpm.RPMTAG_NAME]
        rpm_arch = file_header[rpm.RPMTAG_ARCH]

        mi = self.ts.dbMatch('name', rpm_name)
        if mi:
            compare = -2
            for inst_header in mi:
                compare = rpm.versionCompare(inst_header,file_header)
                if compare == 0:
                    installer.install.write_log(_("Both %s.%s packages are equal.") % (rpm_name, rpm_arch,))
                elif compare == 1:
                    installer.install.write_log(_("Old %s.%s package is newer.") % (rpm_name, rpm_arch,))
                elif compare == -1:
                    installer.install.write_log(_("Old %s.%s package is older.") % (rpm_name, rpm_arch,))
        if compare == -2:
            installer.install.write_log(_("%s.%s package is not installed.") % (rpm_name, rpm_arch,))

        return compare

    def getSystemArch(self):
        try:
            from rpmUtils.arch import getBaseArch
            return getBaseArch()
        except:
            import platform
            arch = platform.processor()
            """
            x86_regex = re.compile("^i[3456]86$")
            if x86_regex.search(arch):
                arch = "i386"
            """
            return arch

    def detectArchOrder(self):
        order = []
        order.append(self.getSystemArch())
        order.append("noarch")
        if not "i386" in order:
            order.append("i386")
        return order

