# Copyright (C) 2012 Sebastian Pipping <sebastian@pipping.org>
# Licender under GPL v2 or later

from __future__ import print_function

VERSION_STR = '0.4.1'

import sys
import os
import portage
import re

try:
	import argparse
except ImportError:
	print("ERROR: You need Python 2.7+ unless you have module argparse "
		"(package dev-python/argparse on Gentoo) installed independently.", file=sys.stderr)
	sys.exit(1)


_revision_matcher = re.compile('-r([0-9]+)$')


def find_atoms(repository_path, category_package):
	versions = list()
	category, package = category_package.split('/')
	for root, dirs, files in os.walk(os.path.join(repository_path, category_package)):
		for f in files:
			if not f.endswith('.ebuild'):
				continue
			versions.append(f[len(package) + 1:-len('.ebuild')])
	return versions


def highest_revision_only(versions):
	def make_version(version, revison):
		if revision == 0:
			return version
		return '%s-r%s' % (version, revison)

	d = dict()
	for v in versions:
		match = _revision_matcher.search(v)
		if match is None:
			revision = 0
			version = v
		else:
			revision = int(match.group(1))
			version = v[:-len(match.group(0))]
		d[version] = max(d.get(version, 0), revision)
	return [make_version(version, revison) for version, revison in d.items()]


def find_missed_bumps(gentoo_versions, overlay_versions):
	missed_version_bumps = list()
	missed_revision_bumps = list()
	gentoo_zero_revisions = set(_revision_matcher.sub('', gv) for gv in gentoo_versions)
	for ov in overlay_versions:
		if '999' in ov:
			continue
		newer_than_gentoo = True
		for gv in gentoo_versions:
			if '999' in gv:
				continue
			if portage.vercmp(ov, gv) <= 0:
				newer_than_gentoo = False
				break
		if newer_than_gentoo:
			ov_without_revision = _revision_matcher.sub('', ov)
			if ov_without_revision in gentoo_zero_revisions:
				missed_revision_bumps.append(ov)
			else:
				missed_version_bumps.append(ov)
	return (missed_revision_bumps, missed_version_bumps)

def find_ebuild_changes(category_package, overlay_path, gentoo_versions, overlay_versions):
	ebuild_changes = list()
	intersection = set(gentoo_versions) & set(overlay_versions)
	if not intersection:
		return list()
	category, package = category_package.split('/')
	for version in intersection:
		ebuild_name = '%s-%s.ebuild' % (package, version)
		command = """bash -c 'FILTER() { sed -e "/# $Header/d" -e "/# Copyright/d" -e "/KEYWORDS=/d" "$@"; }; """ \
					"""cmp --quiet <(FILTER /usr/portage/%s/%s) <(FILTER %s/%s/%s) ; """ \
					"""exit $?'""" \
				% (category_package, ebuild_name, overlay_path, category_package, ebuild_name)
		ret = os.system(command)
		if not ret:
			continue
		# print("meld '/usr/portage/%s/%s' '%s/%s/%s'" % (category_package, ebuild_name, conf.overlay_path, category_package, ebuild_name))
		ebuild_changes.append(version)
	return ebuild_changes


def sorted_versions(versions):
	return sorted(versions, cmp=portage.vercmp)


def dump_tree(tree, title):
	print('===============================================================')
	print(title)
	print('===============================================================')
	for category, package_versions in sorted(tree.items()):
		print('%s/' % category)
		for package, versions in sorted(package_versions.items()):
			print('  %s :: %s' % (package, ', '.join(sorted_versions(versions))))
	print()


def parse_command_line(args):
	parser = argparse.ArgumentParser(description='Simple tool for static analysis of Gentoo overlays')
	parser.add_argument(
		'--version',
		action='version', version='%(prog)s ' + VERSION_STR)
	parser.add_argument('overlay_path',
		metavar='PATH', action='store',
		help='Path to Gentoo overlay')
	conf = parser.parse_args(args=args[1:])

	return conf


def main(args):
	conf = parse_command_line(args)

	def sanitize_overlay_path(path):
		path = path.rstrip('/')
		if path == '':
			return '/'
		return path

	conf.overlay_path = sanitize_overlay_path(conf.overlay_path)

	missed_revision_bumps_tree = dict()
	missed_version_bumps_tree = dict()
	ebuild_changes_tree = dict()

	for root, dirs, files in os.walk(conf.overlay_path):
		if '.svn' in dirs:
			dirs[:] = [d for d in dirs if d != '.svn']


		for d in dirs:
			full_path_overlay = os.path.join(root, d)
			category_package = full_path_overlay[len(conf.overlay_path + '/'):]
			if len(category_package.split('/')) != 2:
				continue
			full_path_gentoo = os.path.join('/usr/portage/', category_package)
			found = os.path.exists(full_path_gentoo)
			if not found:
				continue

			overlay_versions = find_atoms(conf.overlay_path, category_package)
			gentoo_versions = find_atoms('/usr/portage/', category_package)
			(missed_revision_bumps, missed_version_bumps) = find_missed_bumps(gentoo_versions, overlay_versions)
			ebuild_changes = find_ebuild_changes(category_package, conf.overlay_path, gentoo_versions, overlay_versions)
			category, package = category_package.split('/')
			if missed_revision_bumps:
				missed_revision_bumps_tree.setdefault(category, dict())[package] = highest_revision_only(missed_revision_bumps)
			if missed_version_bumps:
				missed_version_bumps_tree.setdefault(category, dict())[package] = highest_revision_only(missed_version_bumps)
			if ebuild_changes:
				ebuild_changes_tree.setdefault(category, dict())[package] = ebuild_changes

	dump_tree(missed_version_bumps_tree, 'Version bumps missing from Gentoo main tree')
	dump_tree(missed_revision_bumps_tree, 'Revision bumps missing from Gentoo main tree')
	dump_tree(ebuild_changes_tree, 'Ebuils that differ at same revision')
	return 0


def go():
	ret = main(sys.argv)
	sys.exit(ret)
