#!/usr/bin/env python

import os,sys,argparse,multiprocessing as mp
SLINGSHOT_N=mp.cpu_count()


COMMANDS = [
	dict(name='list', help='List names and descriptions of all corpora', takes_corpus_obj=False, group='Corpus status'),
	dict(name='status', help='Get download/installation status for all corpora', takes_corpus_obj=False, group='Corpus status'),
	dict(name='info', help='Get information about a corpus', takes_corpus_obj=True, group='Corpus status'),

	dict(name='create', help='Create a corpus with LLP', takes_corpus_obj=False, group='New corpora'),
	dict(name='import', help='Import an existing corpus into LLP', takes_corpus_obj=False, group='New corpora'),

	dict(name='compile', help='Compile corpus from sources', takes_corpus_obj=True, group='Downloading corpora'),
	dict(name='download', help='Download files of corpus (TXT/XML/metadata/data)', takes_corpus_obj=True, group='Downloading corpora'),
	dict(name='install', help='Run a sweep of text mining routines', takes_corpus_obj=True, group='Downloading corpora'),

	dict(name='zip', help='Zip and archive a corpus', takes_corpus_obj=True, group='Archiving and sharing'),
	dict(name='upload', help='Upload to a cloud the zip archives', takes_corpus_obj=True, group='Archiving and sharing'),
	dict(name='share', help='Get cloud share links for a corpus', takes_corpus_obj=True, group='Archiving and sharing'),
	dict(name='links', help='Get cloud share links for all corpora',takes_corpus_obj=False, group='Archiving and sharing'),
	dict(name='configure', help='Check LLP configuration', takes_corpus_obj=False, group='Configuration')
	]

#SUBCOMMANDS={}
#SUBCOMMANDS['create']


LOGO="""
#####################################
# LLP: Literary Language Processing #
#####################################
"""

def usage():
	groups=set()
	lines=[]
	for cmd_d in COMMANDS:
		cmd=cmd_d['name']
		help=cmd_d['help']
		takes_corpus = cmd_d['takes_corpus_obj']
		arggroup_name = cmd_d['group']

		if not arggroup_name in groups:
			groups|={arggroup_name}
			lines.append('\n\t# '+arggroup_name)

		lines.append('\t{cmd:30s} '.format(cmd=cmd))

	return LOGO + '\n'.join(lines)


