#!/usr/bin/env python2.7

# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

from __future__ import unicode_literals
import os
import subprocess
import frappe

site_arg_optional = ['serve', 'build', 'watch', 'celery', 'resize_images']

def get_site(parsed_args):
	if not parsed_args.get("site") and os.path.exists(os.path.join(parsed_args["sites_path"], "currentsite.txt")):
		with open(os.path.join(parsed_args["sites_path"], "currentsite.txt"), "r") as sitefile:
			parsed_args["site"] = sitefile.read().strip()
			return  parsed_args["site"]
	return parsed_args.get("site")

def main():
	parsed_args = frappe._dict(vars(setup_parser()))
	fn = get_function(parsed_args)
	if parsed_args.get("sites_path"):
		parsed_args["sites_path"] = parsed_args["sites_path"][0]
	else:
		parsed_args["sites_path"] = os.environ.get("SITES_PATH", ".")
	sites_path = parsed_args.get("sites_path")

	if not parsed_args.get("make_app"):

		if parsed_args.get("site")=="all":
			for site in get_sites(parsed_args["sites_path"]):
				print "\nRunning", fn, "for", site
				print "-"*50
				args = parsed_args.copy()
				args["site"] = site
				frappe.init(site, sites_path=sites_path)
				return run(fn, args)
		else:
			site = get_site(parsed_args)
			if fn not in site_arg_optional and not site:
				print 'site argument required'
				return 1
			elif site:
				frappe.init(site, sites_path=sites_path)
			else:
				# site argument optional
				frappe.init("", sites_path=sites_path)
			return run(fn, parsed_args)
	else:
		return run(fn, parsed_args)

def cmd(fn):
	def new_fn(*args, **kwargs):
		import inspect
		fnargs, varargs, varkw, defaults = inspect.getargspec(fn)
		new_kwargs = {}
		for i, a in enumerate(fnargs):
			# should not pass an argument more than once
			if i >= len(args) and a in kwargs:
				new_kwargs[a] = kwargs.get(a)

		return fn(*args, **new_kwargs)

	return new_fn


def run(fn, args):
	import cProfile, pstats, StringIO

	use_profiler = args.get("profile") and fn!="serve"
	if use_profiler:
		pr = cProfile.Profile()
		pr.enable()

	if isinstance(args.get(fn), (list, tuple)):
		out = globals().get(fn)(*args.get(fn), **args)
	else:
		out = globals().get(fn)(**args)

	if use_profiler:
		pr.disable()
		s = StringIO.StringIO()
		ps = pstats.Stats(pr, stream=s).sort_stats('tottime', 'ncalls')
		ps.print_stats()
		print s.getvalue()

	return out

def get_function(args):
	for fn, val in args.items():
		if (val or isinstance(val, list)) and globals().get(fn):
			return fn

def get_sites(sites_path=None):
	import os
	if not sites_path:
		sites_path = '.'
	return [site for site in os.listdir(sites_path)
			if os.path.isdir(os.path.join(sites_path, site))
				and not site in ('assets',)]

def setup_parser():
	import argparse
	parser = argparse.ArgumentParser(description="Run frappe utility functions")

	setup_install(parser)
	setup_utilities(parser)
	setup_translation(parser)
	setup_test(parser)

	parser.add_argument("site", nargs="?")

	# common
	parser.add_argument("-f", "--force", default=False, action="store_true",
		help="Force execution where applicable (look for [-f] in help)")
	parser.add_argument("--all", default=False, action="store_true",
		help="Get all (where applicable)")
	parser.add_argument("--verbose", default=False, action="store_true",
		help="Show verbose output (where applicable)")
	parser.add_argument("--quiet", default=False, action="store_true",
		help="Do not show verbose output (where applicable)")

	return parser.parse_args()

def setup_install(parser):
	parser.add_argument("--make_app", metavar="DEST", nargs=1,
		help="Make a new application with boilerplate")
	parser.add_argument("--install", metavar="DB-NAME", nargs=1,
		help="Install a new db")
	parser.add_argument("--sites_path", metavar="SITES_PATH", nargs=1,
		help="path to directory with sites")
	parser.add_argument("--install_app", metavar="APP-NAME", nargs=1,
		help="Install a new app")
	parser.add_argument("--add_to_installed_apps", metavar="APP-NAME", nargs="*",
		help="Add these app(s) to Installed Apps")
	parser.add_argument("--root-password", nargs=1,
		help="Root password for new app")
	parser.add_argument("--reinstall", default=False, action="store_true",
		help="Install a fresh app in db_name specified in conf.py")
	parser.add_argument("--restore", metavar=("DB-NAME", "SQL-FILE"), nargs=2,
		help="Restore from an sql file")
	parser.add_argument("--add_system_manager", nargs="+",
		metavar=("EMAIL", "[FIRST-NAME] [LAST-NAME]"), help="Add a user with all roles")

