#!/usr/bin/python3

# Copyright 2013: Emmanouil Kiagias <e.kiagias@gmail.com>
# Converted via 2to3 by Andreas Tille <tille@debian.org>
# License: GPL

"""
no  documentation for the moment
"""

import os
import re
import sys
import json
import pprint
import logging
import argparse
import subprocess
from debian import deb822

# I shamelessly copied class Deb822List into a file blends_helper.py to test using it here
# That's so hackish that I do not even commit this - needs further discussion
from blends import Deb822List
from debian.deb822 import Deb822

#with this we distinguish the start of automatic entry in the changelog so we
#can replace the entry if needed
START_FLAG = "* start of automatic changelog entry *"

def clean_up_packages(packages):
	logger = logging.getLogger(__name__)
	# Hack: Debian Edu tasks files are using '\' at EOL which is broken
	#       in RFC 822 files, but blend-gen-control from blends-dev relies
	#       on this.  So remove this stuff here for the Moment
	pkgs = re.sub('\\\\\n\s+', '', packages)

	# Remove versions from versioned depends
	pkgs = re.sub(' *\([ ><=\.0-9]+\) *', '', pkgs)

	# temporary strip spaces from alternatives ('|') to enable erroneous space handling as it was done before
	pkgs = re.sub('\s*\|\s*', '|', pkgs)

	# turn alternatives ('|') into real depends for this purpose
	# because we are finally interested in all alternatives
	pkgslist = pkgs.split(',')
	# Collect all dependencies in one line first,
	# create an object for each later
	pkgs_in_one_line = []
	for depl in pkgslist:
		dl = depl.strip()
		if dl != '': # avoid confusion when ',' is at end of line
			if re.search('\s', dl):
				#logger.error("Blend %s task %s: Syntax error '%s'" % (blend, task, dl))
				# trying to fix the syntax error after issuing error message
				dlspaces = re.sub('\s+', ',', dl).split(',')
				for dls in dlspaces:
					pkgs_in_one_line.append(dls.strip())
					#logger.info("Blend %s task %s: Found '%s' package inside broken syntax string - please fix task file anyway" % (blend, task, dls.strip()))
			else:
				# in case we have to deal with a set of alternatives
				if re.search('\|', dl):
					#for da in dl.split('|'):
					#  deps_in_one_line.append(da)
					dl = re.sub('\|', ' | ', dl)
				pkgs_in_one_line.append(dl)
				# self.inject_package_alternatives(blend, task, strength, dl)

	return pkgs_in_one_line

def load_task(path_to_task):
	"""
	parses a task file and return a dictionary containing all its package headers elements
	(depends, suggests etc)
	"""
	ftask = open(path_to_task, 'r')
	task = os.path.basename(path_to_task)
	taskinfo = {}

	for header in ["depends", "suggests", "recommends", "ignore", "avoid"]:
		taskinfo[header] = []

	for paragraph in deb822.Sources.iter_paragraphs(ftask, shared_storage=False):
		if "depends" in paragraph:
			taskinfo["depends"] += clean_up_packages(paragraph["depends"])

		if "suggests" in paragraph:
			taskinfo["suggests"] += clean_up_packages(paragraph["suggests"])

		if "recommends" in paragraph:
			taskinfo["recommends"] += clean_up_packages(paragraph["recommends"])

		if "ignore" in paragraph:
			taskinfo["ignore"] += clean_up_packages(paragraph["ignore"])

		if "avoid" in paragraph:
			taskinfo["avoid"] += clean_up_packages(paragraph["avoid"])

	return task, taskinfo

