import os
import sys
import subprocess

from bento.core.package \
    import \
        file_list
from bento.commands.errors \
    import \
        CommandExecutionFailure
from bento.commands.core \
    import \
        Command, Option
from bento.core.utils \
    import \
        pprint, test_program_run
from bento.commands import hooks

import yaku.context
import yaku.scheduler

@hooks.pre_configure
def pre_configure(ctx):
    try:
        ctx.yaku_context.load_tool("python_2to3")
    except ImportError:
        pass

PY3K_BUILD = os.path.join("build", "py3k")

@hooks.command
def _run_2to3(ctx):
    njobs = int(os.environ.get("BENTO_2TO3_NJOBS", 1))

    bld = yaku.context.get_bld()
    if not "python_2to3" in bld.builders:
        raise ValueError("2to3 is not available")
    builder = bld.builders["python_2to3"]

    builder.env["2TO3_EXCLUDE_LIST"] = [
        os.path.join("bento", "private", "_ply", "ply", f) for f in \
        os.listdir(os.path.join("bento", "private", "_ply", "ply"))
        if f.endswith(".py")]

    files = file_list(ctx.pkg, bld.src_root)
    builder.convert("bento_2to3", files)

    try:
        yaku.scheduler.run_tasks_parallel(bld, maxjobs=njobs)
    finally:
        bld.store()

@hooks.command
def run_2to3(ctx):
    """\
Purpose: run 2to3 on bento.
Usage: bentomaker run_2to3 [OPTIONS]"""
    msg = """\
===============================================================================
python 3 version of bento is now available in build/py3k. You can install bento
for python 3 as follows:

    cd %s
    python3 -m bentomakerlib.bentomaker configure # add custom options here
    python3 -m bentomakerlib.bentomaker install
===============================================================================\
""" % PY3K_BUILD
    pprint('BLUE', msg)

class TestPy3kCommand(Command):
    long_descr = """\
Purpose: test py3k output generated by 2to3
Usage:   bentomaker test_py3k [OPTIONS]."""
    short_descr = "check whether generated py3k code works."
    common_options = Command.common_options \
                        + [Option("-p", "--python-interpreter",
                                  help="python interpreter to use", dest="python_interpreter")]
    def run(self, ctx):
        argv = ctx.get_command_arguments()
        p = ctx.options_context.parser
        o, a = p.parse_args(argv)
        if o.help:
            p.print_help()
            return
        if o.python_interpreter:
            python_interpreter = o.python_interpreter
        else:
            python_interpreter = "python3"

        pprint("BLUE", "running distcheck on python 3 code...")
        p = subprocess.Popen([python_interpreter, "-m", "bentomakerlib.bentomaker", "distcheck"],
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=PY3K_BUILD)
        stdout, stderr = p.communicate()
        print(try_decode(stdout))
        if p.returncode != 0:
            pprint('RED', "error while distchecking python 3 code: exception was")
            pprint('RED', "=" * 79)
            pprint('RED', try_decode(stderr))
            pprint('RED', "=" * 79)
            return p.returncode

def try_decode(s):
    try:
        return s.decode()
    except UnicodeError:
        return s

class TestCommand(Command):
    def run(self, ctx):
        pprint('BLUE', "Running test command....")

        if sys.platform == "win32":
            suffix = ".exe"
        else:
            suffix = ""
        p = ctx.options_context.parser
        o, a = p.parse_args(ctx.get_command_arguments())
        if sys.version_info[:2] < (2, 5):
            cmd = [sys.executable, "-c", "'import nose; nose.core.main()'"]
            cmd = " ".join(cmd)
            shell = True
        else:
            cmd = [sys.executable, "-m", "nose.core"]
            if not test_program_run(cmd + ["-h"]):
                raise CommandExecutionFailure(
                        "program %r not found - needed to test bento !" % cmd[0])
            cmd.extend(["-s", "-v", "bento"])
            shell = False
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
        stdout, stderr = p.communicate()
        if p.returncode != 0:
            print(try_decode(stderr))
            return p.returncode
        else:
            for stream in [stdout, stderr]:
                print(try_decode(stream))

@hooks.startup
def startup(context):
    context.register_command("test", TestCommand)
    context.register_command("test_py3k", TestPy3kCommand)

    context.set_before("_run_2to3", "configure")
    context.set_before("run_2to3", "_run_2to3")
    context.set_before("test_py3k", "_run_2to3")
