#!/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

import xml.etree.ElementTree as ET

from repa.common import RepaException, Colorizer, get_prerelease_projects
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, revision):
    """Get tag from the latest accepted SR."""
    for sreq in obs.get_srs(project, 'accepted', package):
        descr = sreq[-1]
        if descr and 'Commit: %s' % revision in descr:
            return descr.split('Tag: ')[-1].split()[0]
    return 'Not found'


def get_pending(obs, target_proj):
    """Get pair of git path: tag for pending prerelease projects."""
    result = {}
    for proj in get_prerelease_projects(obs, target_proj):
        meta = json.loads(obs.get_description(proj))
        for path in meta["projects"]:
            result[path] = meta["git_tag"]
    return result


def diff(obs, target_project, project1, target_manifest, manifest1,
         is_colorize=False):
    """Show the difference between project1 and project2."""
    colorizer = Colorizer(is_colorize)

    target_data = dict(gen_data(target_manifest))
    data1 = dict(gen_data(manifest1))

    pending = get_pending(obs, target_project)

    for path in sorted(data1):
        # if submission is not pending in target project
        # and revisions are the same - skip it
        if path not in pending and \
            (path in target_data and data1[path] == target_data[path]):
            continue

        package = os.path.basename(path)
        tag1 = get_tag(obs, project1, package, data1[path])
        target_tag = get_tag(obs, target_project, package,
                             target_data.get(path))

        if tag1 in pending.values():
            if is_colorize:
                tag1 = colorizer.green(tag1)
            else:
                tag1 += ' ready'
        print "%-50s %s %s %-37s %-37s %s" % \
                  (path, target_data.get(path, 'Not found ')[:10],
                   data1[path][:10], target_tag, tag1,
                   pending.get(path, 'Not found'))


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

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

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

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

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