def compare_tasks(tasks, tasks_compare, taskprefix):
	"""
	This function will dump in stdout the package differences between
	the given tasks1 and tasks2
	"""

	first_print = True

	for task in sorted(tasks):
		if not task in tasks_compare:
			continue
		
		task_first = True 
		first_add = True
		for header in ["depends", "recommends", "suggests", "ignore", "avoid"]:
			added =  set(tasks[task][header]) - set(tasks_compare[task][header])
			if added:
				if first_print:
					print(START_FLAG, "\n")
					print("* Changes in metapackage dependencies")
					first_print = False
				if task_first:
					print(" -{0}-{1}".format(taskprefix,task))
					task_first = False
				if first_add:
					print("  added:")
					first_add = False
				print("    {0}: ".format(header.capitalize()), end=' ')
				print(", ".join(added))
		
		first_remove = True
		for header in ["depends", "recommends", "suggests", "ignore", "avoid"]:
			removed =  set(tasks_compare[task][header]) - set(tasks[task][header])
			if removed:
				if first_print:
					print(START_FLAG, "\n")
					print("* Changes in metapackage dependencies")
					first_print = False
				if task_first:
					print(" -{0}-{1}".format(taskprefix,task))
					task_first = False
				if first_remove:
					print("  removed:")
					first_remove = False
				print("    {0}: ".format(header.capitalize()), end=' ')
				print(", ".join(removed))

	removed_tasks =  set(tasks_compare.keys()) - set(tasks.keys())
	added_tasks =  set(tasks.keys()) - set(tasks_compare.keys())
	if added_tasks:
		if first_print:
			print(START_FLAG, "\n")
			print("* Changes in metapackage dependencies")
			first_print = False
		print("* New metapackages:")
		for newtask in added_tasks:
			print(" -{0}-{1}".format(taskprefix, newtask))

	if removed_tasks:
		if first_print:
			print(START_FLAG, "\n")
			print("* Changes in metapackage dependencies")
			first_print = False
		print("* Removed metapackages:")
		for removedtask in removed_tasks:
			print(" - {0}-{1}".format(taskprefix, removedtask))

def load_tasks(tasks_path):
	tasks = {}

	for taskpath in tasks_path:
		taskname, taskinfo = load_task(taskpath)
		tasks[taskname] = taskinfo

	return tasks

if __name__ == "__main__":
	blend_dev_dir = "/usr/share/blends-dev/"
	default_json = "tasks.json"

	##TODO add proper epilog giving example usage
	parser = argparse.ArgumentParser(epilog="")
	
	parser.add_argument("-t", "--tasks", dest="tasks", type=str,
	                    help="Path to task files", default=".")
	parser.add_argument("-s", "--status-dump", dest="statusdump", action="store_true",
						help="Dump dependencies status into a json file")
	parser.add_argument("-o", "--output", dest="output", type=str, default=default_json,
						help="Output file where to store the dependencies json file(when -s/--status-dump is provided)")
	parser.add_argument("-c", "--compare", dest="compare", type=str,
						help="Provide two comma separated(without spaces)  paths to json files to be compared")
	parser.add_argument("-d", "--debug", dest="debug", action="store_true", default=False,
	                    help="Print debug information")
	#parse the command line arguments
	args = parser.parse_args()

	if args.debug:
	    logging.basicConfig(level=logging.DEBUG)
	else:
	    logging.basicConfig()
	logger = logging.getLogger(__name__)

	#load the taskprefix
	with open(os.path.join('debian', 'control.stub'), encoding="UTF-8") as fp:
	    control_stub = Deb822List(Deb822.iter_paragraphs(fp))
	taskprefix = control_stub[0]['Source'].split('-', 1)[-1]

	if not args.statusdump and not args.compare:
		logger.error("At least -s/--statusdump or -c/--compare argument must be provided")
		sys.exit(-1)

	path_to_tasks = os.path.join(args.tasks, "tasks")
	if not os.path.isdir(path_to_tasks):
		logger.error("tasks directory could not be found in given path. aborting...")
		sys.exit(-1)

	logger.debug("Reading task files from directory {0}".format(path_to_tasks))
	tasks = [ os.path.join(path_to_tasks, fold) for fold in os.listdir(path_to_tasks) if not fold.startswith('.') ]
	giventasks = load_tasks(tasks)

	if args.statusdump:
		logger.debug("Status dump was selected")

		with open(args.output, "w") as fout:
			logger.debug("Dumping json dependencies file into {0}".format(args.output))
			json.dump(giventasks, fout)

		sys.exit(0)

	if args.compare:
		if not ',' in args.compare:
			logger.error("For --compare two comma separated paths to json files should be provided.")
			sys.exit(-1)

		latest, previous = [ x.strip() for x in args.compare.split(',') ]

		if not os.path.isfile(previous) or not os.path.isfile(latest):
			logger.error("Please provide existing json files to be compared.")
			sys.exit(-1)

		logger.debug("Comparing json files:")
		logger.debug("{0} with {1}".format(latest, previous))

		latest_tasks = json.load(open(latest))
		previous_tasks = json.load(open(previous))

		logger.debug("Comparing releases...")
		compare_tasks(latest_tasks, previous_tasks, taskprefix)

