#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4
#
# Copyright (C) 2010, 2011, 2012, 2013, 2014 Intel, 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; 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.
#
#    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
"""
This code is called by jenkins jobs triggered by OBS events.
"""

import os
import sys
import re
import datetime

from common.repomaker import RepoMaker, RepoMakerError
from common.buildtrigger import trigger_info, trigger_next
from common.buildservice import BuildService
from common.backenddb import BackendDB
from common.snapshot import Snapshot, SnapshotError, snapshot_project_enabled
from common.utils import make_latest_link

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


def prepare_trigger_data(images, build_id, path_repo, project,
                           url_pub_base):
    """
    prepare_trigger_data:
        Prepare the trigger data
    Args:
         images_ks (truple list): [(ks_file_name, ks_file_content),]
         build_id (str): the prerelease repo build_id
    """
    download_host = os.getenv('DOWNLOAD_HOST')
    trigger_data = {} #trigger job_imager
    trigger_snapdiff = {}

    for repo_name in images:
        trigger_data[repo_name] = []
        trigger_snapdiff[repo_name] = []
        for ksname, kickstart in images[repo_name].ksi.items():
            name = ksname.replace('.ks', '')
            if download_host:
                # replace host name
                kickstart = re.sub('^(repo .*--baseurl=[^:]+://)[^/]+(/.*)',
                                   '\\1%s\\2' % download_host, kickstart,
                                   count=1, flags=re.MULTILINE)
            data = {'name': name,
                    'kickstart': kickstart,
                    'buildid': build_id,
                    'images_path': os.path.join("images", repo_name, name),
                    'project': project,
                    'repo': repo_name,
                    'repo_path': path_repo,
                    'url_pub_base': url_pub_base
                    }

            trigger_data[repo_name].append(data)
            data_snapdiff = data.copy()
            data_snapdiff.pop('kickstart')
            trigger_snapdiff[repo_name].append(data_snapdiff)

    return trigger_data, trigger_snapdiff

def trigger_image_creation(trigger_data):
    """Trigger the image_creation jobs"""
    for repo in trigger_data.keys():
        for index, data in enumerate(trigger_data[repo]):
            trigger_next('image_trigger_%s_%s' % (repo, index), data)