def main():
	import llp
	#from llp.lib.tab_completer import tabCompleter
	#import readline
	#tabber=tabCompleter()
	parser = argparse.ArgumentParser()
	subparsers = parser.add_subparsers(dest='cmd')

	# readline.set_completer_delims('\t')
	# if 'libedit' in readline.__doc__:
	# 	readline.parse_and_bind("bind ^I rl_complete")
	# else:
	# 	readline.parse_and_bind("tab: complete")
	# readline.set_completer(tabber.pathCompleter)

	name='CorpusName'
	idx='corpus_id'

	## Set defaults
	path_root_default=os.path.join(llp.PATH_CORPUS,idx)
	path_code_default=idx+'.py'
	path_txt_default=os.path.join(path_root_default,'txt')
	path_xml_default=os.path.join(path_root_default,'xml')
	path_metadata_default=os.path.join(path_root_default,'metadata.txt')
	class_name_default = ''.join([x for x in name if x.isalnum() or x=='_'])
	default_col_id='id'
	default_col_fn='fn'

	## ADD SUBPARSERS
	groups={}
	arggroups = {}

	for cmd_d in COMMANDS:
		cmd=cmd_d['name']
		help=cmd_d['help']
		takes_corpus = cmd_d['takes_corpus_obj']
		arggroup_name = cmd_d['group']

		groups[cmd] = group = subparsers.add_parser(cmd,help=help)
		if takes_corpus:
			groups[cmd].add_argument('corpus',help='A corpus, specified by name or ID (in manifest)')


	## CUSTOMIZE SUBPARSERS

	### CREATE
	groups['create'].add_argument('-name',help='Name of corpus, in CamelCase (e.g. ChadwyckPoetry): ')
	groups['create'].add_argument('-id',help='ID of corpus, in lowercase (e.g. chadwyck_poetry)')
	groups['create'].add_argument('-path_root',help='Path to root folder for corpus ee [default: %s]' % path_root_default)
	groups['create'].add_argument('-path_txt',help='Path to folder of corpus text files [default: %s]' % path_txt_default)
	groups['create'].add_argument('-path_xml',help='Path to folder of corpus xml files [default: %s]' % path_xml_default)
	groups['create'].add_argument('-path_metadata',help='Path to corpus metadata (CSV/TSV/XLS/XLSX) [default: %s]' % path_metadata_default)
	groups['create'].add_argument('-path_python',help='Path to python file [default: %s]' % path_code_default)
	groups['create'].add_argument('-class_name',help='Name of corpus class within code [default: %s]' % class_name_default)
	groups['create'].add_argument('-defaults',help='Use defaults (besides name and id]',action='store_true')


	## IMPORT
	groups['import'].add_argument('-name',help='Name of corpus, in CamelCase (e.g. ChadwyckPoetry): ')
	groups['import'].add_argument('-id',help='ID of corpus, in lowercase (e.g. chadwyck_poetry)')
	groups['import'].add_argument('-path_root',help='Path to root folder for corpus ee [default: %s]' % path_root_default)
	groups['import'].add_argument('-path_txt',help='Path to folder of corpus text files [default: %s]' % path_txt_default)
	groups['import'].add_argument('-path_xml',help='Path to folder of corpus xml files [default: %s]' % path_xml_default)
	groups['import'].add_argument('-path_metadata',help='Path to corpus metadata (CSV/TSV/XLS/XLSX) [default: %s]' % path_metadata_default)
	groups['import'].add_argument('-col_fn',help='Column in metadata to look for TXT or XML filename [default: %s]' % default_col_fn)
	groups['import'].add_argument('-col_id',help='Column in metadata to look for ID (the TXT or XML filename, but without a filename extension) [default: %s]' % default_col_fn)

	groups['install'].add_argument('-part',help='Name of part of installation (otherwise all)')


	parser.add_argument('-slingshot',action='store_true',default=False)
	parser.add_argument('-parallel',help='How many parallel processes to run on, using Slingshot?',default=None)
	parser.add_argument('-sbatch',help='Run on sherlock?',action='store_true')
	parser.add_argument('-slingshot_opts',help='Other options to pass to slingshot',default='-progress')
	parser.add_argument('-savedir',help='Directory to save to (if applicable)',default='')
	parser.add_argument('-hours',help='Number of hours to run on Sherlock',default=2)
	parser.add_argument('-quiet',help='No interactive mode',action='store_true')
	args = parser.parse_args()

	if not hasattr(args,'corpus'): args.corpus=None
	if not hasattr(args,'cmd'): args.corpus=None


	if args.parallel: args.slingshot=True

	if args.sbatch and not '-sbatch' in args.slingshot_opts:
		args.slingshot_opts+=' -sbatch'
		args.slingshot_opts= args.slingshot_opts.replace('-progress','')



	## SOLO COMMANDS
	if args.cmd=='list':
		import llp
		manifest=llp.corpus.load_manifest()
		corpus_names=sorted(list(set(manifest.keys())))
		for cname in corpus_names:
			print('>>',cname,'\n'+manifest.get(cname,{}).get('desc','').strip()+'\n')

	elif args.cmd=='status':
		from llp.corpus import check_corpora
		check_corpora()

	elif args.cmd=='links':
		from llp import tools
		print(tools.cloud_list())

	elif args.cmd=='configure':
		import llp
		llp.configure_prompt()

	elif args.cmd=='create':
		start_new_corpus_interactive(args)

	elif args.cmd=='create':
		start_new_corpus_interactive(args)

	elif args.cmd=='import':
		start_new_corpus_interactive(args,import_existing=True)

	## COMMANDS ON CORPORA
	elif args.cmd in {'compile','install','download'} and args.corpus:
		import llp
		corpus = llp.load(args.corpus)
		cmder = getattr(corpus,args.cmd)
		if 'ask' in llp.tools.valid_args_for(cmder):
			cmder(ask=not args.quiet)
		else:
			cmder()

	elif args.cmd=='info' and args.corpus:
		import llp
		corpus = llp.load(args.corpus)
		print('['+corpus.name+']')
		for x in sorted(dir(corpus)):
			if x.startswith('path_'):
				v=getattr(corpus,x)
				if v and type(v)==str: print(x,'-->',v,'!' if not os.path.exists(v) else '')


	elif args.cmd=='zip' and args.corpus:
		import llp
		corpus = llp.load(args.corpus)
		ofolder = args.savedir
		sbatch_opts = llp.config['SBATCH_OPTS']+' -t {mins}'.format(mins=60*int(args.hours)) if 'SBATCH_OPTS' in llp.config else ''
		corpus.zip(savedir=ofolder,sbatch=args.sbatch, sbatch_opts=sbatch_opts, ask=not args.quiet)

	elif args.cmd=='upload' and args.corpus:
		import llp
		corpus = llp.load(args.corpus)
		corpus.upload(ask=not args.quiet)

	elif args.cmd=='share' and args.corpus:
		import llp
		corpus = llp.load(args.corpus)
		corpus.share()

	elif args.corpus and args.cmd in {'save_freqs','save_plain_text','save_metadata'}:
		import llp

		#xargs=list(args[3:])
		xargs=[]
		kwargs={}

		#if args.slingshot:
		kwargs['slingshot']=args.slingshot
		kwargs['slingshot_n']=args.parallel
		kwargs['slingshot_opts']=args.slingshot_opts

		C=llp.load(args.corpus)
		f=getattr(C,args.cmd)
		f(*xargs,**kwargs)

	#else:
	#	import llp
	#	C=llp.load(args.corpus)
	#	f=getattr(C,args.cmd)
	#	f()












