from distutils.command.build_ext import build_ext
from distutils.errors import (
    CCompilerError,
    DistutilsExecError,
    DistutilsPlatformError,
)
from distutils.core import Extension
from os.path import (
    dirname,
    join,
    abspath,
    exists,
    basename,
    splitext,
)
from os import mkdir
from subprocess import run
from platform import system
from sys import exit

track = "../../patches/physics_girl_st.yml"

class BuildFailed(Exception):
    pass

class ExtBuilder(build_ext):

    def run(self):
        try:
            build_ext.run(self)
        except (DistutilsPlatformError, FileNotFoundError):
            raise BuildFailed('File not found. Could not compile C extension.')

    def build_extension(self, ext):
        try:
            build_ext.build_extension(self, ext)
        except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError):
            raise BuildFailed('Could not compile C extension.')

def build(setup_kwargs):
    # Make sure the build directory exists and setup the
    # relative paths correctly.
    cwd = abspath(".")
    print("Running from:", cwd)

    current_source_dir = abspath(dirname(__file__))
    project_source_dir = abspath(join(current_source_dir, "..", "..", ".."))
    current_binary_dir = join(current_source_dir, 'build')
    if not exists(current_binary_dir):
        mkdir(current_binary_dir)
    host_is_windows = system() == "Windows"
    executable_suffix = ".exe" if host_is_windows else ""
    object_suffix = ".obj" if host_is_windows else ".o"

    # Build the sointu compiler first.
    compiler_executable = join(current_binary_dir, "sointu-compile{}".format(executable_suffix))
    result = run(
        args=[
            "go", "build",
            "-o", compiler_executable,
            "cmd/sointu-compile/main.go",
        ],
        cwd=project_source_dir,
        shell=True if host_is_windows else False,
    )
    if result.returncode != 0:
        print("sointu-compile build process exited with:", result.returncode)
        print(result.stdout)
        exit(1)

    track_file_name = abspath(join(current_source_dir, track))
    (track_name_base, _) = splitext(basename(track_file_name)) 
    print("Compiling track:", track_file_name)

    # Compile the track.
    sointu_compiler_arch = "amd64"
    track_asm_file = join(current_binary_dir, '{}.asm'.format(track_name_base))
    result = run(
        args=[
            compiler_executable,
            "-o", track_asm_file,
            "-arch={}".format(sointu_compiler_arch),
            track_file_name,
        ],
    )
    if result.returncode != 0:
        print("sointu-compile process exited with:", result.returncode)
        print(result.stdout)
        exit(1)

    # Assemble the track.
    nasm_abi = "Win64" if host_is_windows else "Elf64"
    track_object_file = join(current_binary_dir, '{}{}'.format(track_name_base, object_suffix))
    print("Assembling track asm source:", track_asm_file)
    result = run(
        args=[
            'nasm',
            '-o', track_object_file,
            '-f', nasm_abi,
            track_asm_file,
        ],
    )
    if result.returncode != 0:
        print("nasm process exited with:", result.returncode)
        print(result.stdout)
        exit(1)
    
    # Export the plugin.
    print("Linking object file into Python extension module:", track_object_file)
    setup_kwargs.update({
        "ext_modules": [
            Extension(
                "sointu",
                include_dirs=[
                    current_binary_dir,
                    current_source_dir,
                ],
                sources=[
                    "sointu.c",
                ],
                extra_compile_args=[
                    "-DTRACK_HEADER=\"{}.h\"".format(track_name_base),
                ] + ([
                    "-DWIN32",
                ] if host_is_windows else [
                    "-DUNIX",
                    "-fPIC",
                ]),
                extra_objects=[
                    track_object_file,
                ],
                extra_link_args=[
                    "dsound.lib",
                    "ws2_32.lib",
                    "ucrt.lib",
                    "user32.lib",
                ] if host_is_windows else [
                    "-z", "noexecstack",
                    "--no-pie",
                    "-lasound",
                    "-lpthread",
                    "-lpython3.11",
                ],
            ),
        ],
        "cmdclass": {
            "build_ext": ExtBuilder,
        },
    })
