#!/usr/bin/env python

import sys
import argparse

from glob import glob
from datetime import datetime
from shutil import rmtree

from os import path
sys.path.insert(0, path.dirname(path.dirname(path.realpath(__file__))))
from pyppl import logger, utils

parser           = argparse.ArgumentParser(description='A set of CLI tools for pyppl.')
parser.add_argument('-w', '--workdir', help="The path of workdir, default: %(default)s", required=False, default="./workdir")
cmdparsers       = parser.add_subparsers(dest='command')
parser_clean     = cmdparsers.add_parser('clean', help="Clean a workdir")
parser_clean.add_argument ('-f', '--force', help="Force cleaning, don't ask for confirmation.", action='store_true')
parser_clean.add_argument ('-n', '--nthread', help="Number of threads used to remove the directory.", dest='n', type=int, default=1)
parser_clean.set_defaults(force = False)
parser_list      = cmdparsers.add_parser('list', help="List the status of a workdir")
parser_compare   = cmdparsers.add_parser('compare', help="Compare the settings of two processes.")
parser_compare.add_argument ('-p1', '--process1', dest='p1', help="The first process, either full path (with -d) or the basename (could be without 'PyPPL.')", required = False)
parser_compare.add_argument ('-p2', '--process2', dest='p2', help="The second process, either full path (with -d) or the basename (could be without 'PyPPL.')", required = False)
parser_compare.add_argument ('-p', '--pprefix', dest='p', help="The process without suffix, will compare the most 2 recent processes (with -d for full path).", required = False)
args             = parser.parse_args()

def mtime4proc (procdir):
	settingsfile  = path.join(procdir, "proc.settings")
	if not path.exists(settingsfile):
		return 0
	return path.getmtime(settingsfile)

def getGroups (workdir):
	ret = {}
	procs = sorted ([path.basename(x) for x in glob (path.join(workdir, "PyPPL.*"))])
	for proc in procs:
		ppath = path.join (workdir, proc)
		if not path.isdir (ppath):
			continue
		parts = proc.split ('.')
		pname = '.'.join(parts[1:-1])
		if not ret.has_key(pname):
			ret[pname] = []
		ret[pname].append(parts[-1])
	return ret

def ask2Remove (msg, procpath, ask = True):
	def rm ():
		utils.Parallel(args.n).run(rmtree, [(pp, ) for pp in glob(path.join(procpath, '*/'))])
		#utils.parallel(rmtree, [(pp, ) for pp in glob(path.join(procpath, '*/'))], args.n, 'process')
		rmtree(procpath)
		print logger.colors.bold + logger.colors.yellow + "  Removed: " + procpath + logger.colors.end

	if not ask:
		rm ()
		return

	dists = ['', 'Y', 'y', 'N', 'n']
	r     = raw_input(msg + ', clean it? [Y/n] ')
	while r not in dists:
		r     = raw_input(msg + ', clean it? [Y/n] ')
	if r in ['', 'Y', 'y']:
		rm()

fmtTime = lambda x: datetime.fromtimestamp(x).strftime('%Y-%m-%d %H:%M:%S')

if args.command in ['list', 'clean']:
	print ''
	print logger.colors.green  + 'GREEN PROC : Latest processes' + logger.colors.end
	print logger.colors.yellow + 'YELLOW PROC: Older processes' + logger.colors.end
	print logger.colors.red    + 'RED PROC   : Settings file not exists' + logger.colors.end
	print logger.colors.none + '\nWORKDIR: ' + args.workdir + logger.colors.end
	print "-" * (len (args.workdir) + 9)

	groups = getGroups (args.workdir)

	if not groups:
		print logger.colors.red + "No processes found!\n" + logger.colors.end

	maxlen = max([len(g) for g in groups.keys()])
	maxlen = max(maxlen, 30)
	for g in sorted(groups.keys()):
		sufs = groups[g]
		print "\n- PROCESSES: %s" % (g)
		print "  " + "-" * (maxlen + 30)

		for i, suf in enumerate(sorted(sufs, key=lambda x: mtime4proc(path.join (args.workdir, "PyPPL.%s.%s" % (g, x))), reverse=True)):
			procpath = path.join (args.workdir, "PyPPL.%s.%s" % (g, suf))
			color    = logger.colors.green
			ptime    = mtime4proc(procpath)
			if i == 0 and ptime != 0:
				print color + '  ' + g + '.' + suf + ': ' + fmtTime(ptime) + logger.colors.end
			else:
				procpath = path.join (args.workdir, "PyPPL.%s.%s" % (g, suf))
				if ptime == 0:
					color = logger.colors.red
					msg = color + '  ' + g + '.' + suf + ': ' + fmtTime(ptime) + logger.colors.end
					if args.command == 'list':
						print msg
					else:
						ask2Remove(msg, procpath, not args.force)
				else:
					color = logger.colors.yellow
					msg = color + '  ' + g + '.' + suf + ': ' + fmtTime(ptime) + logger.colors.end
					if args.command == 'list':
						print msg
					else:
						ask2Remove(msg, procpath, not args.force)
	print ''

