# make build dir (clean)
# copy everything relevant from source to dest
# copy in central bib file (optional)
# change to directory
# run latex
# run bibtex (on what files ...)
#   * ans: all files that start with build file name
# (optional) copy resulting pdf somewhere
# (optional) delete build dir
import os
import shutil
import commands

# constants
TMP_BUILD_DIR = '/tmp/latex-builder'
BIB_FILE = '/home/rgrp/svnroot/rgrp/work/econ/biblio/econ.bib'

class LatexBuilderBase(object):
    """Build LaTeX source to pdf.
    """

    def __init__(self, master_build_file_name, build_dir=None, skip=True,
            verbose=False, ignore_errors=False, simple_build=False):
        '''
        @param skip: skip what one can. See comments on L{main()} for details.
        @param simple_build: do not do bibtex and only run latex once (as
            opposed to 3 times when with bibtex)
        '''
        self.master_file_path = master_build_file_name
        self.master_file_name = os.path.basename(self.master_file_path)
        if build_dir is None:
            self.build_dir = TMP_BUILD_DIR
        else:
            self.build_dir = build_dir
        self.verbose = verbose
        self.skip = skip
        self.ignore_errors = ignore_errors
        self.simple_build = simple_build
    
    ## =========================
    ## Helpers

    def _run(self, cmd):
        print 'Running cmd: %s' % cmd
        if self.verbose:
            os.system(cmd)
        else:
            status, output = commands.getstatusoutput(cmd)
            if status and not self.ignore_errors:
                msg = 'ERROR: %s' % output
                raise Exception(msg)
            elif status:
                print output

    def pdflatex(self, filename):
        pdflatex = 'pdflatex --output-dir=' + self.build_dir + ' %s'
        ltxbuild = pdflatex % filename
        self._run(ltxbuild)

    def bibtex(self, filename):
        if filename.endswith('.tex'):
            filename = filename[:-4]
        bibtex = 'bibtex %s' % filename
        self._run(bibtex)

    def copy_stuff(self, filepath):
        dirpath = os.path.dirname(filepath)
        files = os.listdir(dirpath)
        for fn in files:
            root, ext = os.path.splitext(fn)
            # *.png, *.tex, *.bib, *.py
            if ext in [ '.png', '.tex', '.bib', '.py' ]:
                fpath = os.path.join(dirpath, fn)
                shutil.copy(fpath, self.build_dir)
            # holding often contains external media (images etc ...)
            if fn == 'holding':
                srcpath = os.path.join(dirpath, fn)
                destpath = os.path.join(self.build_dir, 'holding')
                # only copy if not already there to be efficient
                if not os.path.exists(destpath):
                    shutil.copytree(srcpath, destpath)

    ## ==========================
    ## Actions

    def clean(self):
        if os.path.exists(self.build_dir):
            shutil.rmtree(self.build_dir)

    def prepare(self):
        if not os.path.exists(self.build_dir):
            os.makedirs(self.build_dir)
        self.prepare_extra()

    def prepare_extra(self):
        '''Extra prepare stuff. To be overriden in base classes.

        Working directory is base directory.
        '''
        pass

    def copy_central_bib(self):
        shutil.copy(BIB_FILE, self.build_dir)

    def copy_pdf_back(self):
        "Copy pdf file back to original file."
        pdffn = self.master_file_name[-4] + '.pdf'
        src = os.path.join(self.build_dir, pdffn) 
        dest = os.path.dirname(self.master_file_path)
        shutil.copy(src, dest)
    
    def run_bibtex(self):
        # get a list of all the tex files in that directory
        def is_tex_file(fn):
            return fn.endswith('.tex')
        texfiles = filter(is_tex_file, os.listdir(self.build_dir))
        for fn in texfiles:
            self.bibtex(fn)

    def run_latex(self):
        self.pdflatex(self.master_file_name)

    def _setup(self):
        self.prepare()
        self.copy_stuff(self.master_file_path)
        self.copy_central_bib()
    
    def build_start(self):
        '''To be overridden in base classes.
        
        Run after change into build directory but before any building.'''
        pass

    def _build(self):
        os.chdir(self.build_dir)
        self.build_start()
        self.run_latex()
        if not self.simple_build:
            self.run_bibtex()
            self.run_latex()
            self.run_latex()

    def execute(self):
        self._setup()
        self._build()


class LatexBuilderStub(LatexBuilderBase):

    def execute(self):
        self.clean()
        super(LatexBuilderStub, self).execute()
        print '***** hello *****'


class TestLatexBuilder:

    def setup_class(self):
        src = '/Users/rgrp/svk/rgrp/work/econ/network/trunk/porting.tex'
        self.builder = LatexBuilderStub(src)
        self.builder.clean()
        self.builder.execute()
        self.dest = TMP_BUILD_DIR
        self.files = os.listdir(self.dest)

    def test_1(self):
        assert os.path.exists(self.dest)

    def test_2(self):
        assert 'porting.tex' in self.files

    def test_bibfile_is_there(self):
        assert 'econ.bib' in self.files

    def test_it_worked(self):
        assert 'porting.pdf' in self.files

def main(builder):
    import optparse
    usage = \
'''%prog [options] [action] 

Build a latex file to a pdf file.

action = build (default) | clean | prepare
'''
    parser = optparse.OptionParser(usage)
    parser.add_option('-v', '--verbose',
              action='store_true', dest='verbose', default=False,
              help='Be verbose in printing information to stdout')
    parser.add_option('-n', '--noskip', dest='skip', action='store_false',
            default=True, help='Avoid lengthy builds of things like images.')
    parser.add_option('-i', '--ignore-errors',
              action='store_true', dest='ignore_errors', default=False,
              help='Ignore errors when running commands and just keep going.')
    options, args = parser.parse_args()
    builder.skip = options.skip
    builder.verbose = options.verbose
    builder.ignore_errors = options.ignore_errors
    action = 'build'
    if len(args) > 0:
        action = args[0]

    if action == 'build':
        builder.execute()
    elif action == 'clean':
        builder.clean()
    elif action == 'prepare':
        builder.prepare()
    else:
        print 'Unknown action: %s' % action

