#!/usr/bin/python

import argparse
import json
import shutil
import os

def copy_file(src, dest, backup):
	success = True
	if not backup is None:
		(backup_folder, backup_file) = os.path.split(backup)
		print("Creating backup file for " + dest + " at " + backup)
		try:
			if not os.path.exists(backup_folder):
				os.makedirs(backup_folder)
			shutil.copyfile(dest, backup)
		except Exception as e:
			print("Backup failed: " + str(e))
			success = False
			
	if success:
		(dest_folder, dest_file) = os.path.split(dest)
		print("Copy file " + src + " to " + dest)
		try:	
			if not os.path.exists(dest_folder):
				os.makedirs(dest_folder)
			shutil.copyfile(src, dest)
		except IOError as e:
			print("Copy failed: " + str(e))

def copy_dir(src, dest, backup):
	success = True

	if not backup is None:
		try:
			print("Creating backup file for " + dest + " at " + backup)
			rmtree(backup, ignore_errors=True)
			shutil.copytree(dest, backup)
		except IOError as e:
			print("Backup failed: " + str(e))
			success = False

	if success:
		try:
			print("Copy directory " + src + " to " + dest)
			shutil.copytree(src, dest)
		except IOError as e:
			print("Copy failed: " + str(e))

def push(args):
	defs = json.load(open(os.path.expanduser(args.file)))
	for definition in defs:
		if definition["group"] in args.categories:
			print("Pushing " + definition["name"]);
			src = os.path.expanduser(os.path.join(args.source, definition["source"]))
			dest = os.path.expanduser(os.path.join(args.target, definition["target"]))
			backup = os.path.expanduser(os.path.join(args.backup, definition["target"]))
			if definition["type"].lower() == "f":
				# Copy a file
				if args.unsafe:
					if not args.wimp:
						copy_file(src, dest, None)
					else:
						print("Would copy file. Src:" + src + " Dest:" + dest);
				else:
					if not args.wimp:
						copy_file(src, dest, backup)
					else:
						print("Would copy file. Src:" + src + " Dest:" + dest + " Backup:" + backup);
			elif definition["type"].lower() == "d":
				# Copy a directory
				if args.verbose:
					print(definition["name"] + ": Pushing directory from " + src + " to " + dest)
				if args.unsafe:
					if not args.wimp:
						copy_dir(src, dest, None)
					else:
						print("Would copy file. Src:" + src + " Dest:" + dest);
				else:
					if not args.wimp:
						copy_dir(src, dest, backup)
					else:
						print("Would copy dir. Src:" + src + " Dest:" + dest + " Backup:" + backup);
			else:
				print(definition["name"] + ": Unknown type \""+definition["type"]+"\"")

def pull(args):
	defs = json.load(open(os.path.expanduser(args.file)))
	for definition in defs:
		if definition["group"] in args.categories:
			print("Pulling " + definition["name"]);
			src = os.path.expanduser(os.path.join(args.target, definition["target"]))
			dest = os.path.expanduser(os.path.join(args.source, definition["source"]))
			if definition["type"].lower() == "f":
				# Copy a file
				if not args.wimp:
					copy_file(src, dest, None)
				else:
					print("Would copy file. Src:" + src + " Dest:" + dest);
			elif definition["type"].lower() == "d":
				# Copy a directory
				if not args.wimp:
					copy_dir(src, dest, None)
				else:
					print("Would copy directory. Src:" + src + " Dest:" + dest);
			else:
				print(definition["name"] + ": Unknown type \""+definition["type"]+"\"")

def revert(args):
	defs = json.load(open(os.path.expanduser(args.file)))
	for definition in defs:
		if definition["group"] in args.categories:
			src = os.path.expanduser(os.path.join(args.backup, definition["target"]))
			dest = os.path.expanduser(os.path.join(args.target, definition["target"]))
			if definition["type"].lower() == "f":
				# Copy a file
				if not args.wimp:
					copy_file(src, dest, None)
				else:
					print("Would copy file. Src:" + src + " Dest:" + dest);
			elif definition["type"].lower() == "d":
				# Copy a directory
				if not args.wimp:
					copy_dir(src, dest, None)
				else:
					print("Would copy directory. Src:" + src + " Dest:" + dest);
			else:
				print(definition["name"] + ": Unknown type \""+definition["type"]+"\"")

def main():

	default_defs = "~/env/env.def"
	default_source = "~/env/"
	default_target = "~/"
	default_backup = "~/.backup/env/"

	parser = argparse.ArgumentParser()
	parser.add_argument("-v", "--verbose", action="store_true", help="Increase Verbosity")
	parser.add_argument("-f", "--file", default=default_defs, help="Definition File to use")
	parser.add_argument("-s", "--source", default=default_source, help="Override source root")
	parser.add_argument("-t", "--target", default=default_target, help="Override target root")
	parser.add_argument("-w", "--wimp", action="store_true", help="Don't actually make any changes (implies -v)")
	subparsers = parser.add_subparsers()
	
	parser_push = subparsers.add_parser("push", help="Push configs into environment")
	parser_push.add_argument("-u", "--unsafe", action="store_true", help="No backups Created")
	parser_push.add_argument("-a", "--All", action="store_true", help="Cleanup Backups")
	parser_push.add_argument("-b", "--backup", default=default_backup, help="Override backup root")
	parser_push.add_argument("categories", nargs=argparse.REMAINDER)
	parser_push.set_defaults(func=push)

	parser_pull = subparsers.add_parser("pull", help="Pull configs from environment")
	parser_pull.add_argument("-a", "--All", action="store_true", help="Cleanup Backups")
	parser_pull.add_argument("categories", nargs=argparse.REMAINDER)
	parser_pull.set_defaults(func=pull)

	parser_revert = subparsers.add_parser("revert", help="Revert configs from backups")
	parser_revert.add_argument("-c", "--cleanup", action="store_true", help="Cleanup Backups")
	parser_revert.add_argument("-a", "--All", action="store_true", help="Cleanup Backups")
	parser_revert.add_argument("-b", "--backup", default=default_backup, help="Override backup root")
	parser_revert.add_argument("categories", nargs=argparse.REMAINDER)
	parser_revert.set_defaults(func=revert)

	args = parser.parse_args()	

	if args.wimp:
		args.verbose = True

	args.func(args)	

if __name__ == "__main__":
	main();
