#!/usr/bin/env python
"""Script for generating CMake configuration scripts, e.g. gcc_debug.sh"""

import os
from dataclasses import dataclass
from typing import List


TEMPLATE = """#! /bin/bash

../tools/clean_build.sh 2>/dev/null

# Should be enabled for H3C only
#set_env add CMAKE_PREFIX_PATH /hpc/software/intel/oneapi/mkl/2023.1.0
#set_env add CMAKE_MODULE_PATH /hpc/software/intel/oneapi/mkl/2023.1.0/lib/cmake/mkl

# Add -DMKL_THREADING=intel_thread to enforce OpenMP threading if needed.

cmake .. \\
"""


@dataclass
class BuildConfig:
    """
    Attributes should map to CMake options one-by-one.

    Default settings (DO NOT CHANGE THEM):
        gcc
        g++
        Release mode
        (Y) shared libs
        (Y) examples
        (N) tests
        (Y) Python interface
        (Y) openmp
        (N) MPI
        (N) CUDA
        (N) FEAST
        (N) HDF5
        default eigen_backend
        default diag_backend
        default tbpm_backend
    """
    cmake_c_compiler: str = "gcc"
    cmake_cxx_compiler: str = "g++"
    cmake_build_type: str = "Release"
    build_shared_libs: bool = True
    build_examples: bool = True
    build_tests: bool = False
    build_python_interface: bool = True
    with_openmp: bool = True
    with_mpi: bool = False
    with_cuda: bool = False
    with_feast: bool = False
    with_hdf5: bool = False
    eigen_backend: str = "default"
    diag_backend: str = "default"
    tbpm_backend: str = "default"

    def set_compiler_vendor(self, vendor: str = "gnu") -> None:
        if vendor == "intel":
            self.cmake_c_compiler = "icx"
            self.cmake_cxx_compiler = "icpx"
        elif vendor == "clang":
            self.cmake_c_compiler: str = "clang"
            self.cmake_cxx_compiler: str = "clang++"
        else:
            self.cmake_c_compiler: str = "gcc"
            self.cmake_cxx_compiler: str = "g++"

    def set_backend(self, *flags: List[str]) -> None:
        self.eigen_backend = flags[0]
        self.diag_backend = flags[1]
        self.tbpm_backend = flags[2]

    def to_cmd(self) -> str:
        b2s = lambda flag: "on" if flag else "off"
        cmd = TEMPLATE
        counter = 0
        for k, v in self.__dict__.items():
            s = b2s(v) if isinstance(v, bool) else v
            if counter < len(self.__dict__) - 1:
                cmd += f"-D{k.upper()}={s} \\\n"
            else:
                cmd += f"-D{k.upper()}={s}\n"
            counter += 1
        if self.with_cuda:
            cmd = cmd.rstrip("\n") + " -DCUDA_ARCHITECTURES=61\n"
        return cmd

    def write_sh(self, file_name: str) -> None:
        with open(f"{file_name}.sh", "w") as out_file:
            out_file.writelines(self.to_cmd())
        os.system(f"chmod +x {file_name}.sh")

    def run_cmake(self) -> None:
        os.system(self.to_cmd())


def main() -> None:
    config = BuildConfig()
    config.cmake_build_type = "Debug"
    config.build_tests = True
    config.write_sh("gcc_debug")
    config.set_compiler_vendor("intel")
    config.write_sh("icc_debug")
    config.set_compiler_vendor("clang")
    config.write_sh("clang_debug")

    config = BuildConfig()
    config.cmake_build_type = "Release"
    config.write_sh("gcc_release")
    config.set_compiler_vendor("intel")
    config.write_sh("icc_release")
    config.set_compiler_vendor("clang")
    config.write_sh("clang_release")


if __name__ == "__main__":
    main()