if args.command == 'compare':

	if not args.p1 and not args.p2 and not args.p:
		print "Expect paired processes (-p1 and -p2) OR the process prefix (-p)"
		sys.exit (1)
	if args.p and (args.p1 or args.p2):
		print "Expect paired processes (-p1 and -p2) OR the process prefix (-p)"
		sys.exit (1)
	if (args.p1 and not args.p2) or (args.p2 and not args.p1):
		print "Expect paired processes (both -p1 and -p2)"
		sys.exit (1)

	from difflib import unified_diff

	if args.p:
		if '/' in args.p[:-1]:
			prefix = args.p
			prefix = prefix + '.notag' if path.basename(prefix).count('.') < 2 else prefix
		else:

			prefix = 'PyPPL.' + args.p if not args.p.startswith('PyPPL.') else args.p
			prefix = prefix + '.notag' if prefix.count('.') < 2 else prefix
			prefix = path.join(args.workdir, prefix)

		procs  = glob (prefix + '.*')
		if not procs:
			sfile1 = ''
			sfile2 = ''
		elif len(procs) == 1:
			sfile1 = path.join(procs[0], 'proc.settings')
			sfile2 = ''
		else:
			procs = sorted(procs, key=mtime4proc, reverse=True)[:2]
			sfile1 = path.join(procs[0], 'proc.settings')
			sfile2 = path.join(procs[1], 'proc.settings')
	else:
		if '/' in args.p1[:-1]:
			p1 = args.p1
		else:
			p1 = 'PyPPL.' + args.p1 if not args.p1.startswith('PyPPL.') else args.p1
			p1 = path.join(args.workdir, p1)

		if '/' in args.p2[:-1]:
			p2 = args.p2
		else:
			p2 = 'PyPPL.' + args.p2 if not args.p2.startswith('PyPPL.') else args.p2
			p2 = path.join(args.workdir, p2)
		sfile1    = path.join(p1, 'proc.settings')
		sfile2    = path.join(p2, 'proc.settings')

	print logger.colors.green + "1. " + sfile1 + logger.colors.end
	print logger.colors.red   + "2. " + sfile2 + logger.colors.end
	print '-' * (max(len(sfile1), len(sfile2)) + 3)
	if path.exists(sfile1) and path.exists(sfile2):
		def readSections (sfile):
			ret = {}
			sec = ''
			with open(sfile) as f:
				for line in f:
					if line.startswith('['):
						sec = line.strip()[1:-1]
					elif sec:
						if sec not in ret:
							ret[sec] = []
						ret[sec].append(line)
			return ret

		seclines1 = readSections(sfile1)
		seclines2 = readSections(sfile2)

		for sec in sorted(seclines1.keys()):
			lines1 = seclines1[sec]
			lines2 = seclines2[sec]
			diff   = unified_diff(lines1, lines2, n=0)
			toprnt = ''
			for i, d in enumerate(diff):
				if i==0 and d.strip() == '---':
					pass
				elif i==1 and d.strip() == '+++':
					pass
				elif d.startswith('-'):
					toprnt += logger.colors.green + d + logger.colors.end
				elif d.startswith('+'):
					toprnt += logger.colors.red   + d + logger.colors.end
				elif d.startswith('@'):
					toprnt += logger.colors.yellow + d + logger.colors.end
				else:
					toprnt += d
			if toprnt:
				print "["+ sec +"]"
				print toprnt

	elif not path.exists(sfile1) and not path.exists(sfile2):
		print "None of the proc.settings files exist, nothing to compare.\n"
		sys.exit(0)
	else:
		print "Only one of the processes' proc.settings exists, see the configuration in:"
		print logger.colors.green + (sfile1 if path.exists(sfile1) else sfile2) + logger.colors.end + "\n"
		sys.exit(0)
