#!/usr/bin/env python3
# Apache License, Version 2.0

import argparse
import os
import subprocess
import sys
import tempfile


def render_file(filepath):
    command = (
        BLENDER,
        "--background",
        "-noaudio",
        "--factory-startup",
        filepath,
        "-E", "CYCLES",
        "-o", TEMP_FILE_MASK,
        "-F", "PNG",
        "-f", "1",
        )
    try:
        output = subprocess.check_output(command)
        if VERBOSE:
            print(output.decode("utf-8"))
        return None
    except subprocess.CalledProcessError as e:
        if os.path.exists(TEMP_FILE):
            os.remove(TEMP_FILE)
        if VERBOSE:
            print(e.output.decode("utf-8"))
        if b"Error: engine not found" in e.output:
            return "NO_CYCLES"
        elif b"blender probably wont start" in e.output:
            return "NO_START"
        return "CRASH"
    except:
        if os.path.exists(TEMP_FILE):
            os.remove(TEMP_FILE)
        if VERBOSE:
            print(e.output.decode("utf-8"))
        return "CRASH"


def test_get_name(filepath):
    filename = os.path.basename(filepath)
    return os.path.splitext(filename)[0]


def verify_output(filepath):
    testname = test_get_name(filepath)
    dirpath = os.path.dirname(filepath)
    reference_dirpath = os.path.join(dirpath, "reference_renders")
    reference_image = os.path.join(reference_dirpath, testname + ".png")
    if not os.path.exists(reference_image):
        return False
    command = (
        IDIFF,
        "-fail", "0.01",
        "-failpercent", "1",
        reference_image,
        TEMP_FILE,
        )
    try:
        subprocess.check_output(command)
        return True
    except subprocess.CalledProcessError as e:
        if VERBOSE:
            print(e.output.decode("utf-8"))
        return e.returncode == 1


def run_test(filepath):
    testname = test_get_name(filepath)
    spacer = "." * (32 - len(testname))
    print(testname, spacer, end="")
    sys.stdout.flush()
    error = render_file(filepath)
    if not error:
        if verify_output(filepath):
            print("PASS")
        else:
            error = "VERIFY"
    if error:
        print("FAIL", error)
    return error


def blend_list(path):
    for dirpath, dirnames, filenames in os.walk(path):
        for filename in filenames:
            if filename.lower().endswith(".blend"):
                filepath = os.path.join(dirpath, filename)
                yield filepath


def run_all_tests(dirpath):
    failed_tests = []
    all_files = list(blend_list(dirpath))
    all_files.sort()
    for filepath in all_files:
        error = run_test(filepath)
        if error:
            if error == "NO_CYCLES":
                print("Can't perform tests because Cycles failed to load!")
                return False
            elif error == "NO_START":
                print('Can not perform tests because blender fails to start.',
                      'Make sure INSTALL target was run.')
                return False
            elif error == 'VERIFY':
                pass
            else:
                print("Unknown error %r" % error)
            testname = test_get_name(filepath)
            failed_tests.append(testname)
    if failed_tests:
        failed_tests.sort()
        print("\n\nFAILED tests:")
        for test in failed_tests:
            print("   ", test)
        return False
    return True


def create_argparse():
    parser = argparse.ArgumentParser()
    parser.add_argument("-blender", nargs="+")
    parser.add_argument("-testdir", nargs=1)
    parser.add_argument("-idiff", nargs=1)
    return parser


def main():
    parser = create_argparse()
    args = parser.parse_args()

    global BLENDER, ROOT, IDIFF
    global TEMP_FILE, TEMP_FILE_MASK, TEST_SCRIPT
    global VERBOSE

    BLENDER = args.blender[0]
    ROOT = args.testdir[0]
    IDIFF = args.idiff[0]

    TEMP = tempfile.mkdtemp()
    TEMP_FILE_MASK = os.path.join(TEMP, "test")
    TEMP_FILE = TEMP_FILE_MASK + "0001.png"

    TEST_SCRIPT = os.path.join(os.path.dirname(__file__), "runtime_check.py")

    VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None

    ok = run_all_tests(ROOT)

    # Cleanup temp files and folders
    if os.path.exists(TEMP_FILE):
        os.remove(TEMP_FILE)
    os.rmdir(TEMP)

    sys.exit(not ok)


if __name__ == "__main__":
    main()