def setup_test(parser):
	parser.add_argument("--run_tests", default=False, action="store_true",
		help="Run tests options [-d doctype], [-m module]")
	parser.add_argument("--app", metavar="APP-NAME", nargs=1,
		help="Run command for specified app")
	parser.add_argument("-d", "--doctype", metavar="DOCTYPE", nargs=1,
		help="Run command for specified doctype")
	parser.add_argument("-m", "--module", metavar="MODULE", nargs=1,
		help="Run command for specified module")
	parser.add_argument("--tests", metavar="TEST FUNCTION", nargs="*",
		help="Run one or more specific test functions")
	parser.add_argument("--serve_test", action="store_true", help="Run development server for testing")
	parser.add_argument("--driver", nargs="?", help="Run selenium using given driver")


def setup_utilities(parser):
	# serving
	parser.add_argument("--port", type=int, help="port for development server")
	parser.add_argument("--use", action="store_true", help="Set current site for development.")

	# update
	parser.add_argument("-u", "--update", nargs="*", metavar=("REMOTE", "BRANCH"),
		help="Perform git pull, run patches, sync schema and rebuild files/translations")
	parser.add_argument("--reload_gunicorn", default=False, action="store_true", help="reload gunicorn on update")
	parser.add_argument("--patch", nargs=1, metavar="PATCH-MODULE",
		help="Run a particular patch [-f]")
	parser.add_argument("-l", "--latest", default=False, action="store_true",
		help="Run patches, sync schema and rebuild files/translations")
	parser.add_argument("--sync_all", default=False, action="store_true",
		help="Reload all doctypes, pages, etc. using txt files [-f]")
	parser.add_argument("--update_all_sites", nargs="*", metavar=("REMOTE", "BRANCH"),
		help="Perform git pull, run patches, sync schema and rebuild files/translations")

	parser.add_argument("--reload_doc", nargs=3,
		metavar=('"MODULE"', '"DOCTYPE"', '"DOCNAME"'))

	# build
	parser.add_argument("-b", "--build", default=False, action="store_true",
		help="Minify + concatenate JS and CSS files, build translations")
	parser.add_argument("--make_copy", default=False, action="store_true",
		help="Make copy of assets instead of symlinks")
	parser.add_argument("-w", "--watch", default=False, action="store_true",
		help="Watch and concatenate JS and CSS files as and when they change")

	# misc
	parser.add_argument("--backup", default=False, action="store_true",
		help="Take backup of database in backup folder [--with_files]")
	parser.add_argument("--move", default=False, action="store_true",
		help="Move site to different directory defined by --dest_dir")
	parser.add_argument("--dest_dir", nargs=1, metavar="DEST-DIR",
		help="Move site to different directory")
	parser.add_argument("--with_files", default=False, action="store_true",
		help="Also take backup of files")
	parser.add_argument("--domain", nargs="*",
		help="Get or set domain in Website Settings")
	parser.add_argument("--make_conf", nargs="*", metavar=("DB-NAME", "DB-PASSWORD"),
		help="Create new conf.py file")
	parser.add_argument("--make_custom_server_script", nargs=1, metavar="DOCTYPE",
		help="Create new conf.py file")
	parser.add_argument("--set_admin_password", metavar='ADMIN-PASSWORD', nargs=1,
		help="Set administrator password")
	parser.add_argument("--request", metavar='URL-ARGS', nargs=1, help="Run request as admin")
	parser.add_argument("--mysql", action="store_true", help="get mysql shell for a site")
	parser.add_argument("--serve", action="store_true", help="Run development server")
	parser.add_argument("--profile", action="store_true", help="enable profiling in development server")
	parser.add_argument("--smtp", action="store_true", help="Run smtp debug server",
		dest="smtp_debug_server")
	parser.add_argument("--python", action="store_true", help="get python shell for a site")
	parser.add_argument("--flush_memcache", action="store_true", help="flush memcached")
	parser.add_argument("--ipython", action="store_true", help="get ipython shell for a site")
	parser.add_argument("--execute", help="execute a function", nargs=1, metavar="FUNCTION")
	parser.add_argument("--get_site_status", action="store_true", help="Get site details")
	parser.add_argument("--update_site_config", nargs=1,
		metavar="site-CONFIG-JSON",
		help="Update site_config.json for a given site")
	parser.add_argument("--resize_images", nargs=1, metavar="PATH", help="resize images to a max width of 500px")


	# clear
	parser.add_argument("--clear_web", default=False, action="store_true",
		help="Clear website cache")
	parser.add_argument("--build_sitemap", default=False, action="store_true",
		help="Build Website Route")
	parser.add_argument("--sync_statics", default=False, action="store_true",
		help="Sync files from templates/statics to Web Pages")
	parser.add_argument("--clear_cache", default=False, action="store_true",
		help="Clear cache, doctype cache and defaults")
	parser.add_argument("--reset_perms", default=False, action="store_true",
		help="Reset permissions for all doctypes")
	parser.add_argument("--clear_all_sessions", default=False, action="store_true",
		help="Clear sessions of all users (logs them out)")

	# scheduler
	parser.add_argument("--run_scheduler", default=False, action="store_true",
		help="Trigger scheduler")
	parser.add_argument("--celery", nargs="*", help="Run Celery Commands")
	parser.add_argument("--run_scheduler_event", nargs=1,
		metavar="all | daily | weekly | monthly",
		help="Run a scheduler event")

	# replace
	parser.add_argument("--replace", nargs=3,
		metavar=("SEARCH-REGEX", "REPLACE-BY", "FILE-EXTN"),
		help="Multi-file search-replace [-f]")

	# import/export
	parser.add_argument("--export_doc", nargs=2, metavar=('"DOCTYPE"', '"DOCNAME"'))
	parser.add_argument("--export_doclist", nargs=3, metavar=("DOCTYPE", "NAME", "PATH"),
		help="""Export doclist as json to the given path, use '-' as name for Singles.""")
	parser.add_argument("--export_csv", nargs=2, metavar=("DOCTYPE", "PATH"),
		help="""Dump DocType as csv""")
	parser.add_argument("--export_fixtures", default=False, action="store_true",
		help="""Export fixtures""")
	parser.add_argument("--import_doc", nargs=1, metavar="PATH",
		help="""Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported""")

