#!/usr/bin/env python
#
# This file is part of REPA: Release Engineering Process Assistant.
#
# Copyright (C) 2014 Intel Corporation
#
# REPA is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# 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.

"""
Diff module.
Show the difference between projects.
"""

import sys
import os
import json

from collections import namedtuple

import xml.etree.ElementTree as ET

from repa.common import RepaException, Colorizer, get_prerelease
from repa.obs import OBS
from repa.main import sub_main

def gen_data(manifest):
    """Parse manifest. Yield git path and revision for every entry."""
    try:
        etree = ET.parse(manifest)
    except ET.ParseError as err:
        raise RepaException("Can't parse manifest %s: %s" % (manifest, err))

    for project in etree.getroot().findall('project'):
        yield project.get('path'), project.get('revision')

def get_tag(obs, project, package):
    """Get tag from _git-meta file in target project."""
    context = obs.get_file(project, package, "_service:gbs:_git-meta")
    if not context:
        return None
    try:
        data = json.loads(context)
    except ValueError:
        return None
    if "tag" in data:
        return data["tag"]["tagname"]

    return None

def diff(obs, cmpinfo, targetinfo, is_colorize=False):
    """
    Show the difference between two projects in terms of
    revisions and tags.
    cmpinfo and target info are named tuples: (project, manifest)
    """
    colorizer = Colorizer(is_colorize)

    target_data = dict(gen_data(targetinfo.manifest))
    cmp_data = dict(gen_data(cmpinfo.manifest))

    for path in sorted(cmp_data):
        rev_target = target_data.get(path)
        rev_cmp = cmp_data[path]
        if rev_target != rev_cmp:
            package = os.path.basename(path)

            # Get tag, accepted to cmp_project
            cmp_tag = get_tag(obs, cmpinfo.project, package)
            status = ''
            # Colorize tag if it's pending in target project
            if cmp_tag:
                try:
                    prerelease = get_prerelease(cmp_tag, targetinfo.project)
                    if obs.exists(prerelease):
                        if is_colorize:
                            cmp_tag = colorizer.green(cmp_tag)
                        else:
                            status = 'pending'
                except RepaException:
                    pass

            print "%-55s %-12s %-12s %-40s %s" % \
                  (path, rev_cmp[:10], str(rev_target)[:10], cmp_tag, status)


class Diff(object):
    """Subcommand: diff projects."""

    name = 'diff'
    description = 'Show the difference between projects'
    help = description

    @staticmethod
    def add_arguments(parser, _):
        """Adds arguments to the parser. Called from [sub_]main."""
        parser.add_argument('cmp_project', help='Compared OBS project')
        parser.add_argument('cmp_manifest', help='Manifest of compared project')
        parser.add_argument('manifest', help='Manifest of current project')

    @staticmethod
    def run(argv):
        """Command line entry point. Called from [sub_]main."""
        obs = OBS(argv.apiurl, argv.apiuser, argv.apipasswd)
        difftype = namedtuple('diff', ['project', 'manifest'])
        return diff(obs, difftype(argv.cmp_project, argv.cmp_manifest),
                    difftype(argv.project, argv.manifest), argv.colorize)

if __name__ == '__main__':
    sys.exit(sub_main(sys.argv[1:], Diff()))
