#!/usr/bin/env python
#
# 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.
#
"""
    job_sync_snapdiff.py
    ~~~~~~~~~~~~~~~~~~~~

    Generate repo and image diffs, sync them to tizen.org.

"""

import os
import re
import sys
import bs4
from jinja2 import Environment, FileSystemLoader
import requests
import snapdiff

from common.buildtrigger import trigger_info
from common.utils import sync
from common.buildtrigger import trigger_next

def search_last_build(build_url, release_name, released=False):
    """Search the lastest build in build_url.

    :param build_url: build url contains builds.
    :param released: is a released url or not.
    :return: path. the last build url.
    """
    res = requests.get(build_url)
    if res.status_code != 200:
        print 'access build_url: %s , error: %s' % (build_url, res.status_code)
        return ""
    soup = bs4.BeautifulSoup(res.text)
    pattern_re = '^%s_[0-9]{8}.[0-9]+/$' % release_name
    pattern = re.compile(pattern_re)
    builds = [i.text for i in soup.find_all(href=pattern)]
    if not builds:
        print "Not find the any snapshot in %s, skip" % build_url
        return ""

    if released:
        last_build = builds[-1]
    elif len(builds) > 1:
        last_build = builds[-2]
    else:
        last_build = None

    return os.path.join(build_url, last_build) if last_build else None

def get_last_build(url_pub_base, repo_path, release_name):
    """Return the second lastest build.

    :param url_pub_base: the public base url.
    :param repo_path: the relative repo path.
    :return: path. the second lastest build url.
    """
    last_build_url = os.path.join(url_pub_base,
                                  repo_path[:repo_path.rfind('/')])
    return search_last_build(last_build_url, release_name)

def get_released_build(release_base, repo_path, release_name):
    """Return the lastest released build.

    :param release_base: release base url.
    :param repo_path: the relative repo path.
    :return: path tuple. the lastest daily and weekly builds.
    """
    base_contains = '/'.join(repo_path.split('/')[1:-1])
    daily_url = os.path.join(release_base, 'daily', base_contains)
    weekly_url = os.path.join(release_base, 'weekly', base_contains)
    return search_last_build(daily_url, release_name, True), \
           search_last_build(weekly_url, release_name, True)

def _get_buildid(url):
    """Get build id from url"""
    result = re.search(r'\w*_\d{8}\.\d*', url)
    return result.group(0) if result else url

def _get_name(url):
    """Get image name from url"""
    name = url[:-1].split('/')[-1]
    return name or url

def generate_diff(old_url, new_url, repo, name, snap_diff_name, style='repo'):
    """Generate repo or image diff.

    :param old_url: old repo url.
    :param new_url: new repo url.
    :param repo_path: the repo base path.
    :param name: diff prefix name.
    """
    sync_out = os.path.join(os.getenv('WORKSPACE'), 'outdir')
    reports = os.path.join(sync_out, 'builddata', 'reports', snap_diff_name, repo)
    diff_name = '-'.join([name, _get_buildid(old_url), _get_buildid(new_url)])
    snapdiff.diff_to_dist(old_url, new_url, reports, style=style,
            diff_name=diff_name)
    return '%s.html' % diff_name

def sync_world(template_name, repo_name, repo_path, snap_diff_name, kwargs):
    """Sync repo and image diff.

    :param template_name: template we will use generate sumary index.
    :param kwargs: all context we use render template.
    :param kwargs: type is dict
    """
    template_path = os.path.join(os.path.dirname(__file__), 'templates')
    env = Environment(loader=FileSystemLoader(template_path))
    template = env.get_template(template_name)
    content = template.render(kwargs)
    sync_out = os.path.join(os.getenv('WORKSPACE'), 'outdir')
    reports = os.path.join(sync_out, 'builddata', 'reports', snap_diff_name, repo_name)
    if not os.path.exists(reports):
        return
    with open(os.path.join(reports, 'index.html'), 'w') as index_file_p:
        index_file_p.write(content)
    return sync(sync_out, os.path.join(os.getenv('IMG_SYNC_DEST_BASE'), \
                repo_path))