def setup_translation(parser):
	parser.add_argument("--build_message_files", default=False, action="store_true",
		help="Build message files for translation.")
	parser.add_argument("--get_untranslated", nargs=2, metavar=("LANG-CODE", "TARGET-FILE-PATH"),
		help="""Get untranslated strings for lang.""")
	parser.add_argument("--update_translations", nargs=3,
		metavar=("LANG-CODE", "UNTRANSLATED-FILE-PATH", "TRANSLATED-FILE-PATH"),
		help="""Update translated strings.""")

# methods
@cmd
def make_app(destination):
	from frappe.utils.boilerplate import make_boilerplate
	make_boilerplate(destination)

@cmd
def use(sites_path):
	with open(os.path.join(sites_path,  "currentsite.txt"), "w") as sitefile:
		sitefile.write(frappe.local.site)

# install
@cmd
def install(db_name, root_login="root", root_password=None, source_sql=None,
		admin_password = 'admin', force=False, site_config=None, reinstall=False, quiet=False):
	from frappe.installer import install_db, install_app, make_site_dirs
	verbose = not quiet

	install_db(root_login=root_login, root_password=root_password, db_name=db_name, source_sql=source_sql,
		admin_password = admin_password, verbose=verbose, force=force, site_config=site_config, reinstall=reinstall)
	make_site_dirs()
	install_app("frappe", verbose=verbose, set_as_patched=not source_sql)
	if frappe.conf.get("install_apps"):
		for app in frappe.conf.install_apps:
			install_app(app, verbose=verbose, set_as_patched=not source_sql)
	frappe.destroy()

@cmd
def install_app(app_name, quiet=False):
	verbose = not quiet
	from frappe.installer import install_app
	frappe.connect()
	install_app(app_name, verbose=verbose)
	frappe.destroy()

