#!/usr/bin/env python3

## Generate pdf form lilypond file
## by using standard command
##
## Most of programming was done by Sven Axelsson, http://svenax.net/

import io, os
from argparse import ArgumentParser

class MakeDrum:
	LILYPOND = 'lilypond'
	VERSION  = '0.9.7'
	MASTER_DIR = os.path.dirname(os.path.abspath(__file__))
	RUN_DIR = os.path.abspath(os.curdir)
	TMP_DIR  = 'tmp'
	TMP_PREFIX = 'tmp_'
	OUT_DIR = '.'

	def __init__(self):
		# Gather options and create the template file
		usage = __file__
		parser = ArgumentParser(usage)

		parser.add_argument('--version',
			action='store_true', dest='show_version', default=False,
			help='show makeDrum version and exit')
		parser.add_argument('--lilyversion',
			action='store_true', dest='show_lilyversion', default=False,
			help='show Lilypond version and exit')
		parser.add_argument('-l', '--lilycommand',
			default=self.LILYPOND, dest='lilypond',
			help='Command used to compile')
			

		parser.add_argument('-i', '--include',
			dest='includes', nargs='*', default=[],action='append',
			help='Include the specified file for compiling')
		parser.add_argument('-I', '--path-include',
			dest='search_paths', nargs='*', default=[], action='append',
			help='Paths for lilypond to search through while compiling')

		parser.add_argument('-p', '--paper-size',
			dest='papersize', default='a4',
			help='Paper size. Default: A4')
		parser.add_argument('-o', '--orientation',
			dest='orientation', default='landscape',
			help='Paper orientation. Default: landscape')
		parser.add_argument('-s', '--staff-size',
			dest='staffsize', default='18',
			help='Staff size. Default: 18pt.')
		parser.add_argument('-w', '--view-spacing',
			action='store_true', dest='view_spacing', default=False,
			help='Turn on "Paper.annotatespacing".')

		parser.add_argument('--rename',
			action='store_true', dest='rename', default=False,
			help='Rename the final output to it\'s relative location, replacing /\'s with -\'s')
		parser.add_argument('-r', '--suffix',
			dest='suffix', default='',
			help='String added at end of pdf\'s filename')
		parser.add_argument('-g', '--generated',
			dest='gen_out', default=self.TMP_DIR,
			help='Put generated lilyfiles in $gen_out')
		parser.add_argument('-q', '--self-compilable', default=False,
			action='store_true', dest='compilable',
			help='Make a self compilable file')
		parser.add_argument('--no-compile', default=True,
			action='store_false', dest='compile',
			help='Do not compile generated Lilypond files')
		parser.add_argument('--no-log',
			action='store_false', dest='log', default=True,
			help='Do not generate log files.')
		parser.add_argument('--no-cleanup',
			action='store_false', dest='clean', default=True,
			help='Leave all temporary files in place')
		parser.add_argument('-d', '--out-dir',
			dest='out_dir', default=self.OUT_DIR,
			help='Output dir, for lilypond. If it doesn\'t exist, try to create it')
		parser.add_argument('music_file',
			default='', nargs='*',
			help='file to process')
		parser.add_argument('-@', '--list_file',
			dest='list_file', default='',
			help='list of files to process')

		# All unknown args are passed on to the lilypond command
		self.args, self.unknownargs = parser.parse_known_args()

		if self.args.show_version:
			print(__name__, ' ', self.VERSION)
			return

		if self.args.show_lilyversion:
			print(os.system(self.LILYPOND+' --version'))
			return

		if self.args.view_spacing:
			self.args.view_spacing = "##t"
		else:
			self.args.view_spacing = "##f"

		# Input files
		if self.args.list_file != '':
			with io.open(self.args.list_file, 'r', encoding='utf8') as list_file:
				for line in list_file.readlines():
					self.args.music_file.append(line)

		# Check for files
		if not self.args.music_file:
			parser.print_usage()
			return

		# Check for files to include
		self.args.includes = [el for elements in self.args.includes for el in elements]

		# Clean up of files
		self.remove_tmp_dir = self.args.clean

		if not os.path.exists(os.path.join(os.path.curdir, self.TMP_DIR)):
			try: os.makedirs(os.path.join(os.path.curdir, self.TMP_DIR))
			except:
				print('Seems like no temporary directory can be created')
				return
		if not os.path.exists(os.path.join(os.path.curdir, self.args.out_dir)):
			try: os.makedirs(os.path.join(os.path.curdir, self.args.out_dir))
			except:
				print('Seems like no output directory can be created')
				return

		for file_path in self.args.music_file:
			self.processit(self.TMP_DIR, os.path.join(self.RUN_DIR, file_path), self.args.gen_out, self.args.compile)

		if not os.listdir(self.TMP_DIR):
			os.rmdir(self.TMP_DIR)


	def processit(self, tmp_dir, file, gen_out, compile):
		print ('Generating for ',file, end=' ', flush=True)
		tmp_file = self.maketemplate(tmp_dir, file, self.args.compilable)
		print ('[OK]')


		if gen_out is not None and gen_out != tmp_dir:
			new_tmp_file = os.path.basename(tmp_file).replace(self.TMP_PREFIX, '');
			print ('Moving ', tmp_file, ' to ', new_tmp_file, end=' ', flush=True)
			gen_dir = os.path.join(self.RUN_DIR, gen_out);
			# if not dir $gen_out, make it
			if not os.path.exists(gen_dir):
				try: os.makedirs(gen_dir)
				except:
					print('[Error]')
					print(' ! Seems like the {} directory cannot be created'.format(gen_dir))
					return
			# mv file to dir, remove self.TMP_PREFIX
			os.rename(tmp_file, os.path.join(gen_dir, new_tmp_file))
			tmp_file = new_tmp_file
			print('[OK]')

		if compile:
			if self.args.log:
				logfile = os.path.join(self.TMP_DIR, os.path.relpath(file).replace(".ly", '').replace('/', '-')+'.log')
				log = ' > '+logfile+' 2>&1'
			else:
				log = ''

			print ('Compiling ', file, end=' ', flush=True)
			if not self.args.log:
				print()

			# stringify search paths
			#TODO: this is ugly code
			paths = ' -I '.join(str(x)[1:-1] for x in self.args.search_paths)
			if paths:
			    paths = ' -I ' + paths

			if self.args.rename:
			    lilyout = os.path.join(self.RUN_DIR, self.args.out_dir, os.path.basename(tmp_file).replace(self.TMP_PREFIX, '').rsplit( ".", 1 )[ 0 ])
			else:
			    lilyout = os.path.basename(file).rsplit( ".", 1 )[ 0 ]


			unknownargs = " " + " ".join(self.unknownargs)

			lilycmd = self.LILYPOND + unknownargs + paths + ' --output='+lilyout+' '+tmp_file+log

			if os.system(lilycmd) != 0:
				self.remove_tmp_dir = False
				print ('[Error]')
				if self.args.log:
					print (' ! Did not compile, please see the log at ', logfile)
			else :
				print ('[OK]')
	
				if self.args.clean:
					#remove files
					if self.args.log:
						os.remove(logfile)
					if not self.args.compilable:
						os.remove(tmp_file)


	def maketemplate(self, tmp_dir, file, compilable):

		lily_includes = ''

		# set up a tmp file with template and file combined
		tmp_file = os.path.join(tmp_dir, self.TMP_PREFIX + os.path.relpath(file).replace('../','').replace('music/','',1).replace('/', '-')[:-3] + self.args.suffix + '.ly')

		# Make the file
		with io.open(tmp_file, 'w+', encoding='utf8') as out_file:

			def printline(line, relpath = file):

				# Check if there's an include in the line, if there is try to copy it all (Recursive)
				if line.startswith(u'\\include'):
					# Rewrite includes to absolute location of file
					incline = line.replace('\\include', '').strip('"\'\n ')
					printline(u"\n %%%% \"{}\"\n".format(incline))

					# Only rewrite if it begins with a dot
					# if / it's absolute, and something else means search_path
					if incline.startswith('.'): #
						incline = os.path.join(os.path.abspath(os.path.dirname(relpath)), incline)

					if compilable:
						try:
							inc_file = io.open(incline,'r',encoding='utf8')
						except IOError:
							out_file.write(line + "%% Error to copy %%\n")
							return
						with inc_file:
							for subline in inc_file.readlines():
								printline(subline, incline)
					else:
						out_file.write(u"\\include \""+incline+"\"\n")
					return

				out_file.write(line.replace('\r', ''))

			# Go do things with it
			printline(u'\ufeff')

			printline(
u"""% Generated from """+file+""" by """+__file__+""" version """+self.VERSION+"""
\\version "2.18.0"

#(ly:set-option 'point-and-click #f)
%\layout {
% \context {
%   \Score {
%	 \override NonMusicalPaperColumn #'line-break-permission = ##f
%   }
% }
%}
""")
			for f in self.args.includes:
				printline(u"\n")
				printline(u"\\include \"{}\"".format(f), self.RUN_DIR+'/build')

			printline(u"""
#(set-global-staff-size """+self.args.staffsize+""")
#(set-default-paper-size \""""+self.args.papersize+"""\" '"""+self.args.orientation+""")

% The tune to generate.
""")

			# Read lily file into tmp file
			with io.open(file, 'r', encoding='utf8') as in_file:
				for line in in_file.readlines():
					if line.startswith(u'\ufeff'): continue

					printline(line)
		# Return tmp_file_path
		return tmp_file

MakeDrum();