def main():
    """The main body"""
    info = trigger_info(os.getenv('TRIGGER_INFO'))
    repo_name = list(info)[0]
    content = info[repo_name]

    if not content:
        return

    url_pub_base = content[0]['url_pub_base']
    repo_path = content[0]['repo_path']
    build_id = content[0]['buildid']
    project = content[0]['project']
    # pylint: disable=W0612
    # disable unused variable
    release_name, _release_id = re.search(r'(\S*)_(\d{8}\.\d*)', \
                                         build_id).groups()
    # pylint: enable=W0612
    diff_root = os.path.join(url_pub_base, repo_path)
    current_repo_url = os.path.join(diff_root, 'repos', repo_name, 'packages')

    context = {'id': content[0]['buildid']}
    context['repo'] = []

    # if last build is selected, use it.
    last_repo_base = ""
    if "selected_snapshot" in content[0]:
        last_repo_base = content[0]["selected_snapshot"]
    else:
        # generate current and last repo diff
        last_repo_base = get_last_build(url_pub_base, repo_path, release_name)

    snap_diff_name = "diff"
    if last_repo_base:
        last_repo_url = os.path.join(last_repo_base, 'repos',
                                     repo_name, 'packages')
        snap_diff_name = '_'.join(['diff', _get_buildid(last_repo_url), _get_buildid(current_repo_url)])
        last_current = generate_diff(last_repo_url, current_repo_url,
                                     repo_name, 'last_current', snap_diff_name)
        last_id = _get_buildid(last_repo_url)
        context['repo'].append(('last_build', last_id, last_current))

    # releases' url
    releases_url = os.path.join(url_pub_base, 'releases')

    # generate current and releases repo diff
    daily_repo_base, weekly_repo_base = get_released_build(releases_url,
                                                           repo_path,
                                                           release_name)

    daily_repo_url = os.path.join(daily_repo_base,
                                  'repos', repo_name, 'packages')
    weekly_repo_url = os.path.join(weekly_repo_base,
                                   'repos', repo_name, 'packages')
    if (daily_repo_base and
        requests.get(daily_repo_url).status_code == 200):
        daily_current = generate_diff(daily_repo_url, current_repo_url,
                                      repo_name, 'daily_current', snap_diff_name)
        daily_id = _get_buildid(daily_repo_url)
        context['repo'].append(('daily_build', daily_id, daily_current))

    if (weekly_repo_base and
        requests.get(weekly_repo_url).status_code == 200):
        weekly_current = generate_diff(weekly_repo_url, current_repo_url,
                                       repo_name, 'weekly_current', snap_diff_name)
        weekly_id = _get_buildid(weekly_repo_url)
        context['repo'].append(('weekly_build', weekly_id, weekly_current))

    # generate image diff
    context['images'] = {}

    for item in content:
        current_image_url = os.path.join(diff_root, item['images_path'])
        last_image_url = os.path.join(
                       last_repo_base,
                       item['images_path']) if last_repo_base else None
        daily_image_url = os.path.join(
                        daily_repo_base,
                        item['images_path']) if daily_repo_base else None
        weekly_image_url = os.path.join(
                         weekly_repo_base,
                         item['images_path']) if weekly_repo_base else None

        context['images'][item['name']] = []

        # collect each image information.
        if requests.get(current_image_url).status_code == 200:
            if (last_image_url and
                requests.get(last_image_url).status_code == 200):
                name = generate_diff(last_image_url, current_image_url,
                                     repo_name, item['name']+'_last_current',
                                     snap_diff_name,
                                     style='image')
                context['images'][item['name']].append(('last_build',
                                                        last_id, name))
            if (daily_image_url and
                requests.get(daily_image_url).status_code == 200):
                name = generate_diff(daily_image_url, current_image_url,
                                     repo_name, item['name']+'_daily_current',
                                     snap_diff_name,
                                     style='image')
                context['images'][item['name']].append(('daily_build',
                                                        daily_id, name))
            if (weekly_image_url and
                requests.get(weekly_image_url).status_code == 200):
                name = generate_diff(weekly_image_url, current_image_url,
                                     repo_name, item['name']+'_weekly_current',
                                     snap_diff_name,
                                     style='image')
                context['images'][item['name']].append(('weekly_build',
                                                        weekly_id, name))

        # if image is empty, pop it.
        if not context['images'][item['name']]:
            context['images'].pop(item['name'])

    # sync all
    output = sync_world('index.html', repo_name, repo_path, snap_diff_name, context)
    # TRIGGER NEXT RSYNC-DOWNLOAD (MIRROR)
    if os.getenv("RSYNC_DOWNLOAD_ENABLED", "0") != "0":
        trigger_next('RSYNC_DOWNLOAD', {'repo_path': repo_path,
                                        'project': project})
    return output

if __name__ == '__main__':
    sys.exit(main())
