#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4
#
# Copyright (c) 2014, 2015, 2016 Samsung Electronics.Co.Ltd.
#
# 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; version 2 of the License
#
# 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.
#
import os
import re
import sys
import json
import subprocess
import gzip
import shutil
from datetime import datetime
from common.buildtrigger import trigger_info, trigger_next
from common.utils import xml_to_obj, execute_shell

class LocalError(Exception):
    """Local error exception."""
    pass

class ref_import_rpm_obs(object):

    profile = None
    profile_basedir = None
    profile_repos = {}

    def setup_profile(self, name, project):

        # set up profile
        self.profile = {'name': name, \
                        'refprj': project, \
                        'dest_dir': '/srv/obs/build', \
                        'option': 1}

        #prepare dir 
        basedir = os.path.join(self.profile['dest_dir'],self.profile['refprj'])
        if os.path.exists(basedir):
            #shutil.rmtree(basedir)
            print "exists"
        else:
            print "%s dir is not exists" %(basedir)
            return False
        self.profile_basedir = basedir
        #os.mkdirs(basedir)
        repo_dirs = {}
        repo_dirs['repo'] = [ (repo) for repo in os.listdir(basedir) if os.path.isdir(os.path.join(basedir, repo))]
        for repo_dir in repo_dirs['repo']:
            repo_dirs[repo_dir] = [ (arch_dir, os.path.join(repo_dir, arch_dir)) \
                                    for arch_dir in os.listdir(os.path.join(basedir,repo_dir)) \
                                        if os.path.isdir(os.path.join(basedir,repo_dir, arch_dir)) ]

        self.profile_repos = repo_dirs
        for repo in self.profile_repos['repo']:
            for arch , path in self.profile_repos[repo]:
                print 'repo = %s , arch = %s, path = %s' %(repo, arch, path)

        #self.profile['repoarch']
        print 'project = %s' % (project)
        sys.stdout.flush()

        return self.profile

    def copy_rsync_rpm(self, repospath, archfilelists, dstdir):
        """
        """
        file_name="/srv/obs/build/_filelist"
        content = ""

        if os.path.isfile(file_name):
            os.remove(file_name)

        with open(file_name, 'w') as f:
            for filepath in archfilelists:
                f.write(os.path.join(repospath,filepath)+'\n')
        cmd = "cat %s | " %(file_name)
        cmd += "xargs -n 1 -P 8 -I% rsync -avz --bwlimit=5120000 % "
        cmd += "%s/" %(dstdir)
        print 'cmd = %s' %(cmd)
        subprocess.call(cmd, shell=True)

    def construct_srv_obs_build_project_repo_package(self, rsyncd, snapshotdir):
        """
        """
        def clear_primary_files(self):
            for repo in self.profile_repos['repo']:
                dstdir = os.path.join(self.profile_basedir, repo)
                primarymd = os.path.join(dstdir, \
                                         [ x for x in os.listdir(dstdir) \
                                               if x.endswith('-primary.xml.gz') ][0])
                os.remove(primarymd)

        print "\n\n3) RSYC DOWNLOAD PRIMARY AND DEPENDS XML for %s at %s " % (self.profile['name'], datetime.now())
        for repo in self.profile_repos['repo']:
            repodata_path = os.path.join(rsyncd,snapshotdir,"repos",repo,"packages","repodata")
            cmd = "rsync %s/ --list-only --include='%s' --exclude='*'" \
                  " | awk '{ print $5; }' | grep '.xml.gz' " \
                  % (repodata_path, "*-primary.xml.gz")
            primarylist = execute_shell(cmd)
            if primarylist and type(primarylist) == list:
                dstdir =  os.path.join(self.profile_basedir, repo)
                self.copy_rsync_rpm(repodata_path, primarylist, dstdir)
            else:
                print 'No primary repodata found'
                clear_primary_files(self)
                return 3

        depends_path = os.path.join(rsyncd,snapshotdir,"builddata","depends")
        cmd = "rsync %s/ --list-only --include='%s' --exclude='*'" \
              " | awk '{ print $5; }' | grep '_revpkgdepends.xml' " \
              % (depends_path, "*_revpkgdepends.xml")
        deplist = execute_shell(cmd)
        if deplist and type(deplist) == list:
            dstdir =  os.path.join(self.profile_basedir)
            self.copy_rsync_rpm(depends_path, deplist, dstdir)
        else:
            clear_primary_files(self)
            print 'No depends list found'
            return 4

        print "\n\n4) GENERATE PACKAGE RPM LIST for %s at %s " % (self.profile['name'], datetime.now())
        for repo in self.profile_repos['repo']:
            src_bin_map = {}
            dstdir = os.path.join(self.profile_basedir, repo)
            primarymd = os.path.join(dstdir, \
                                     [ x for x in os.listdir(dstdir) \
                                           if x.endswith('-primary.xml.gz') ][0])
            # Read primary repo metadata
            primary = xml_to_obj(gzip.open(os.path.join(dstdir, primarymd)))
            print 'Removing primarymd %s' % primarymd
            os.remove(primarymd)
            for package in primary.package:
                spec_name = re.search(r'(.*)-(.*)-(.*).src.rpm', package.format.sourcerpm).groups()[0]
                if spec_name in src_bin_map:
                    src_bin_map[spec_name].append(package.location.href)
                else:
                    src_bin_map[spec_name] = [package.location.href]
            # Read builddep info
            for _file in [ x for x in os.listdir(self.profile_basedir) \
                               if repo in x and x.endswith('_revpkgdepends.xml') ]:
                with open(os.path.join(self.profile_basedir, _file)) as df:
                    depends = xml_to_obj(''.join(df.readlines()))
                    if not depends or not depends.package:
                        continue
                    for package in depends.package:
                        if package.source in src_bin_map and package.source != package.name:
                            src_bin_map[package.name] = src_bin_map.pop(package.source)
            print '\nRemoving garbage buildepinfo files'
            for f in os.listdir(self.profile_basedir):
                if re.search('.*_%s_.*_revpkgdepends.xml' % repo, f):
                    os.remove(os.path.join(self.profile_basedir, f))

            print "\n\n5) HARD LINK PACKAGE RPMS for %s at %s" % (repo, datetime.now())
            target_arch = [ x for x in os.listdir(dstdir) if os.path.isdir(os.path.join(dstdir, x)) ]
            #TODO: arch hack
            if len(target_arch) != 1:
                if 'i586' in target_arch:
                    target_arch.remove('i586')
            if len(target_arch) != 1:
                if 'x86_64' in target_arch:
                    target_arch.remove('x86_64')
            print '  * %s... Start make link, target_arch : %s' % (repo, target_arch)
            count = 0
            for pkg_name in src_bin_map:
                for y in src_bin_map[pkg_name]:
                    arch, rpm = y.split('/')
                    #TODO: i686 -> i586, noarch -> target_arch
                    arch = arch.replace('i686', 'i586').replace('noarch', target_arch[0])
                    src_file = os.path.join(dstdir,arch,':full', rpm)
                    pkg_dir = os.path.join(dstdir,arch,pkg_name)
                    dst_file = os.path.join(pkg_dir, rpm)
                    if not os.path.exists(src_file):
                        print '  not exist... %s, %s' % (src_file, y)
                        raise LocalError('missing rpms')
                    # Link rpms... /repo/arch/:full/*.rpm -> /repo/arch/pkg/*.rpm
                    if not os.path.exists(pkg_dir):
                        os.makedirs(pkg_dir)
                    if os.path.exists(dst_file):
                        os.remove(dst_file)
                    os.link(src_file, dst_file)
                    count += 1
            print '    Total %d rpms linked at %s' % (count, datetime.now())
            sys.stdout.flush()
        print '\n\n'

    def run_ref_import_rpm_obs(self, action=None):

        print '---[JOB STARTED]-------------------------'

        fields = trigger_info(os.getenv("TRIGGER_INFO"))

        # Check if we've got required fieldsdk-rootstraps in TRIGGER_INFO
        for field in ('profile', 'target', 'build_id', 'repo_path'):
            if field not in fields:
                print 'Error: TRIGGER_INFO doesn\'t contain %s' % field
                return -1

        self.profile = self.setup_profile(fields['profile'], fields['target'])
        if not self.profile:
            print 'Skip Sync OBS project %s' % fields['target']
            return 0

        buildid = fields['build_id'].split('_')[1]
        snapshotdir = fields['repo_path']
        rsyncd = os.getenv('IMG_SYNC_DEST_BASE')
        print 'rsyncd = %s snapshotdir = %s ' %(rsyncd, snapshotdir)
        if not rsyncd and not snapshotdir and not buildid:
            print "Please check rsync and snapshot dir. skip!!!"
            return

        print "1) Init a rpm files from download server "
        for repo in self.profile_repos['repo']:

            rpath = os.path.join(rsyncd,snapshotdir,"repos",repo)
            repospath = os.path.join(rsyncd,snapshotdir,"repos")
            noarchstring = "*.noarch.rpm"

            print "1-1) repo = %s" %(repo)
            for arch , path in self.profile_repos[repo]:
                print "1-2) repo = %s , arch = %s" %(repo,arch)
                rescan_after_delete = False
                for del_x in os.listdir(os.path.join(self.profile_basedir, repo, arch)):
                    del_path = os.path.join(self.profile_basedir, repo, arch, del_x)
                    if os.path.isdir(del_path):
                        shutil.rmtree(del_path)
                        rescan_after_delete = True
                if rescan_after_delete:
                    del_cmd = "obs_admin --rescan-repository %s %s %s " \
                               %(self.profile['refprj'],repo,arch)
                    subprocess.call(del_cmd, shell=True)

                archstring = "*.%s.rpm" % (arch)
 
                #print rpath
                cmd = "rsync %s -r --list-only --include='%s' --include='*/' --exclude='*'" \
                      " | awk '{ print $5; }' | grep '.rpm' " \
                      % (rpath , archstring)
                archfilelists = execute_shell(cmd)
                if arch == 'i586':
                    print 'add i686 arch'
                    cmd = "rsync %s -r --list-only --include='%s' --include='*/' --exclude='*'" \
                          " | awk '{ print $5; }' | grep '.rpm' " \
                          % (rpath , "*.i686.rpm")
                    #print cmd
                    extrafilelists = execute_shell(cmd)
                    if extrafilelists:
                        if archfilelists:
                            archfilelists += extrafilelists
                        else:
                            archfilelists = extrafilelists

                # make dir
                dstdir = os.path.join(self.profile_basedir, repo, arch,":full")
                #print dstdir
                if not os.path.exists(dstdir):
                    os.mkdir(dstdir)

                # Copy arch rpm binary
                print "1-4) Copy arch rpm binary "
                if archfilelists:
                    self.copy_rsync_rpm(repospath, archfilelists, dstdir)

                # search noarch list
                cmd = "rsync %s -r --list-only --include='%s' --include='*/' --exclude='*'" \
                      " | awk '{ print $5; }' | grep '.rpm' " \
                      % (rpath , noarchstring)
                #print cmd
                noarchfilelists = execute_shell(cmd)

                # Copy noarch rpm binary
                print "1-6) Copy noarch rpm binary "
                if noarchfilelists:
                    self.copy_rsync_rpm(repospath, noarchfilelists, dstdir)

        # Link rpms from :full to each package directories
        #self.construct_srv_obs_build_project_repo_package(rsyncd, snapshotdir)

        # obs-admin rescan
        print "2) obs_admin --rescan-repository "
        for repo in self.profile_repos['repo']:
            for arch , path in self.profile_repos[repo]:
                cmd = "obs_admin --rescan-repository %s %s %s " \
                      %(self.profile['refprj'],repo,arch)
                subprocess.call(cmd, shell=True)

        # Link rpms from :full to each package directories
        if os.getenv("REF_IMPORT_RPM_WITH_SRC",0) == 1:
            self.construct_srv_obs_build_project_repo_package(rsyncd, snapshotdir)

    def main(self, action):

        print('-----[JOB STARTED: importrpm_for_obs  ]-----')
        self.run_ref_import_rpm_obs(action[1] if len(action) == 2 else None)

if __name__ == '__main__':
    try:
        trigger = ref_import_rpm_obs()
        sys.exit(trigger.main(sys.argv))
    except Exception as e:
        print(e)
        sys.exit(1)

