#!/usr/bin/python
# vim: sw=4 et

# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
#
# 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.

from __future__ import print_function

import argparse
import glob
import os
import platform
import shutil
import sys
import tempfile
import time

import pymod2pkg

import sh
from sh import Command


def pymodule2pkg(spectemplate):
    specname = os.path.splitext(spectemplate)[0]
    modulename = os.path.splitext(os.path.basename(specname))[0]
    pkgname = pymod2pkg.module2package(modulename,
                                       platform.linux_distribution()[0])
    if modulename == 'openstack-macros':
        pkgname = modulename

    return pkgname


def get_osc_user():
    import osc.conf
    osc.conf.get_config()
    return osc.conf.get_apiurl_usr(osc.conf.config['apiurl'])


def upload_meta(project, build_repository, linkproject):
    projectlink = ''
    if linkproject:
        projectlink = '<link project="%s"/>\n' % linkproject

    description = ''
    if linkproject:
        if 'ZUUL_UUID' in os.environ:
            description = """
This project tests the following Zuul Change IDs: %(ZUUL_CHANGE_IDS)s\n
Branch used: %(ZUUL_BRANCH)s\n
Project used: %(ZUUL_PROJECT)s
""" % (os.environ)
    templ = """
<project name="%(project)s">
  <title>Autogenerated CI project</title>
  <description>
%(description)s
  </description>
%(projectlink)s
  <person userid="%(user)s" role="maintainer"/>
  <publish>
    <disable/>
  </publish>
%(build_repository)s
</project>""" % ({'project': project,
                  'user': get_osc_user(),
                  'description': description,
                  'projectlink': projectlink,
                  'build_repository': build_repository})

    with tempfile.NamedTemporaryFile(delete=False) as meta:
        meta.write(templ)
        print('Updating meta for ', project)
        meta.close()

        # work around build service bug that triggers a database deadlock
        for fail_counter in range(1, 5):
            try:
                sh.osc('api', '-T', meta.name, '/source/%s/_meta' % project)
                break
            except sh.ErrorReturnCode_1:
                # Sleep a bit and try again. This has not been scientifically
                # proven to be the correct sleep factor, but it seems to work
                time.sleep(2)
                continue
        os.unlink(meta.name)


def upload_meta_enable_repository(project, linkproject):
    repository = """
  <repository name="standard" %(repoflags)s>
    <path project="%(linkproject)s" repository="standard"/>
    <arch>x86_64</arch>
  </repository>
""" % ({'linkproject': linkproject,
        'repoflags': 'rebuild="local" block="local" linkedbuild="localdep"'})

    upload_meta(project, repository, linkproject)


def create_new_build_project(workdir, project, linkproject):
    sh.mkdir('-p', workdir)
    olddir = os.getcwd()
    try:
        os.chdir(workdir)
        if linkproject:
            upload_meta_enable_repository(project, linkproject)
        sh.osc('init', project)
    finally:
        os.chdir(olddir)


def generate_pkgspec(pkgoutdir, global_requirements, spectemplate, pkgname):

    obsservicedir = '/usr/lib/obs/service/'
    outdir = ('--outdir', pkgoutdir)

    olddir = os.getcwd()
    try:
        os.chdir(pkgoutdir)
        renderspec = Command(os.path.join(obsservicedir, 'renderspec'))

        renderspec(
            '--input-template', os.path.join(olddir, spectemplate),
            '--requirements', os.path.join(olddir, global_requirements),
            '--output-name', pkgname + '.spec', *outdir)

        format_spec_file = Command(
            os.path.join(obsservicedir, 'format_spec_file'))
        format_spec_file(*outdir)

        # configure a download cache to avoid downloading the same files
        download_env = os.environ.copy()
        download_env["CACHEDIRECTORY"] = os.path.join(
            os.path.expanduser("~"), ".cache", "download_files")

        download_files = Command(os.path.join(obsservicedir, 'download_files'))
        download_files(_env=download_env, *outdir)
    finally:
        os.chdir(olddir)


def osc_mkpac(workdir, packagename):
    olddir = os.getcwd()
    try:
        os.chdir(workdir)
        sh.osc('mkpac', packagename)
    finally:
        os.chdir(olddir)


def spec_is_modified(pkgoutdir, project, pkgname):
    specname = pkgname + ".spec"
    cached_spec = os.path.join(pkgoutdir, '.osc', specname)
    cleanup = False
    if not os.path.exists(cached_spec):
        cleanup = True
        sh.osc('api', '/source/%s/%s/%s.spec' % (
            project, pkgname, pkgname), _out=cached_spec)
    r = sh.cmp(
        '-s', os.path.join(pkgoutdir, specname), cached_spec, _ok_code=[0, 1])
    if cleanup:
        os.remove(cached_spec)
    return r.exit_code == 1


def osc_detachbranch(workdir, project, pkgname):
    olddir = os.getcwd()
    try:
        os.chdir(os.path.join(workdir))
        sh.osc('detachbranch', project, pkgname)
        os.mkdir(pkgname + '.b')
        for f in glob.glob(os.path.join(pkgname, '*')):
            os.rename(f, os.path.join(pkgname + '.b', os.path.basename(f)))
        sh.rm('-rf', pkgname)
        sh.osc('co', pkgname)
        for f in glob.glob(os.path.join(pkgname + '.b', '*')):
            dst = os.path.basename(f)
            try:
                os.unlink(os.path.join(pkgname, dst))
            except OSError:
                pass
            os.rename(f, os.path.join(pkgname, dst))
        os.rmdir(pkgname + '.b')
    finally:
        os.chdir(olddir)


def osc_commit_all(workdir, packagename):
    olddir = os.getcwd()
    try:
        os.chdir(os.path.join(workdir, packagename))
        sh.osc('addremove')
        sh.osc('commit', '--noservice', '-n')
    finally:
        os.chdir(olddir)


def copy_extra_sources(specdir, pkgoutdir):