@cmd
def add_to_installed_apps(*apps):
	from frappe.installer import add_to_installed_apps
	frappe.connect()
	all_apps = frappe.get_all_apps(with_frappe=True)
	for each in apps:
		if each in all_apps:
			add_to_installed_apps(each, rebuild_sitemap=False)
	frappe.destroy()

@cmd
def reinstall(quiet=False):
	verbose = not quiet
	try:
		frappe.connect()
		frappe.clear_cache()
	except:
		pass
	finally:
		frappe.db.close()

	install(db_name=frappe.conf.db_name, verbose=verbose, force=True, reinstall=True)

@cmd
def restore(db_name, source_sql, force=False, quiet=False):
	verbose = not quiet
	install(db_name, source_sql=source_sql, verbose=verbose, force=force)

@cmd
def add_system_manager(email, first_name=None, last_name=None):
	import frappe.utils.user
	frappe.connect()
	frappe.utils.user.add_system_manager(email, first_name, last_name)
	frappe.db.commit()
	frappe.destroy()

# utilities

@cmd
def update(remote=None, branch=None, reload_gunicorn=False):
	pull(remote=remote, branch=branch)

	# maybe there are new framework changes, any consequences?
	reload(frappe)
	build()
	latest()
	if reload_gunicorn:
		subprocess.check_output("killall -HUP gunicorn".split())

@cmd
def latest(rebuild_website_config=True, quiet=False):
	import frappe.modules.patch_handler
	import frappe.model.sync
	from frappe.website import rebuild_config
	from frappe.utils.fixtures import sync_fixtures
	import frappe.translate
	from frappe.website import statics

	verbose = not quiet

	frappe.connect()

	try:
		# run patches
		frappe.modules.patch_handler.run_all()
		# sync
		frappe.model.sync.sync_all(verbose=verbose)
		sync_fixtures()

		statics.sync().start()
		# build website config if any changes in templates etc.
		if rebuild_website_config:
			rebuild_config()


		frappe.translate.clear_cache()

	finally:
		frappe.destroy()

@cmd
def sync_all(force=False, quiet=False):
	import frappe.model.sync
	verbose = not quiet
	frappe.connect()
	frappe.model.sync.sync_all(force=force, verbose=verbose)
	frappe.destroy()

@cmd
def patch(patch_module, force=False):
	import frappe.modules.patch_handler
	frappe.connect()
	frappe.modules.patch_handler.run_single(patch_module, force=force)
	frappe.destroy()

@cmd
def update_all_sites(remote=None, branch=None, quiet=False):
	verbose = not quiet
	pull(remote, branch)

	# maybe there are new framework changes, any consequences?
	reload(frappe)

	build()
	for site in get_sites():
		frappe.init(site)
		latest(verbose=verbose)

@cmd
def reload_doc(module, doctype, docname, force=False):
	frappe.connect()
	frappe.reload_doc(module, doctype, docname, force=force)
	frappe.db.commit()
	frappe.destroy()

@cmd
def build(make_copy=False):
	import frappe.build
	import frappe
	frappe.build.bundle(False, make_copy=make_copy)

@cmd
def watch():
	import frappe.build
	frappe.build.watch(True)

@cmd
def backup(with_files=False, backup_path_db=None, backup_path_files=None, quiet=False):
	from frappe.utils.backups import scheduled_backup
	verbose = not quiet
	frappe.connect()
	odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files)
	if verbose:
		from frappe.utils import now
		print "database backup taken -", odb.backup_path_db, "- on", now()
		if with_files:
			print "files backup taken -", odb.backup_path_files, "- on", now()
	frappe.destroy()

@cmd
def move(dest_dir=None, site=None):
	import os
	if not dest_dir:
		raise Exception, "--dest_dir is required for --move"
	if not os.path.isdir(dest_dir):
		raise Exception, "destination is not a directory or does not exist"

	old_path = frappe.utils.get_site()
	new_path = os.path.join(dest_dir, site)

	# check if site dump of same name already exists
	site_dump_exists = True
	count = 0
	while site_dump_exists:
		final_new_path = new_path + (count and str(count) or "")
		site_dump_exists = os.path.exists(final_new_path)
		count = int(count or 0) + 1

	os.rename(old_path, final_new_path)
	frappe.destroy()
	return os.path.basename(final_new_path)

