#!/usr/bin/env python -u
import codecs, os, string, subprocess, sys
from optparse import OptionParser

class MakeLily:
    # Path constants
    EDITOR = 'subl'
    LILYPOND = 'lilypond'
    MOGRIFY = 'mogrify'
    READER = 'open -a PDF\ Expert.app '
    SCRIPT_PATH = os.path.expanduser('~/Documents/Sheetmusic/Bagpipe')

    def __init__(self):
        # Gather options and create the template file

        usage = 'makelily.py [options] music-file.ly'
        parser = OptionParser(usage)
        parser.add_option('-@', '--list-file',
                action='store', type='string', dest='list_file', default='',
                help='Read files to process from a file.')
        parser.add_option('-s', '--staff-size',
                action='store', type='string', dest='staffsize', default='15',
                help='Staff size. Default 15pt.')
        parser.add_option('-p', '--paper-size',
                action='store', type='string', dest='papersize', default='a4',
                help='Paper size. Default A4 (Yes, I am from Europe).')
        parser.add_option('-l', '--landscape',
                action='store_const', const='landscape', dest='paperdir', default='portrait',
                help='Use landscape paper orientation. Default portrait.')
        parser.add_option('-f', '--format',
                action='store', type='string', dest='format', default=None,
                help='Use header format file "header_{format}". Default "header_default".')
        parser.add_option('-t', '--type',
                action='store', type='string', dest='type', default='pdf',
                help="Save output as {type}.")
        parser.add_option('-c', '--crop',
                action='store_true', dest='crop', default=False,
                help='Crop page to minimal size.')
        parser.add_option('-r', '--root',
                action='store_true', dest='root', default=False,
                help='Save result in project root.')
        parser.add_option('-v', '--version',
                action='store_true', dest='show_version', default=False,
                help='Show Lilypond version.')
        parser.add_option('-w', '--view-spacing',
                action='store_true', dest='view_spacing', default=False,
                help='Turn on "Paper.annotatespacing".')
        parser.add_option('-o', '--open-pdf',
                action='store_true', dest='open_pdf', default=False,
                help='Open the generated PDF in the configured reader.')
        parser.add_option('--no-log',
                action='store_false', dest='log', default=True,
                help='Do not generate log files.')

        (self.options, self.args) = parser.parse_args()

        if self.options.show_version:
            print os.system(self.LILYPOND+' --version')
            return

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

        if self.options.list_file != '':
            self.args = open(self.options.list_file, 'r').readlines()

        cwd = os.getcwd()
        os.chdir(self.SCRIPT_PATH)

        for file_path in self.args:
            self.processit(file_path.strip())

        os.chdir(cwd)

    def processit(self, file_path):

        file_path = os.path.abspath(os.path.expanduser(file_path))
        rel_file_path = file_path.replace(self.SCRIPT_PATH, '')
        rel_dir = os.path.dirname(rel_file_path)
        rel_dir = rel_dir.replace('/', '')
        out_path = os.path.join(self.SCRIPT_PATH, '_output', rel_dir)

        if self.options.root:
            out_path = self.SCRIPT_PATH
        (file_base, _) = os.path.splitext(os.path.basename(rel_file_path))
        orig_file_base = file_base

        header_format = self.options.format
        if header_format == None:
            if file_base.endswith(".book"):
                header_format = "header_book"
                file_base = file_base.replace(".book", "")
            else:
                header_format = "header_default"

        elide_tagline = ''
        if self.options.crop:
            elide_tagline = 'tagline = ""'

        # The output file header
        out = u"""
% Generated from """+file_path+""" by makelily.py

\\version "2.16.0"

#(ly:set-option 'point-and-click #f)

#(set-global-staff-size """+self.options.staffsize+""")
#(set-default-paper-size \""""+self.options.papersize+"""\" '"""+self.options.paperdir+""")

\\include "./bagpipe_new.ly"
\\include "./bagpipe_extra.ly"
\\include "./"""+header_format+""".ly"

% Local tweaks
\\paper {
    annotatespacing = """+self.options.view_spacing+"""
    ragged-bottom = ##t
    """+elide_tagline+"""
    }
\layout {
  \context {
    \Score
    % We only permit explicit line breaks
    \override NonMusicalPaperColumn #'line-break-permission = ##f
  }
}

% The tune to generate.
"""
        # Run Lilypond to produce output

        if self.options.log:
            log = ' > '+file_base+'.log 2>&1'
        else:
            log = ''

        print 'Processing "'+os.path.join(rel_dir, orig_file_base)+'.ly"'
        print '** Creating preview file'
        self.maketemplate(file_base, file_path, out, True)
        is_ok = os.system(self.LILYPOND+' -dpreview -dresolution=84 -dpreview-include-book-title -dwithout-comment '+file_base+'.ly'+log) == 0
        
        if is_ok:
            print '** Creating output file'
            self.maketemplate(file_base, file_path, out, False)
            if self.options.type == 'pdf':
                is_ok = os.system(self.LILYPOND+' --pdf '+file_base+'.ly'+log) == 0
            else:
                is_ok = os.system(self.LILYPOND+' --png -dresolution=144 '+file_base+'.ly'+log) == 0

        # Move the output to the correct directories and delete temp files
        print '** Cleaning up'
        music_files = [file_base+ext for ext in ('.pdf', '.preview.png')]
        junk_files = [file_base+ext for ext in ('.preview.eps', '.preview.pdf', '.ly', '.ps')]
        try: os.makedirs(out_path)
        except: pass

        if not self.options.root:
            for f in music_files:
                try:
                    os.remove(os.path.join(out_path, f))
                except: pass
            for f in music_files:
                try:
                    os.rename(os.path.join(self.SCRIPT_PATH, f), os.path.join(out_path, f))
                except: pass

        for f in junk_files:
            try: os.remove(os.path.join(self.SCRIPT_PATH, f))
            except: pass

        if self.options.crop:
            os.system(self.MOGRIFY+' -trim -bordercolor white -border 12 '+os.path.join(out_path, file_base+'.'+self.options.type))

        if is_ok:
            try:
                os.remove(os.path.join(self.SCRIPT_PATH, file_base+'.log'))
                if self.options.open_pdf:
                    print '** Opening PDF'
                    os.system(self.READER+os.path.join(out_path, file_base+'.pdf')+' &')
            except: pass
        else:
            if not self.options.log:
                os.system(self.EDITOR+' '+file_base+'.log')

    # Read the included music file and append it to our template.
    # If minimal is set, skip lines between
    # %%% START SKIP / %%% END SKIP markers
    # We assume all files are UTF8 encoded.
    def maketemplate(self, file_base, file, template, minimal):
        out_file = codecs.open(file_base+'.ly', 'w', 'utf8')
        out_file.write(u'\ufeff')
        out_file.write(template)
        music = codecs.open(file, 'r', 'utf8').read()
        if music.startswith(u'\ufeff'): music = music[1:]
        music = music.split(u'\n')
        printit = 1
        for line in music:
            if minimal:
                if line.strip().startswith(u'%%% START SKIP'): printit = 0
                if line.strip().startswith(u'%%% END SKIP'): printit = 1
            if printit:
                out_file.write(line.replace('\r', '')+'\n')
        out_file.close()

# Create an object and start the processing
MakeLily()

