""" The doit file. Because makefiles are old and hard to read.
    Can't promise this will be more readable, but at least it wont be old :)
"""

import os
from enum import Enum
from types import SimpleNamespace
from typing import Iterator
from pathlib import Path

from doit.tools import Interactive

class BoardTarget(Enum):
    RPI3B = 1
    RPI4 = 2

## Target
BOARD_TARGET = BoardTarget.RPI3B

## Common constants
consts = SimpleNamespace()
consts.WORK_DIR = os.getcwd()
# Rust equivalent to -Werror
consts.RUSTC_FLAGS = "-D warnings "
# Force documentation
consts.RUSTC_FLAGS += "-D missing_docs"
consts.CARGO_COMPILE_FLAGS = "--release"

if BOARD_TARGET == BoardTarget.RPI3B:
    consts.TARGET = "aarch64-unknown-none-softfloat"
    consts.KERNEL_NAME = "kernel8.img"
    consts.QEMU_CMD = "qemu-system-aarch64"
    consts.QEMU_MACHINE = "raspi3b"
    consts.QEMU_ARGS = "-serial stdio -display none"
    consts.OBJDUMP_CMD = "aarch64-none-elf-objdump"
    consts.OBJDUMP_ARGS = "-D"
    consts.NM_CMD = "aarch64-none-elf-nm"
    consts.READELF_CMD = "aarch64-none-elf-readelf"
    consts.LD_SCRIPT_PATH = f"{consts.WORK_DIR}/linker.ld"
    consts.RUSTC_FLAGS = f"-C target-cpu=cortex-a53 -C link-arg=--script={consts.LD_SCRIPT_PATH} {consts.RUSTC_FLAGS}"
    consts.CARGO_COMPILE_FLAGS = f"--features target_rpi3 {consts.CARGO_COMPILE_FLAGS}"
elif BOARD_TARGET == BoardTarget.RPI4:
    consts.TARGET = "aarch64-unknown-none-softfloat"
    consts.KERNEL_NAME = "kernel8.img"
    consts.QEMU_CMD = "qemu-system-aarch64"
    consts.QEMU_MACHINE = None
    consts.QEMU_ARGS = "-serial stdio -display none"
    consts.OBJDUMP_CMD = "aarch64-none-elf-objdump"
    consts.OBJDUMP_ARGS = "-D"
    consts.NM_CMD = "aarch64-none-elf-nm"
    consts.READELF_CMD = "aarch64-none-elf-readelf"
    consts.LD_SCRIPT_PATH = f"{consts.WORK_DIR}/linker.ld"
    consts.RUSTC_FLAGS = f"-C target-cpu=cortex-a72 -C link-arg=--script={consts.LD_SCRIPT_PATH} {consts.RUSTC_FLAGS}"
    consts.CARGO_COMPILE_FLAGS = f"--features target_rpi4 {consts.CARGO_COMPILE_FLAGS}"

DOIT_CONFIG = {
    'default_tasks': ['build'],
}

def get_rust_files() -> Iterator[str]:
    """ Get a list of the rust source files.
    """
    return map(lambda p: str(p.resolve()), Path('./src').glob('**/*.rs'))

def get_asm_files() -> Iterator[str]:
    """ Get a list of the assembly source files.
    """
    return map(lambda p: str(p.resolve()), Path('./src').glob('**/*.s'))

def get_build_dep() -> list[str]:
    """ Get a list of the files impacting the binary.
    """
    # TODO: add dodo.py to the list?
    return [
        consts.LD_SCRIPT_PATH, 
        *get_rust_files(), 
        *get_asm_files(),
    ]

def task_install_dep():
    """ Install some required tools.
    """
    return {
        'actions': [
            'cargo install cargo-binutils',
            f'rustup target add {consts.TARGET}',
            'rustup component add llvm-tools-preview',
        ],
    }

def task_check():
    """ Run a compiler check on the code.
    """
    task = {
        'file_dep': get_build_dep(),
        'actions': [f'RUSTFLAGS="{consts.RUSTC_FLAGS}" cargo check --target={consts.TARGET} {consts.CARGO_COMPILE_FLAGS}'],
    }
    print(task)
    return task

def task_compile():
    """ Compile the sources.
    """
    task = {
        'file_dep': get_build_dep(),
        'targets': [f'{consts.WORK_DIR}/target/{consts.TARGET}/release/kernel'],
        'actions': [f'RUSTFLAGS="{consts.RUSTC_FLAGS}" cargo rustc --target={consts.TARGET} {consts.CARGO_COMPILE_FLAGS}'],
        'clean': [f'cargo clean']
    }
    print(task)
    return task
    
def task_build():
    """ Strip the binary from ELF format to raw binary.
    """
    return {
        'file_dep': [f'{consts.WORK_DIR}/target/{consts.TARGET}/release/kernel'],
        'targets': [f'{consts.WORK_DIR}/consts.KERNEL_NAME'],
        'actions': [
            f'rust-objcopy --strip-all -O binary {consts.WORK_DIR}/target/{consts.TARGET}/release/kernel {consts.WORK_DIR}/{consts.KERNEL_NAME}',
        ],
        'clean': True,
    }

def task_qemu():
    """ Run the kernel in qemu.
    """
    if consts.QEMU_MACHINE is None:
        raise Exception("qemu does not support emulation for this board yet")
    r = {
        'file_dep': [f'{consts.WORK_DIR}/{consts.KERNEL_NAME}'],
        'uptodate': [False], # Always run the cmd
        'actions': [Interactive(f'{consts.QEMU_CMD} -M {consts.QEMU_MACHINE} {consts.QEMU_ARGS} -kernel {consts.KERNEL_NAME}')],
    }
    print(r)
    return r

def task_objdump():
    """ Inspect the ELF binary file.
    """
    r = {
        'file_dep': [f'{consts.WORK_DIR}/target/{consts.TARGET}/release/kernel'],
        'uptodate': [False], # Always run the cmd
        'actions': [Interactive(f'{consts.OBJDUMP_CMD} {consts.OBJDUMP_ARGS} {consts.WORK_DIR}/target/{consts.TARGET}/release/kernel')]
    }
    print(r)
    return r