@cmd
def domain(host_url=None):
	frappe.connect()
	if host_url:
		frappe.db.set_value("Website Settings", None, "subdomain", host_url)
		frappe.db.commit()
	else:
		print frappe.db.get_value("Website Settings", None, "subdomain")
	frappe.destroy()

@cmd
def make_conf(db_name=None, db_password=None, site_config=None):
	from frappe.install_lib.install import make_conf
	make_conf(db_name=db_name, db_password=db_password, site_config=site_config)

@cmd
def make_custom_server_script(doctype):
	from frappe.core.doctype.custom_script.custom_script import make_custom_server_script_file
	frappe.connect()
	make_custom_server_script_file(doctype)
	frappe.destroy()

# clear
@cmd
def clear_cache():
	import frappe.sessions
	frappe.connect()
	frappe.clear_cache()
	frappe.destroy()

@cmd
def clear_web():
	import frappe.website.render
	frappe.connect()
	frappe.website.render.clear_cache()
	frappe.destroy()

@cmd
def clear_all_sessions():
	import frappe.sessions
	frappe.connect()
	frappe.sessions.clear_all_sessions()
	frappe.db.commit()
	frappe.destroy()

@cmd
def build_sitemap():
	from frappe.website import rebuild_config
	frappe.connect()
	rebuild_config()
	frappe.destroy()

@cmd
def sync_statics():
	from frappe.website import statics
	frappe.connect()
	statics.sync_statics()
	frappe.db.commit()
	frappe.destroy()

