#!/usr/bin/env python
#-*- coding: utf-8 -*-
import os
import re
import sys
import shutil
import tarfile
import subprocess
import logging
from optparse import OptionParser
from os.path import join
from tempfile import mkdtemp

# Version setter
VERSION = "1.0"
methods = {}


# Check if pysvn is installed
try:
    import pysvn

    def load_svn(base_url, release, tmp_dir):
        source = base_url + "/" + release
        svn = pysvn.Client()

        for part in ["gosa-core", "gosa-plugins"]:
            print("Retrieving %s from SVN..." % part)
            rev = svn.export(source + "/" + part, join(tmp_dir, part))

        return "svn" + str(rev.number)

    methods["svn"] = load_svn
except:
    pass


def tar_extract(name, target):
    tar = tarfile.open(name, "r:gz")
    tar.extractall(target)
    tar.close()

def tar_create(name, source):
    tar = tarfile.open(name, "w:gz")
    tar.add(source)
    tar.close()

def build(release, base_url, target="/tmp", method="svn", key_id=None, cleanup=True, smarty=False):

    # Absolutize target
    target = os.path.abspath(target)

    # Create target and temporary directory
    try:
        tmp_dir = mkdtemp()
        target_dir = join(target, "gosa")
        os.makedirs(target_dir)

        # Get data
        if method in methods:
            rev = methods[method](base_url, release, tmp_dir)
        else:
            print("Error: method '%s' is notavailable" % method)

        # Prepare gosa-core for archiving
        core_dir = join(tmp_dir, "gosa", "gosa-core")
        os.makedirs(join(tmp_dir, "gosa"))
        shutil.move(join(tmp_dir, "gosa-core"), join(tmp_dir, "gosa"))
        shutil.move(join(core_dir, "debian"), target_dir)

        # Remove all but .php files provided by GOsa
        if not smarty:
            smarty_dir = join(core_dir, "include", "smarty")
            for rfile in [f for f in os.listdir(join(smarty_dir, "plugins"))
                if not f in ["block.render.php", "block.t.php", "function.msgPool.php",
                "function.factory.php", "function.image.php"]]:

                os.remove(join(smarty_dir, "plugins", rfile))

            for rfile in [join(smarty_dir, f) for f in os.listdir(smarty_dir)]:
                if os.path.basename(rfile) != "plugins":
                    if os.path.isdir(rfile):
                        shutil.rmtree(rfile)
                    else:
                        os.remove(rfile)

            # Remove smarty3 package section
            out = ""
            control = join(target_dir, "debian", "control")
            data = open(control, 'r')
            remove = False

            for line in data:
                line = line.rstrip()
                if line == "Package: smarty3":
                    remove = True
            
                if not remove:
                    out += line + "\n"

                if remove and line == "":
                    remove = False

            of = open(control, 'w')
            of.write(out)
            of.close()

        # Get target version number
        version = None
        with open(join(core_dir, "Changelog")) as f:
            for line in f.readlines():
                if line.startswith("* gosa"):
                    version = line.split(" ")[2].strip()
                    break

        # If we're in trunk or branches, add revision to version
        if re.match(r"^(trunk|branches/)", release):
            version = "%s+%s" % (version, rev)
            os.chdir(target_dir)
            subprocess.call(["dch", "--newversion=" + version + "-1",
                "--no-force-save-on-release", "Development snapshot"])

        # Build gosa-core tar file
        os.chdir(tmp_dir)
        tar_create(join(target, "gosa_%s.orig.tar.gz" % version), "gosa")

        # Clean up and build plugin archives
        tars = []
        os.chdir(join(tmp_dir, "gosa-plugins"))
        for f in os.listdir(join(tmp_dir, "gosa-plugins")):
            pth = join(target, "gosa_%s.orig-%s.tar.gz" % (version, f))
            if os.path.exists(join(tmp_dir, "gosa-plugins", f, "plugin.dsc")):
                os.remove(join(tmp_dir, "gosa-plugins", f, "plugin.dsc"))
            tar_create(pth, f)
            tars.append(pth)

        # Stage build directory
        tar_extract(join(target, "gosa_%s.orig.tar.gz" % version), target)
        for tar_file in tars:
            tar_extract(tar_file, target_dir)

        # Build packages
        print("Building packages to %s" % target)
        os.chdir(target_dir)
        if key_id:
            status = subprocess.call(["dpkg-buildpackage", "-k", key_id, "-rfakeroot"])
        else:
            status = subprocess.call(["dpkg-buildpackage", "-uc", "-us", "-rfakeroot"])

        # Bail out?
        if status:
            raise Exception("Build failed: exit code %s" % status)

    except Exception as detail:
        print("Error:", str(detail))

    # Cleanup
    finally:
        if cleanup:
            if os.path.exists(tmp_dir):
                shutil.rmtree(tmp_dir)
            if os.path.exists(target_dir):
                shutil.rmtree(target_dir)

def main():
    # Sanity check
    for cli, pkg in [("/usr/bin/dpkg-buildpackage", "dpkg-dev"),
        ("/usr/bin/fakeroot", "fakeroot"),
        ("/usr/bin/dch", "devscripts"),
        ("/usr/bin/dh", "debhelper")]:

        if not os.path.exists(cli):
            print("Error: %s is missing, please install %s" % (cli, pkg))
            exit(1)

    # Methods available?
    if not methods:
        print("Error: no retrieval methods available, please install python-svn")
        exit(1)

    # Parse commandline options
    op = OptionParser(usage="%prog - GOsa packaging aid",
        version="%prog " + VERSION)
    op.add_option("-s", "--source", dest="source",
        default="https://oss.gonicus.de/repositories/gosa",
        help="retrieval base path [%default]", metavar="URL")
    op.add_option("-a", "--with-smarty", dest="smarty",
        default=False, action="store_true",
        help="build smarty3 packages from smarty provided by gosa source")
    op.add_option("-t", "--target", dest="target",
        default=".",
        help="target directory [%default]", metavar="PATH")
    op.add_option("-m", "--method", dest="method",
        default="svn",
        help="retrieval method [%default]", metavar="METHOD")
    op.add_option("-n", "--no-cleanup", action="store_false",
        dest="cleanup", default=True,
        help="don't clean up temporary files")
    op.add_option("-k", "--key-id", dest="key_id",
        default=None,
        help="GPG key ID used for signing if applicable", metavar="KEY_ID")

    (options, args) = op.parse_args()
    if len(args) != 1:
        op.print_help()
        exit(1)

    # Allow version shortcut, but prepend tags/
    release = args[0]
    if re.match(r"^[0-9]", release):
        release = "tags/" + release

    # Validate release
    if not re.match(r"^(tags/.|branches/.|trunk$)", release):
        print("Error: release must be in the format tags/x (or just x), branches/x or trunk")
        exit(1)

    # Start build
    build(release, options.source, options.target,
        options.method, options.key_id, options.cleanup, options.smarty)


""" Main GOsa packaging aid """
if __name__ == '__main__':
    if not sys.stdout.encoding:
        sys.stdout = codecs.getwriter('utf8')(sys.stdout)
    if not sys.stderr.encoding:
        sys.stderr = codecs.getwriter('utf8')(sys.stderr)

    main()