def start_new_corpus_interactive(args,import_existing=False):
	import os
	import llp
	from llp import tools

	keys_mentioned =['path_root','path_xml','path_txt','path_python','path_metadata','class_name','name','id','desc','link']
	for k in keys_mentioned:
		if not hasattr(args,k):
			setattr(args,k,'')

	try:
		print('### LLP: Start up a new corpus ###')

		name,idx=args.name,args.id
		if not name: name=input('\n>> (1) Set name of corpus (CamelCase, e.g. ChadwyckPoetry or OldBailey): ').strip()
		if not idx: idx=input('>> (2) Set ID of corpus (lower-case, e.g chadwyck_poetry or oldbailey): ').strip()


		## Set defaults
		path_root_default=idx
		path_code_default=idx+'.py'
		path_txt_default='txt'
		#path_xml_default='xml'
		path_xml_default=''
		path_metadata_default='metadata.txt'
		class_name_default = ''.join([x for x in name if x.isalnum() or x=='_'])

		# import vs create
		# if importing, we're finding an existing directory
		# if creating, we're specifying a future directory
		sources = ['.',llp.PATH_CORPUS] if import_existing else [llp.PATH_CORPUS,'.']

		def get_path_abs(path,sources=sources,rel_to=None):
			if not path: return ''
			if os.path.isabs(path):
				rpath=path
			else:
				rpath=''
				for source in sources:
					spath=os.path.join(source,path)
					#if os.path.isabs(spath): return spath
					if os.path.exists(spath):
						rpath=os.path.abspath(spath)
						break
			if not rpath: return ''

			if rel_to:
				return os.path.relpath(rpath,rel_to)
			else:
				return os.path.abspath(rpath)




		attrs={}

		def do_path(path,path_name,msg,root,default,remove_root=True):
			if not path and not args.defaults:
				path=get_path_abs(input(msg).strip())
			else:
				print(msg)
				#print(f'   -{path_name} set from command line...')
			path_abs_default=os.path.join(root,default)
			path_abs=path=get_path_abs(path)
			if not path:
				path=default
				path_abs=os.path.join(root,path)
			if not path: return ''
			link_to=path_abs_default if path_abs!=path_abs_default else None
			tools.check_make_dir(path_abs) #,link_to=link_to)

			#print('?',path,path_name,path_abs,path_abs_default)
			#if not path_name in {'path_xml'} or os.path.exists(path_abs):
			#print('>> setting: %s =' % path_name,path)
			if remove_root:
				#print(path_name+'\n'+path+'\n'+root)
				if path.startswith(root):
					path=path[len(root):]
					if path and path[0] in {'/','\\'}: path=path[1:]
				#print(path,'\n\n')


			prefix='   %s =' % path_name

			#print(prefix,path)
			print(f'\n   [manifest] {path_name} = {path}')
			print(f'   [abs path] {path_abs}\n')

			if path_abs and link_to and os.path.dirname(path_abs)!=os.path.dirname(link_to):
				tools.symlink(path_abs,link_to)



			return path

		msg='\n----\n\n>> (3) Set path to corpus root data folder [%s]:\n>> ' % path_root_default
		path_root = attrs['path_root'] = do_path(args.path_root, 'path_root', msg, llp.PATH_CORPUS, path_root_default)
		path_root_abs = os.path.join(llp.PATH_CORPUS,path_root) if not os.path.isabs(path_root) else path_root

		msg='\n----\n\n>> (4) Set path to metadata file [%s/%s]:\n>> ' % (path_root,path_metadata_default)
		path_metadata = attrs['path_metadata'] = do_path(args.path_metadata, 'path_metadata', msg, path_root_abs, path_metadata_default)

		msg='\n----\n\n>> (5) Set path to txt folder (Optional, if xml folder)\n[%s/%s]:\n>> ' % (path_root,path_txt_default)
		path_txt = attrs['path_txt'] = do_path(args.path_txt, 'path_txt', msg, path_root_abs, path_txt_default)

		msg='\n>> (6) Set path to xml folder (Optional, if txt folder)\n[%s/%s]:\n>> ' % (path_root,path_xml_default)
		path_xml = attrs['path_xml'] = do_path(args.path_xml, 'path_xml', msg, path_root_abs, path_xml_default)

		msg='\n>> (7) Set path to a .py file defining corpus object (Optional)\n[%s/%s]:\n>> ' % (path_root,path_code_default)
		path_python = attrs['path_python'] = do_path(args.path_python, 'path_python', msg, path_root_abs, path_code_default)

		# class name
		class_name=args.class_name
		if not class_name: class_name=input('>> (8) Set name of corpus class within python code (Optional) [%s]: ' % class_name_default).strip()
		if not class_name: class_name=class_name_default
		attrs['class_name'] = class_name
		print('\n   [manifest] class_name =',class_name,'\n')

		# optional
		desc=args.desc
		if not desc: desc=input('>> (9) Set description of corpus (Optional): ').strip()
		if not desc: desc='--'
		attrs['desc']=desc

		link=args.link
		if not link: input('>> (10) Set web link to/about corpus (Optional): ').strip()
		if not link: link='--'
		attrs['link']=link
	except KeyboardInterrupt:
		print()
		exit()


	attrs['name']=name
	attrs['id']=idx
	attrs={'name':name,'id':idx,'desc':desc,'link':link,
	'path_root':path_root,'path_txt':path_txt,'path_xml':path_xml,'path_metadata':path_metadata,
	'path_python':path_python,'class_name':class_name}

	from llp.corpus import start_new_corpus
	return start_new_corpus(attrs)


#def import_corpus_interactive():











### RUN
if __name__ == '__main__': main()