def make_repo(project, backenddb, base_path, live_repo_base):
    """
    make repo.

    Args:
        project (str): OBS prerelease project name
        repo (str): name of the OBS live repository
        backenddb (BackendDB): backenddb instance
        base_path (str): path to the location of snapshot
        live_repo_base (str): path to live repo
    Raises:
        LocalError if can't create repos or can't find image configurations
    """

    try:
        snapshot = Snapshot(backenddb, base_path, obs_project=project)
    except SnapshotError, err:
        raise LocalError("Error getting snapshot info: %s" % str(err))

    # Increment snapshot
    snapshot.inc()

    targets = snapshot.targets
    live_repo_path = os.path.join(live_repo_base,
                                  project.replace(':', ':/'))
    repos = {}
    imagedatas = {}

    if buildmonitor_enabled:
        bm_snapshot_name = snapshot.build_id
        #buildmonitor.start_create_snapshot_for_post_build_snapshot(project,
        #                                                           bm_snapshot_name,
        #                                                           bm_start_datetime)
        bm_stage = 'Post_Snap_snapshot'
        bm_data = {"bm_stage" : bm_stage,
                   "project" : project,
                   "bm_snapshot_name" : bm_snapshot_name,
                   "bm_start_datetime" : str(bm_start_datetime)
                  }
        trigger_next("BUILD-MONITOR-2-%s" % bm_stage, bm_data)

    # Convert live repo to download structure
    for repo in targets:
        repomaker = RepoMaker(snapshot.build_id, snapshot.path)

        # Get specific repo buildconf from OBS, put the buildconf together with
        # repo-md
        build = BuildService(os.getenv("OBS_API_URL"),
                             os.getenv("OBS_API_USERNAME"),
                             os.getenv("OBS_API_PASSWD"))
        buildconf = build.get_repo_config(project, repo['Name'])

        try:
            repomaker.add_repo(live_repo_path, repo['Name'],
                               repo['Architectures'],
                               buildconf=buildconf, move=False)
        except RepoMakerError, err:
            raise LocalError("Unable to create download repo: %s" % err)

        if not repomaker.imagedata:
            if buildmonitor_enabled:
                print '[%s][LocalError] bm_project(%s)\n' % (__file__, bm_project)
                #buildmonitor.update_fail_create_snapshot_for_sr_stage(bm_project)
                bm_stage = 'Post_Snap_Fail'
                bm_data = {"bm_stage" : bm_stage,
                           "bm_snapshot_name" : snapshot.build_id,
                          }
                trigger_next("BUILD-MONITOR-5-%s" % bm_stage, bm_data)
            raise LocalError("Image configuration not found in %s" %
                              snapshot.path)

        repos.update(repomaker.repos)
        imagedatas[repo['Name']] = repomaker.imagedata

        if buildmonitor_enabled:
            bm_repo = repo['Name']             # emul32-wayland / emul64-wayland / arm-wayland / arm64-wayland / target-TM1
            bm_arch = repo['Architectures'][0] # ia32 / x86_64 / armv7l / aarch64 / armv7l
            if bm_arch == 'ia32':
                bm_arch = 'i686'

            bm_repo_dir = os.path.join(snapshot.path, "repos", bm_repo, "packages")
            #print '[%s] snapshot.path(%s), bm_repo(%s), bm_arch(%s)\n' \
            #      % (__file__, snapshot.path, bm_repo, bm_arch)
            #print '[%s] bm_repo_dir(%s), os.listdir(bm_repo_dir)(%s)\n' \
            #      % (__file__, bm_repo_dir, os.listdir(bm_repo_dir))

            bm_base_url = os.getenv("URL_PUBLIC_REPO_BASE")
            bm_pkg_url = bm_repo_dir.replace(base_path, bm_base_url)
            #print '[%s] bm_base_url(%s), base_path(%s), bm_pkg_url(%s)\n' \
            #      % (__file__, bm_base_url, base_path, bm_pkg_url)

            # get rpm files
            bm_pkg_name_lst = []
            bm_pkg_mdate_lst = []
            bm_pkg_size_lst = []
            bm_trg_count = 0
            bm_pkg_count = 0
            BM_PKG_LIMIT = 1100
            for root, dirs, files in os.walk(bm_repo_dir):
                #print '[%s] root(%s), dirs(%s), files(%s)\n' % (__file__, root, dirs, files)
                for each_file in files:
                    if each_file.endswith(".rpm"):
                        rpm_file_path = os.path.join(root, each_file)
                        rpm_file_mdate = os.path.getmtime(rpm_file_path)
                        rpm_file_size = os.path.getsize(rpm_file_path)
                        bm_pkg_name_lst.append(each_file)
                        bm_pkg_mdate_lst.append(rpm_file_mdate)
                        bm_pkg_size_lst.append(rpm_file_size)
                        #print '[%s] rpm_file_path(%s), rpm_file_mdate(%s), rpm_file_size(%s)\n' \
                        #      % (__file__, rpm_file_path, rpm_file_mdate, rpm_file_size)

                        # devide the big pkgs
                        bm_pkg_count += 1
                        #print '[%s] bm_pkg_count(%s), BM_PKG_LIMIT(%s)\n' \
                        #      % (__file__, bm_pkg_count, BM_PKG_LIMIT)
                        if bm_pkg_count >= BM_PKG_LIMIT:
                            # for trigger
                            bm_stage = 'Post_Snap_packages'
                            bm_data = {"bm_stage" : bm_stage,
                                       "bm_snapshot_name" : bm_snapshot_name,
                                       "bm_repo" : bm_repo,
                                       "bm_arch" : bm_arch,
                                       "bm_pkg_url" : bm_pkg_url,
                                       "bm_pkg_name_lst" : bm_pkg_name_lst,
                                       "bm_pkg_mdate_lst" : bm_pkg_mdate_lst,
                                       "bm_pkg_size_lst" : bm_pkg_size_lst,
                                      }
                            trigger_next("BUILD-MONITOR-3-%s-%s-%s-%s" % (bm_stage, bm_repo, bm_arch, bm_trg_count), bm_data)

                            # clear the data
                            bm_pkg_count = 0
                            bm_trg_count += 1
                            bm_pkg_name_lst = []
                            bm_pkg_mdate_lst = []
                            bm_pkg_size_lst = []
                            #print '[%s] reach the BM_PKG_LIMIT!!(%s), bm_pkg_count(%s), bm_trg_count(%s)\n' \
                            #      % (__file__, BM_PKG_LIMIT, bm_pkg_count, bm_trg_count)

            # for rest pkgs
            #buildmonitor.create_snapshot_packages_for_post_build_snapshot_package(repo,
            #                                                                      project,
            #                                                                      repo['Name'],
            #                                                                      repo['Architectures'][0],
            #                                                                      base_path,
            #                                                                      snapshot)
            bm_stage = 'Post_Snap_packages'
            bm_data = {"bm_stage" : bm_stage,
                       "bm_snapshot_name" : bm_snapshot_name,
                       "bm_repo" : bm_repo,
                       "bm_arch" : bm_arch,
                       "bm_pkg_url" : bm_pkg_url,
                       "bm_pkg_name_lst" : bm_pkg_name_lst,
                       "bm_pkg_mdate_lst" : bm_pkg_mdate_lst,
                       "bm_pkg_size_lst" : bm_pkg_size_lst,
                       "bm_trg_count" : bm_trg_count,
                       "bm_pkg_count" : bm_pkg_count,
                       "BM_PKG_LIMIT" : BM_PKG_LIMIT,
                      }
            #print '[%s] for rest pkgs!! BM_PKG_LIMIT(%s), bm_pkg_count(%s), bm_trg_count(%s)\n' \
            #      % (__file__, BM_PKG_LIMIT, bm_pkg_count, bm_trg_count)
            trigger_next("BUILD-MONITOR-3-%s-%s-%s-trg_%s" % (bm_stage, bm_repo, bm_arch, bm_trg_count), bm_data)

        # Generate image info to builddata/ dir
        repomaker.gen_image_info()

        # Generate repo manifest
        repomaker.gen_manifest_info(repo['Name'],
                                    os.getenv('GERRIT_FETCH_URL'),
                                    os.getenv('GERRIT_REVIEW_URL'))
        repomaker.gen_manifest_info_app_from_rpm(repo['Name'],
                                    os.getenv('GERRIT_FETCH_URL'),
                                    os.getenv('GERRIT_REVIEW_URL'),
                                    live_repo_path, repo['Architectures'])

    return {'project': project,
            'repo': repos,
            'repo_path': os.path.join(snapshot.dir, snapshot.build_id),
            'build_id': snapshot.build_id,
            'imagedata': imagedatas
        }

