from __future__ import absolute_import
from __future__ import with_statement

import sys, os, subprocess, shutil, re
import tempfile

from . import util, custom
from bazbase import translators, conversion, structure
from redbeans.latex import url_to_path

class TeXFailedError(IOError):
    pass

PDFLATEX_COMMAND_LINE = ('xelatex','-interaction','nonstopmode')
# TODO(xavid): this doesn't work with Unicode, replace with pdf2ps
DVILATEX_COMMAND_LINE = ('latex','-interaction','nonstopmode')
DVIPS_COMMAND_LINE = ('dvips','-o','')

MISSING_CHARACTER_PAT = re.compile(
    r'^Missing character: There is no (.) in font (.+)\n$')

def typeset(im,format='.pdf'):
    """Latex the latex code referred to by im, then return an im for the resulting file in the specified format."""
    texfpath = im.asPath()
    assert texfpath.endswith('.tex')
    temp_dir = tempfile.mkdtemp('.bazkilatex')
    baseoutpath = os.path.join(temp_dir,os.path.basename(texfpath)[:-4])

    if 'images' in im.metadata():
        images = im.metadata()['images']
        for i in images:
            if format == '.pdf':
                formats = ('.epdf',)
                backup_formats = ('.jpg','.png')
            else:
                formats = ('.eps',)
                backup_formats = ()
            ename,pname_w_filters = i.split('.', 1)
            #if '.' in pname_w_filters:
            #    pname_w_filters = pname_w_filters.split('.')[0]
            from . import getting
            filters = {}
            pname = conversion.extract_filters(pname_w_filters, filters)

            # Ignore any scaling on the image, because we trust the LaTeX
            # to do that less lossily.
            if 'd' in filters:
                filters = dict(filters)
                del filters['d']
            try:
                imgim = conversion.convert_any(ename, pname, formats,
                                               filters)
            except conversion.ConversionFailedException:
                if len(backup_formats) > 0:
                    imgim = conversion.convert_any(ename, pname,
                                                   backup_formats, filters)
                else:
                    raise
            im.addDeps(imgim.getDeps())
            imgpath = imgim.asPath()
            # TODO(xavid): type is a bad name, and it should take a .ext
            ext = imgim.getExtension()
            assert ext[0] == '.'
            elm = structure.get_element(ename)
            url = getting.attachment_url_for(elm, pname_w_filters)
            imgloc = os.path.join(temp_dir, url_to_path(url) + ext)
            imgdir = os.path.dirname(imgloc)
            if not os.path.exists(imgdir):
                # This is a bit silly: we pretend the URL is a directory
                # structure.  Literally.  But at least we remove the
                # http://
                os.makedirs(imgdir)
            # TODO(xavid): should move instead of copying
            shutil.copy(imgpath, imgloc) 
            imgim.nix()

    if format == '.pdf':
        cline = PDFLATEX_COMMAND_LINE
    elif format == '.ps':
        cline = DVILATEX_COMMAND_LINE
    else:
        raise TypeError("Unknown typeset format '%s'!"%format)
    env = {'TEXINPUTS': ':'.join(custom.LATEX_SEARCH_PATHS),
           'HOME': os.getenv('HOME'),
           'PATH': os.getenv('PATH')}
    tex=subprocess.Popen(cline+(texfpath,),
                         cwd=temp_dir,
                         env=env,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    texout,texerr = tex.communicate()
    if tex.returncode != 0:
        raise TeXFailedError(tex.returncode,
                             texout+'\n\nEnvironment: '+str(env))
    missing = {}
    with open(baseoutpath+'.log') as log:
        cont = None
        for l in log:
            if cont:
                assert l.endswith('!\n')
                fontname += l[:-2]
                missing.setdefault(fontname, set()).add(cont)
                cont = None
            elif l.startswith('Missing character'):
                m = MISSING_CHARACTER_PAT.match(unicode(l, 'utf-8'))
                assert m is not None, repr(l)
                fontname = m.group(2)
                if fontname.endswith('!'):
                    missing.setdefault(fontname[:-1], set()).add(m.group(1))
                else:
                    cont = m.group(1)
    if missing:
        message = 'Missing characters: '
        message += '; '.join("font '%s' doesn't contain %s"
                             % (fontname, 
                                ', '.join("%s (%s)" % (c, repr(c)) 
                                          for c in missing[fontname]))
                             for fontname in missing)
        raise UnicodeError(message.encode('utf-8'))
    if format == '.ps':
        subprocess.check_call(DVIPS_COMMAND_LINE+(baseoutpath,),
                              cwd=temp_dir)
    # Clean up and such
    fdesc,retpath = tempfile.mkstemp(suffix=format)
    os.close(fdesc)
    os.rename(baseoutpath+format,retpath)
    #shutil.rmtree(temp_dir)

    im.setPath(retpath)