@cmd
def reset_perms():
	frappe.connect()
	for d in frappe.db.sql_list("""select name from `tabDocType`
		where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""):
			frappe.clear_cache(doctype=d)
			frappe.reset_perms(d)
	frappe.destroy()

@cmd
def execute(method):
	frappe.connect()
	ret = frappe.get_attr(method)()
	frappe.db.commit()
	frappe.destroy()
	if ret:
		print ret

@cmd
def celery(arg):
	import frappe
	import commands, os
	python = commands.getoutput('which python')
	os.execv(python, [python, "-m", "frappe.celery_app"] + arg.split())
	frappe.destroy()

@cmd
def run_scheduler_event(event, force=False):
	import frappe.utils.scheduler
	frappe.connect()
	frappe.utils.scheduler.trigger(frappe.local.site, event, now=force)
	frappe.destroy()

# replace
@cmd
def replace(search_regex, replacement, extn, force=False):
	print search_regex, replacement, extn
	replace_code('.', search_regex, replacement, extn, force=force)

# import/export
@cmd
def export_doc(doctype, docname):
	import frappe.modules
	frappe.connect()
	frappe.modules.export_doc(doctype, docname)
	frappe.destroy()

@cmd
def export_doclist(doctype, name, path):
	from frappe.core.page.data_import_tool import data_import_tool
	frappe.connect()
	data_import_tool.export_json(doctype, name, path)
	frappe.destroy()

@cmd
def export_csv(doctype, path):
	from frappe.core.page.data_import_tool import data_import_tool
	frappe.connect()
	data_import_tool.export_csv(doctype, path)
	frappe.destroy()

@cmd
def export_fixtures():
	from frappe.utils.fixtures import export_fixtures
	frappe.connect()
	export_fixtures()
	frappe.destroy()

@cmd
def import_doc(path, force=False):
	from frappe.core.page.data_import_tool import data_import_tool
	frappe.connect()
	data_import_tool.import_doc(path, overwrite=force)
	frappe.destroy()

# translation
@cmd
def build_message_files():
	import frappe.translate
	frappe.connect()
	frappe.translate.rebuild_all_translation_files()
	frappe.destroy()

@cmd
def get_untranslated(lang, untranslated_file, all=None):
	import frappe.translate
	frappe.connect()
	frappe.translate.get_untranslated(lang, untranslated_file, get_all=all)
	frappe.destroy()

@cmd
def update_translations(lang, untranslated_file, translated_file):
	import frappe.translate
	frappe.connect()
	frappe.translate.update_translations(lang, untranslated_file, translated_file)
	frappe.destroy()

# git
@cmd
def git(param):
	if isinstance(param, (list, tuple)):
		param = " ".join(param)
	import os
	os.system("""cd lib && git %s""" % param)
	os.system("""cd app && git %s""" % param)

def get_remote_and_branch(remote=None, branch=None):
	if not (remote and branch):
		if not frappe.conf.branch:
			raise Exception("Please specify remote and branch")

		remote = remote or "origin"
		branch = branch or frappe.conf.branch
		frappe.destroy()

	return remote, branch

@cmd
def pull(remote=None, branch=None):
	remote, branch = get_remote_and_branch(remote, branch)
	git(("pull", remote, branch))

@cmd
def push(remote=None, branch=None):
	remote, branch = get_remote_and_branch(remote, branch)
	git(("push", remote, branch))

@cmd
def status():
	git("status")

@cmd
def commit(message):
	git("""commit -a -m "%s" """ % message.replace('"', '\"'))

@cmd
def checkout(branch):
	git(("checkout", branch))

@cmd
def set_admin_password(admin_password):
	import frappe
	frappe.connect()
	frappe.db.sql("""update __Auth set `password`=password(%s)
		where user='Administrator'""", (admin_password,))
	frappe.db.commit()
	frappe.destroy()

@cmd
def mysql():
	import frappe
	import commands, os
	msq = commands.getoutput('which mysql')
	os.execv(msq, [msq, '-u', frappe.conf.db_name, '-p'+frappe.conf.db_password, frappe.conf.db_name, '-h', frappe.conf.db_host or "localhost", "-A"])
	frappe.destroy()

@cmd
def python(site):
	import frappe
	import commands, os
	python = commands.getoutput('which python')
	if site:
		os.environ["site"] = site
	os.environ["PYTHONSTARTUP"] = os.path.join(os.path.dirname(frappe.__file__), "pythonrc.py")
	os.execv(python, [python])
	frappe.destroy()

@cmd
def ipython(site):
	import frappe
	frappe.connect(site=site)
	import IPython
	IPython.embed()

@cmd
def smtp_debug_server():
	import commands, os
	python = commands.getoutput('which python')
	os.execv(python, [python, '-m', "smtpd", "-n", "-c", "DebuggingServer", "localhost:25"])

@cmd
def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driver=None):
	import frappe.test_runner
	from frappe.utils import sel

	sel.start(verbose, driver)

	ret = 1
	try:
		ret = frappe.test_runner.main(app and app[0], module and module[0], doctype and doctype[0], verbose,
			tests=tests)
		if len(ret.failures) == 0 and len(ret.errors) == 0:
			ret = 0
	finally:
		sel.close()

	return ret

@cmd
def serve(port=None, profile=False, sites_path='.', site=None):
	if not port: port = 8000

	import frappe.app
	frappe.app.serve(port=port, profile=profile, site=frappe.local.site, sites_path=sites_path)

@cmd
def serve_test(port=None, profile=False, sites_path='.', site=None):
	from frappe.utils import sel
	if not port: port = sel.port
	serve(port)

@cmd
def request(args):
	import frappe.handler
	import frappe.api
	frappe.connect()
	if "?" in args:
		frappe.local.form_dict = frappe._dict([a.split("=") for a in args.split("?")[-1].split("&")])
	else:
		frappe.local.form_dict = frappe._dict()

	if args.startswith("/api/method"):
		frappe.local.form_dict.cmd = args.split("?")[0].split("/")[-1]

	frappe.handler.execute_cmd(frappe.form_dict.cmd)

	print frappe.response
	frappe.destroy()

@cmd
def resize_images(path):
	import frappe.utils.image
	frappe.utils.image.resize_images(path)

@cmd
def flush_memcache():
	frappe.cache().flush_all()


def replace_code(start, txt1, txt2, extn, search=None, force=False):
	"""replace all txt1 by txt2 in files with extension (extn)"""
	import frappe.utils
	import os, re
	esc = frappe.utils.make_esc('[]')
	if not search: search = esc(txt1)
	for wt in os.walk(start, followlinks=1):
		for fn in wt[2]:
			if fn.split('.')[-1]==extn:
				fpath = os.path.join(wt[0], fn)
				with open(fpath, 'r') as f:
					content = f.read()

				if re.search(search, content):
					res = search_replace_with_prompt(fpath, txt1, txt2, force)
					if res == 'skip':
						return 'skip'


def search_replace_with_prompt(fpath, txt1, txt2, force=False):
	""" Search and replace all txt1 by txt2 in the file with confirmation"""
	from termcolor import colored
	with open(fpath, 'r') as f:
		content = f.readlines()

	tmp = []
	for c in content:
		if c.find(txt1) != -1:
			print fpath
			print  colored(txt1, 'red').join(c[:-1].split(txt1))
			a = ''
			if force:
				c = c.replace(txt1, txt2)
			else:
				while a.lower() not in ['y', 'n', 'skip']:
					a = raw_input('Do you want to Change [y/n/skip]?')
				if a.lower() == 'y':
					c = c.replace(txt1, txt2)
				elif a.lower() == 'skip':
					return 'skip'
		tmp.append(c)

	with open(fpath, 'w') as f:
		f.write(''.join(tmp))
	print colored('Updated', 'green')

@cmd
def get_site_status(verbose=False):
	import frappe
	import frappe.utils
	from frappe.utils.user import get_system_managers
	from frappe.core.doctype.user.user import get_total_users, get_active_users, \
		get_website_users, get_active_website_users

	import json
	frappe.connect()
	ret = {
		'last_backup_on': frappe.local.conf.last_backup_on,
		'active_users': get_active_users(),
		'total_users': get_total_users(),
		'active_website_users': get_active_website_users(),
		'website_users': get_website_users(),
		'system_managers': "\n".join(get_system_managers()),
		'default_company': frappe.db.get_default("company"),
		'disk_usage': frappe.utils.get_disk_usage(),
		'working_directory': frappe.local.site_path
	}

	# country, timezone, industry
	for key in ["country", "time_zone", "industry"]:
		ret[key] = frappe.db.get_default(key)

	# basic usage/progress analytics
	for doctype in ("Company", "Customer", "Item", "Quotation", "Sales Invoice",
		"Journal Voucher", "Stock Ledger Entry"):
			key = doctype.lower().replace(" ", "_") + "_exists"
			ret[key] = 1 if frappe.db.count(doctype) else 0

	frappe.destroy()

	if verbose:
		print json.dumps(ret, indent=1, sort_keys=True)

	return ret

@cmd
def update_site_config(site_config, verbose=False):
	import json

	if isinstance(site_config, basestring):
		site_config = json.loads(site_config)

	config = frappe.get_site_config()
	config.update(site_config)
	site_config_path = os.path.join(frappe.local.site_path, "site_config.json")

	with open(site_config_path, "w") as f:
		json.dump(config, f, indent=1, sort_keys=True)

	frappe.destroy()

@cmd
def bump(repo, bump_type):

	import json
	assert repo in ['lib', 'app']
	assert bump_type in ['minor', 'major', 'patch']

	def validate(repo_path):
		import git
		repo = git.Repo(repo_path)
		if repo.active_branch != 'master':
			raise Exception, "Current branch not master in {}".format(repo_path)

	def bump_version(version, version_type):
		import semantic_version
		v = semantic_version.Version(version)
		if version_type == 'minor':
			v.minor += 1
		elif version_type == 'major':
			v.major += 1
		elif version_type == 'patch':
			v.patch += 1
		return unicode(v)

	def add_tag(repo_path, version):
		import git
		repo = git.Repo(repo_path)
		repo.index.add(['config.json'])
		repo.index.commit('bumped to version {}'.format(version))
		repo.create_tag('v' + version, repo.head)

	def update_framework_requirement(version):
		with open('app/config.json') as f:
			config = json.load(f)
		config['requires_framework_version'] = '==' + version
		with open('app/config.json', 'w') as f:
			json.dump(config, f, indent=1, sort_keys=True)

	validate('lib/')
	validate('app/')

	if repo == 'app':
		with open('app/config.json') as f:
			config = json.load(f)
		new_version = bump_version(config['app_version'], bump_type)
		config['app_version'] = new_version
		with open('app/config.json', 'w') as f:
			json.dump(config, f, indent=1, sort_keys=True)
		add_tag('app/', new_version)

	elif repo == 'lib':
		with open('lib/config.json') as f:
			config = json.load(f)
		new_version = bump_version(config['framework_version'], bump_type)
		config['framework_version'] = new_version
		with open('lib/config.json', 'w') as f:
			json.dump(config, f, indent=1, sort_keys=True)
		add_tag('lib/', new_version)

		update_framework_requirement(new_version)

		bump('app', bump_type)


if __name__=="__main__":
	out = main()
	if out and out==1:
		exit(1)