def main():
    """Script entry point."""

    print '---[JOB STARTED: %s ]-------------------------'
    global buildmonitor_enabled
    buildmonitor_enabled = os.getenv("BUILDMONITOR_ENABLED", "0") != "0"
    print 'buildmonitor_enabled(%s)\n' % (buildmonitor_enabled)
    if buildmonitor_enabled:
        global bm_start_datetime
        bm_start_datetime = datetime.datetime.now()

    obs_api = os.getenv("OBS_API_URL")
    obs_user = os.getenv("OBS_API_USERNAME")
    obs_passwd = os.getenv("OBS_API_PASSWD")
    base_url = os.getenv("URL_PUBLIC_REPO_BASE")
    base_path = os.getenv('PATH_REPO_BASE')
    live_repo_base = os.getenv('PATH_LIVE_REPO_BASE')

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

    project = content.get("project")

    build = BuildService(obs_api, obs_user, obs_passwd)

    if buildmonitor_enabled:
        global bm_project # for update_fail_xxx
        bm_project = project
        #buildmonitor.start_create_snapshot_for_sr_stage(project, bm_start_datetime)
        bm_stage = 'Post_Snap_Start'
        bm_data = {"bm_stage" : bm_stage,
                   "project" : project,
                   "bm_start_datetime" : str(bm_start_datetime),
                   "unresolvable_broken_packages": build.get_package_build_result(project, ["unresolvable", "broken"])
                  }
        trigger_next("BUILD-MONITOR-1-%s" % bm_stage, bm_data)

    # Init backend database
    redis_host = os.getenv("REDIS_HOST")
    redis_port = int(os.getenv("REDIS_PORT"))
    backenddb = BackendDB(redis_host, redis_port)

    repo_data = make_repo(project, backenddb, base_path, live_repo_base)

    # update/create the latest repo link
    make_latest_link(os.path.join(base_path, repo_data['repo_path']))

    # prepare trigger data for image creation jobs and snapdiff sync jobs
    trigger_data, trigger_snapdiff = prepare_trigger_data(
                                        repo_data['imagedata'],
                                        repo_data['build_id'],
                                        repo_data['repo_path'],
                                        project, base_url)

    # trigger image creation jobs
    trigger_image_creation(trigger_data)

    # trigger snapdiff sync
    for index, repo in enumerate(trigger_snapdiff.keys()):
        trigger_next('snapdiff-trigger_%s' %
                      index, {repo: trigger_snapdiff[repo]})

    # trigger post snapshot creation job with repo data
    data = repo_data.copy()
    # remove unused item
    data.pop('imagedata')

    trigger_next("post-snapshot", data)

    # Update NUGET pacakges to server
    if os.getenv("NUGET_UPDATE_ENABLE","0") == "1":
        trigger_next("update-nuget", data)

     # Trigger REF_CREATE_PRJ_OBS to server
    if os.getenv("REF_CREATE_PRJ_OBS_ENABLE","0") == "1":
        trigger_next("REF_CREATE_PRJ_OBS", data)

    if buildmonitor_enabled:
        bm_end_datetime = datetime.datetime.now()
        # for sr_stage & build_snapshot
        #buildmonitor.end_create_snapshot_create_images_for_sr_stage(project,
        #                                                            bm_start_datetime,
        #                                                            bm_end_datetime)
        bm_snapshot_url = os.path.join(base_url, repo_data['repo_path'])
        #buildmonitor.end_create_snapshot_for_post_build_snapshot(project,
        #                                                         bm_start_datetime,
        #                                                         bm_end_datetime,
        #                                                         bm_snapshot_url,
        #                                                         repo_data['build_id'])
        bm_stage = 'Post_Snap_End'
        bm_data = {"bm_stage" : bm_stage,
                   "bm_start_datetime" : str(bm_start_datetime),
                   "bm_end_datetime" : str(bm_end_datetime),
                   "bm_snapshot_url" : bm_snapshot_url,
                   "bm_snapshot_name" : repo_data['build_id']
                  }
        trigger_next("BUILD-MONITOR-4-%s" % bm_stage, bm_data)

        # Skip [BUILD-MONITOR_Post_Image] if Base project
        if 'base' in project.lower():
            print '[%s] This is [Base] project!! skip Post_Image \n' % (__file__)
        else:
            # To check the completion of image-creation stage at Post-release
            #buildmonitor.end_create_image_for_sr_stage(bm_start_datetime,
            #                                           project)
            bm_stage = 'Post_Image'
            bm_data = {"bm_stage" : bm_stage,
                       "bm_start_datetime" : str(bm_start_datetime),
                       "project" : project,
                       "bm_snapshot_name" : repo_data['build_id']
                      }
            trigger_next("BUILD-MONITOR_Post_Image", bm_data)

if __name__ == '__main__':
    try:
        sys.exit(main())
    except LocalError, error:
        print error
        sys.exit(1)
