#!/usr/bin/env python
from __future__ import print_function

import sys
import subprocess
import io
import os
import optparse
from pprint import pprint

from ufl.algorithms import tree_format, compute_form_data
from ufl.formatting.ufl2dot import ufl2dot
from ufl.formatting.ufl2latex import forms2latexdocument
from ufl.formatting.ufl2unicode import form2unicode
from ufl.algorithms.formfiles import load_ufl_file

# --- Utilities


def get_status_output(cmd, input=None, cwd=None, env=None):
    """Replacement for commands.getstatusoutput which does not work on Windows (or Python 3)."""
    if isinstance(cmd, string_types):
        cmd = cmd.strip().split()
    pipe = subprocess.Popen(cmd, shell=False, cwd=cwd, env=env,
                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    (output, errout) = pipe.communicate(input=input)
    assert not errout
    status = pipe.returncode
    if isinstance(output, bytes):
        output = output.decode('utf-8')
    return (status, output)


def runcmd(cmd):
    status, output = get_status_output(cmd)
    if status != 0:
        print("*** Error:")
        print(output)
        sys.exit(-1)

def write_file(filename, text):
    "Write text to a file and close it."
    with io.open(filename, "w", encoding="utf-8") as f:
        f.write(text)
    print("Wrote file %s" % filename)

# --- Option parsing

usage = """Convert a .ufl file to some other format.

Examples:

  ufl-convert -omydir -iyourdir -c -f -tpdf -s mass.ufl"""

def opt(long, short, t, default, help):
    return optparse.make_option("--%s" % long, "-%s" % short, action="store", type=t, dest=long, default=default, help=help)

option_list = [ \
    # Directories:
    opt("outputdir", "o", "str", "", "Output directory."),
    opt("inputdir",  "i", "str", "", "Input directory."),
    # Expression transformations:
    opt("labeling",  "l", "str", "repr", "Set to 'repr' or 'compact' for different naming of graph nodes."),
    opt("compile",   "c", "int", 0, "'Compile' forms: apply expression transformations like in a quadrature based form compilation. Only used for latex formatting."),
    # Output formats:
    opt("format",    "f", "str", "", "Rendering format (str, repr, tree, dot, latex, unicode)."),
    opt("filetype",  "t", "str", "", "Output file type (txt, py, dot, tex, ps, pdf, png)."),
    ]

parser = optparse.OptionParser(usage=usage, option_list=option_list)
args = sys.argv[1:]
(options, args) = parser.parse_args(args=args)

if not args:
    print("Missing files!")
    print()
    parser.print_usage()
    sys.exit(-1)


# --- Handle each file

for arg in args:

    # 0) Get and check filename
    uflfilename = os.path.join(options.inputdir, arg)
    path, name = os.path.split(uflfilename)
    basename, ext = os.path.splitext(name)
    if ext != ".ufl":
        print("Expecting a .ufl file, not ", uflfilename)
        sys.exit(-1)

    # 1) Load forms
    ufl_data = load_ufl_file(uflfilename)
    forms = ufl_data.forms
    #expressions = ufl_data.expressions # TODO: Allow rendering expressions without form stuff!

    # Preprocess forms
    form_datas = [compute_form_data(f) for f in forms]

    # 3) Render result
    format = options.format

    # Make format string conform
    if format == "latex":
        format = "tex"
    if format == "tex":
        rendered = forms2latexdocument(forms, uflfilename, compile=options.compile)
    elif format == "unicode":
        data = []
        for form, form_data in zip(forms, form_datas):
            tmp = form2unicode(form, form_data)
            data.append(tmp)
        rendered = "\n\n".join(data)
    elif format in ("str", "repr", "tree"):
        data = []
        for i, fd in enumerate(form_datas):
            f = fd.original_form
            name = ufl_data.object_names.get(f, "form")

            if format == "str":
                s = str(f)
            elif format == "repr":
                s = repr(f)
            elif format == "tree":
                s = tree_format(f)
            tmp = "Form %s:\n%s\n" % (name, s)

            data.append(tmp)
        rendered = "\n\n".join(data)
    elif format == "dot":
        data = []
        nodeoffset = 0
        for i, fd in enumerate(form_datas):
            f = fd.original_form
            name = ufl_data.object_names.get(f, "form")

            begin = (i == 0)
            end = (i == len(forms) - 1)
            dot, nodeoffset = ufl2dot(f, name, nodeoffset, begin, end,
                                      options.labeling, ufl_data.object_names)
            tmp = "/* Form %s: */\n%s\n" % (name, dot)
            data.append(tmp)
        rendered = "\n\n".join(data)
    else:
        print("Unknown rendering format ", format)
        sys.exit(-1)

    # 4) Convert file format
    filetype = options.filetype

    # Default filetypes:
    if not filetype:
        if format == "str":
            filetype = "str"
        elif format == "repr":
            filetype = "repr"
        elif format == "tree":
            filetype = "tree"
        elif format == "dot":
            filetype = "dot"
        elif format == "tex":
            filetype = "tex"
        elif format == "unicode":
            filetype = "txt"

    # Guess that the filetype is the ext, usually the case
    ext = filetype
    if ext and not ext.startswith("."):
        ext = "." + ext
    outputfilename = os.path.join(options.outputdir, basename + ext)

    # Pure text files:
    if filetype == "txt" or filetype == format:
        write_file(outputfilename, rendered)

    # Conversions from tex:
    elif format == "tex":
        texfile = os.path.join(options.outputdir, basename + ".tex") # TODO: Use a proper temp file?
        write_file(texfile, rendered)
        if filetype == "pdf":
            flags = "-file-line-error-style -interaction=nonstopmode"
            cmd = "pdflatex %s '%s'" % (flags, texfile)
            runcmd(cmd)
        else:
            print("Unknown format and filetype combination:", format, filetype)
            sys.exit(-1)

    # Conversions from dot:
    elif format == "dot":
        tempfile = os.path.join(options.outputdir, basename + ".dot") # TODO: Use a proper temp file?
        write_file(tempfile, rendered)
        if filetype in ("png", "ps", "svg", "gif", "dia", "imap", "cmapx"): # taken from "man dot"
            runcmd("dot -T%s -o'%s' '%s'" % (filetype, outputfilename, tempfile))
        elif filetype == "pdf":
            psfilename = os.path.join(options.outputdir, basename + ".ps")
            pdffilename = os.path.join(options.outputdir, basename + ".pdf")
            runcmd("dot -T%s -o'%s' '%s'" % (filetype, psfilename, tempfile))
            runcmd("ps2pdf '%s' '%s'" % (psfilename, pdffilename))
        else:
            print("Unknown format and filetype combination:", format, filetype)
            sys.exit(-1)

    # That's all we know!
    else:
        print("*** Error: Sorry, don't know how to render format '%s' for file type '%s'." \
            % (format, filetype))
        print("Please try another combination, perhaps -fdot -tpdf?")
        sys.exit(-1)
