#pragma once
// Copyright (c) 2019-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.

#include "../utils/lib.h"
#include "../version.h"
#include "../compiler/enums.h"
#include <stdint.h>

#define MAX_SYMTAB_SIZE (1024 * 1024)
#define MAX_BUILD_LIB_DIRS 1024
#define MAX_COMMAND_LINE_FILES 4096
#define MAX_COMMAND_LINE_RUN_ARGS 2048
#define MAX_THREADS 0xFFFF
#define DEFAULT_SYMTAB_SIZE (256 * 1024)
#define DEFAULT_SWITCHRANGE_MAX_SIZE (256)
#define DEFAULT_SWITCH_JUMP_MAX_SIZE (0x3FFF)
#define DEFAULT_PATH "."


typedef enum
{
	COMMAND_MISSING = 0,
	COMMAND_COMPILE,
	COMMAND_COMPILE_ONLY,
	COMMAND_COMPILE_BENCHMARK,
	COMMAND_COMPILE_TEST,
	COMMAND_INIT,
	COMMAND_INIT_LIB,
	COMMAND_BUILD,
	COMMAND_COMPILE_RUN,
	COMMAND_STATIC_LIB,
	COMMAND_DYNAMIC_LIB,
	COMMAND_RUN,
	COMMAND_CLEAN_RUN,
	COMMAND_CLEAN,
	COMMAND_VENDOR_FETCH,
	COMMAND_DIST,
	COMMAND_BENCH,
	COMMAND_BENCHMARK,
	COMMAND_TEST,
	COMMAND_UNIT_TEST,
	COMMAND_PRINT_SYNTAX,
	COMMAND_PROJECT,
	COMMAND_FETCH_SDK,
} CompilerCommand;

typedef enum
{
	SUBCOMMAND_MISSING = 0,
	SUBCOMMAND_VIEW,
	SUBCOMMAND_ADD,
	SUBCOMMAND_FETCH
} ProjectSubcommand;

typedef enum
{
	DIAG_NONE = 0,     // Don't use!
	DIAG_WARNING_TYPE, // Don't use!
	DIAG_UNUSED,
	DIAG_UNUSED_PARAMETER,
	DIAG_UNUSED_FUNCTION,
	DIAG_UNUSED_VARIABLE,
	DIAG_UNUSED_IMPORT,
	DIAG_UNUSED_MODULE,
	DIAG_UNUSED_LABEL,
	DIAG_UNUSED_PUBLIC,
	DIAG_UNUSED_TYPE,
	DIAG_CONVERSION,
	DIAG_COVERED_SWITCH_DEFAULT,
	DIAG_ERROR_TYPE, // Don't use this!
	DIAG_DUPLICATE_ATTRIBUTE,
	DIAG_NOT_IN_ENUM,
	DIAG_MISSING_CASE,
	DIAG_INT_TO_POINTER_CAST,
	DIAG_END_SENTINEL
} DiagnosticsType;



#define ANY_WINDOWS_ARCH_OS WINDOWS_AARCH64: case WINDOWS_X64: case MINGW_X64

typedef enum
{
	TARGET_TYPE_EXECUTABLE,
	TARGET_TYPE_STATIC_LIB,
	TARGET_TYPE_DYNAMIC_LIB,
	TARGET_TYPE_OBJECT_FILES,
	TARGET_TYPE_BENCHMARK,
	TARGET_TYPE_TEST,
	TARGET_TYPE_PREPARE,
} TargetType;

typedef struct
{
	WarningLevel deprecation;
	WarningLevel methods_not_resolved;
	WarningLevel dead_code;
	WarningLevel method_visibility;
	WarningLevel builtin;
} Warnings;

typedef enum
{
	PROJECT_VIEW_TYPE_AUTHOR,
	PROJECT_VIEW_TYPE_VERSION,
	PROJECT_VIEW_TYPE_LANGUAGE_REVISION,
	PROJECT_VIEW_TYPE_WARNINGS_USED,
	PROJECT_VIEW_TYPE_C3L_LIB_SEARCH_PATHS,
	PROJECT_VIEW_TYPE_C3L_LIB_DEPENDENCIES,
	PROJECT_VIEW_TYPE_SOURCE_PATHS,
	PROJECT_VIEW_TYPE_OUTPUT_LOCATION,
	PROJECT_VIEW_TYPE_DEFAULT_OPTIMIZATION,
	PROJECT_VIEW_TYPE_TARGETS,
	PROJECT_VIEW_TYPE_MAX = PROJECT_VIEW_TYPE_TARGETS
} ProjectViewType;

static const char *project_view_flags[PROJECT_VIEW_TYPE_MAX + 1] = {
	[PROJECT_VIEW_TYPE_AUTHOR] = "authors",
	[PROJECT_VIEW_TYPE_VERSION] = "version",
	[PROJECT_VIEW_TYPE_LANGUAGE_REVISION] = "language-revision",
	[PROJECT_VIEW_TYPE_WARNINGS_USED] = "warnings-used",
	[PROJECT_VIEW_TYPE_C3L_LIB_SEARCH_PATHS] = "c3l-lib-search-paths",
	[PROJECT_VIEW_TYPE_C3L_LIB_DEPENDENCIES] = "c3l-lib-dependencies",
	[PROJECT_VIEW_TYPE_SOURCE_PATHS] = "source-paths",
	[PROJECT_VIEW_TYPE_OUTPUT_LOCATION] = "output-location",
	[PROJECT_VIEW_TYPE_DEFAULT_OPTIMIZATION] = "default-optimization",
	[PROJECT_VIEW_TYPE_TARGETS] = "targets",
};

static const char *targets[7] = {
	[TARGET_TYPE_EXECUTABLE] = "executable",
	[TARGET_TYPE_STATIC_LIB] = "static-lib",
	[TARGET_TYPE_DYNAMIC_LIB] = "dynamic-lib",
	[TARGET_TYPE_BENCHMARK] = "benchmark",
	[TARGET_TYPE_TEST] = "test",
	[TARGET_TYPE_OBJECT_FILES] = "object-files",
	[TARGET_TYPE_PREPARE] = "prepare",
};
static const char *target_desc[7] = {
	[TARGET_TYPE_EXECUTABLE] = "Executable",
	[TARGET_TYPE_STATIC_LIB] = "Static library",
	[TARGET_TYPE_DYNAMIC_LIB] = "Dynamic library",
	[TARGET_TYPE_BENCHMARK] = "benchmark suite",
	[TARGET_TYPE_TEST] = "test suite",
	[TARGET_TYPE_OBJECT_FILES] = "object files",
	[TARGET_TYPE_PREPARE] = "prepare"

};


typedef struct BuildOptions_
{
	const char *lib_dir[MAX_BUILD_LIB_DIRS];
	size_t lib_dir_count;
	const char *libs[MAX_BUILD_LIB_DIRS];
	size_t lib_count;
	const char* linker_args[MAX_BUILD_LIB_DIRS];
	size_t linker_arg_count;
	const char* linker_lib_dir[MAX_BUILD_LIB_DIRS];
	size_t linker_lib_dir_count;
	const char* linker_libs[MAX_BUILD_LIB_DIRS];
	size_t linker_lib_count;
	const char *std_lib_dir;
	const char *run_dir;
	struct
	{
		const char *sdk;
		const char *def;
		const char *vs_dirs;
		WinCrtLinking crt_linking;
	} win;
	struct
	{
		const char *sysroot;
		const char *min_version;
		const char *sdk_version;
	} macos;
	struct
	{
		const char *crt;
		const char *crtbegin;
	} linuxpaths;
	struct
	{
		const char *ndk_path;
		int api_version;
	} android;
	int build_threads;
	const char *echo_prefix;
	const char **libraries_to_fetch;
	const char **files;
	const char *test_filter;
	const char **args;
	const char **feature_names;
	const char **removed_feature_names;
	const char *output_name;
	const char *runner_output_name;
	const char *project_name;
	const char *target_select;
	const char *path;
	const char *vendor_download_path;
	const char *template;
	const char **unchecked_directories;
	LinkerType linker_type;
	ValidationLevel validation_level;
	TestLogLevel test_log_level;
	Ansi ansi;
	bool test_breakpoint;
	bool test_quiet;
	bool test_nosort;
	bool test_noleak;
	bool test_show_output;
	bool benchmark_csv_report;
	bool print_large_functions;
	const char *custom_linker_path;
	uint32_t symtab_size;
	unsigned version;
	Warnings warnings;
	CompilerBackend backend;
	CompilerCommand command;
	struct
	{
		ProjectSubcommand command;
		const char *target_name;
		TargetType target_type;
		const char **sources;

		/* Support for flags for 'view' */
		struct
		{
			uint16_t flags_bitvector;
			bool verbose;
		} view_modifier;
	} project_options;
	CompileOption compile_option;
	TrustLevel trust_level;
	DiagnosticsSeverity severity[DIAG_END_SENTINEL];
	OptimizationSetting optsetting;
	DebugInfo debug_info_override;
	ShowBacktrace show_backtrace;
	ArchOsTarget arch_os_target_override;
	SafetyLevel safety_level;
	PanicLevel panic_level;
	SingleModule single_module;
	UnrollLoops unroll_loops;
	MergeFunctions merge_functions;
	AutoVectorization loop_vectorization;
	AutoVectorization slp_vectorization;
	bool emit_llvm;
	bool emit_asm;
	bool benchmark_mode;
	bool test_mode;
	bool lsp_mode;
	bool no_entry;
	bool no_obj;
	bool no_headers;
	bool read_stdin;
	bool print_output;
	bool print_input;
	bool run_once;
	bool suppress_run;
	bool fetch_accept_license;
	bool msvc_show_versions;
	const char *msvc_version_override;
	const char *msvc_sdk_version_override;
	const char *fetch_sdk_target;
	bool old_slice_copy;
	bool old_enums;
	bool old_compact_eq;
	int verbosity_level;
	const char *panicfn;
	const char *benchfn;
	const char *testfn;
	const char *cc;
	const char *build_dir;
	const char *output_dir;
	const char *llvm_out;
	const char *asm_out;
	const char *header_out;
	const char *obj_out;
	const char *script_dir;
	const char **emit_only;
	RelocModel reloc_model;
	X86VectorCapability x86_vector_capability;
	X86CpuSet x86_cpu_set;
	RiscvCpuSet riscv_cpu_set;
	Win64Simd win_64_simd;
	WinDebug win_debug;
	FpOpt fp_math;
	EmitStdlib emit_stdlib;
	UseStdlib use_stdlib;
	CustomLibc custom_libc;
	LinkLibc link_libc;
	StripUnused strip_unused;
	OptimizationLevel optlevel;
	SizeOptimizationLevel optsize;
	RiscvAbi riscv_abi;
	LinuxLibc linux_libc;
	MemoryEnvironment memory_environment;
	SanitizeMode sanitize_mode;
	uint32_t max_vector_size;
	uint32_t max_stack_object_size;
	const char *cpu_flags;
	uint32_t max_macro_iterations;
	bool print_keywords;
	bool print_attributes;
	bool print_builtins;
	bool print_operators;
	bool print_type_properties;
	bool print_project_properties;
	bool print_manifest_properties;
	bool print_precedence;
	bool print_linking;
	bool print_env;
	bool print_asm;
	bool benchmarking;
	bool testing;
} BuildOptions;

typedef struct
{
	const char *author;
	const char *email;
} AuthorEntry;

typedef struct
{
	struct Library__ *parent;
	ArchOsTarget arch_os;
	const char *cc;
	const char *cflags;
	WinCrtLinking win_crt;
	const char **source_dirs;
	const char **csource_dirs;
	const char **csources;
	const char **cinclude_dirs;
	const char **execs;
	const char **link_flags;
	const char **linked_libs;
	const char **dependencies;
} LibraryTarget;

typedef struct Library__
{
	const char *dir;
	const char *provides;
	const char *linklib_dir;
	const char **dependencies;
	const char **execs;
	const char *cc;
	const char *cflags;
	const char **source_dirs;
	const char **csource_dirs;
	const char **cinclude_dirs;
	WinCrtLinking win_crt;
	LibraryTarget *target_used;
	LibraryTarget **targets;
} Library;

typedef struct
{
	TargetType type;
	Library **library_list;
	LibraryTarget **ccompiling_libraries;
	const char *name;
	const char *runner_output_name;
	const char *output_name;
	const char *extension;
	const char *version;
	const char *langrev;
	const char **source_dirs;
	const char **test_source_dirs;
	const char **sources;
	const char **object_files;
	const char **libdirs;
	const char **libs;
	const char **linker_libdirs;
	const char **linker_libs;
	const char *cpu;
	const char *echo_prefix;
	const char **link_args;
	const char *build_dir;
	const char *object_file_dir;
	const char *output_dir;
	const char *ir_file_dir;
	const char *asm_file_dir;
	const char *header_file_dir;
	const char *script_dir;
	const char *run_dir;
	const char **emit_only;
	bool is_non_project;
	bool run_after_compile;
	bool delete_after_run;
	bool generate_benchmark_runner;
	bool generate_test_runner;
	bool benchmark_output;
	bool test_output;
	bool lsp_output;
	bool output_headers;
	bool output_ast;
	bool lex_only;
	bool parse_only;
	bool check_only;
	bool emit_llvm;
	bool emit_asm;
	bool emit_object_files;
	bool benchmarking;
	bool testing;
	bool silent;
	bool quiet;
	bool read_stdin;
	bool print_output;
	bool print_input;
	bool print_linking;
	bool no_entry;
	bool kernel_build;
	bool print_stats;
	bool old_slice_copy;
	bool old_enums;
	bool old_compact_eq;
	bool single_threaded;
	bool print_large_functions;
	int build_threads;
	TrustLevel trust_level;
	OptimizationSetting optsetting;
	OptimizationLevel optlevel;
	MemoryEnvironment memory_environment;
	SizeOptimizationLevel optsize;
	SingleModule single_module;
	ValidationLevel validation_level;
	UseStdlib use_stdlib;
	EmitStdlib emit_stdlib;
	LinkLibc link_libc;
	CustomLibc custom_libc;
	ShowBacktrace show_backtrace;
	StripUnused strip_unused;
	DebugInfo debug_info;
	MergeFunctions merge_functions;
	UnrollLoops unroll_loops;
	AutoVectorization loop_vectorization;
	AutoVectorization slp_vectorization;
	RelocModel reloc_model;
	ArchOsTarget arch_os_target;
	CompilerBackend backend;
	LinkerType linker_type;
	const char *cpu_flags;
	uint32_t symtab_size;
	uint32_t max_vector_size;
	uint32_t max_stack_object_size;
	uint32_t max_macro_iterations;
	uint32_t switchrange_max_size;
	uint32_t switchjump_max_size;
	const char **args;
	const char *panicfn;
	const char *benchfn;
	const char *testfn;
	const char *cc;
	const char *cflags;
	const char **csource_dirs;
	const char **csources;
	const char **cinclude_dirs;
	const char **exec;
	AuthorEntry *authors;
	const char **feature_list;
	const char *custom_linker_path;
	Warnings warnings;
	struct
	{
		WinDebug win_debug;
		SoftFloat soft_float : 3;
		StructReturn x86_struct_return : 3;
		X86VectorCapability x86_vector_capability : 4;
		RiscvAbi riscv_abi : 4;
		RiscvCpuSet riscv_cpu_set : 4;
		Win64Simd pass_win64_simd_as_arrays : 3;
		bool trap_on_wrap : 1;
		bool sanitize_address : 1;
		bool sanitize_memory : 1;
		bool sanitize_thread : 1;
		FpOpt fp_math;
		SafetyLevel safe_mode;
		PanicLevel panic_level;
		X86CpuSet x86_cpu_set;
	} feature;
	struct
	{
		const char *sysroot;
		const char *min_version;
		const char *sdk_version;
		MacSDK *sdk;
	} macos;
	struct
	{
		const char *sdk;
		const char *def;
		const char *vs_dirs;
		WinCrtLinking crt_linking;
		bool use_win_subsystem;
	} win;
	struct
	{
		LinuxLibc libc;
		const char *crt;
		const char *crtbegin;
	} linuxpaths;
	struct
	{
		const char *ndk_path;
		int api_version;
	} android;
} BuildTarget;

static const char *x86_cpu_set[8] = {
	[X86CPU_BASELINE] = "baseline", // NOLINT
	[X86CPU_SSSE3] = "ssse3",
	[X86CPU_SSE4] = "sse4",
	[X86CPU_AVX1] = "avx1",
	[X86CPU_AVX2_V1] = "avx2-v1",
	[X86CPU_AVX2_V2] = "avx2-v2",
	[X86CPU_AVX512] = "avx512",
	[X86CPU_NATIVE] = "native"
};

static const char *riscv_cpu_set[5] = {
	[RISCV_CPU_RVI] = "rvi", // NOLINT
	[RISCV_CPU_RVIMAC] = "rvimac",
	[RISCV_CPU_RVIMAFC] = "rvimafc",
	[RISCV_CPU_RVGC] = "rvgc",
	[RISCV_CPU_RVGCV] = "rvgcv",
};

static BuildTarget default_build_target = {
		.is_non_project = true,
		.optlevel = OPTIMIZATION_NOT_SET,
		.optsetting = OPT_SETTING_NOT_SET,
		.memory_environment = MEMORY_ENV_NORMAL,
		.optsize = SIZE_OPTIMIZATION_NOT_SET,
		.arch_os_target = ARCH_OS_TARGET_DEFAULT,
		.debug_info = DEBUG_INFO_NOT_SET,
		.show_backtrace = SHOW_BACKTRACE_NOT_SET,
		.use_stdlib = USE_STDLIB_NOT_SET,
		.link_libc = LINK_LIBC_NOT_SET,
		.emit_stdlib = EMIT_STDLIB_NOT_SET,
		.custom_libc = CUSTOM_LIBC_NOT_SET,
		.linker_type = LINKER_TYPE_NOT_SET,
		.validation_level = VALIDATION_NOT_SET,
		.single_module = SINGLE_MODULE_NOT_SET,
		.unroll_loops = UNROLL_LOOPS_NOT_SET,
		.merge_functions = MERGE_FUNCTIONS_NOT_SET,
		.slp_vectorization = VECTORIZATION_NOT_SET,
		.loop_vectorization = VECTORIZATION_NOT_SET,
		.strip_unused = STRIP_UNUSED_NOT_SET,
		.symtab_size = DEFAULT_SYMTAB_SIZE,
		.reloc_model = RELOC_DEFAULT,
		.cc = NULL,
		.extension = NULL,
		.version = "1.0.0",
		.langrev = "1",
		.cpu = "generic",
		.type = TARGET_TYPE_EXECUTABLE,
		.feature.x86_struct_return = STRUCT_RETURN_DEFAULT,
		.feature.soft_float = SOFT_FLOAT_DEFAULT,
		.feature.fp_math = FP_DEFAULT,
		.feature.trap_on_wrap = false,
		.feature.riscv_abi = RISCV_ABI_DEFAULT,
		.feature.x86_vector_capability = X86VECTOR_DEFAULT,
		.feature.x86_cpu_set = X86CPU_DEFAULT,
		.feature.riscv_cpu_set = RISCV_CPU_DEFAULT,
		.feature.win_debug = WIN_DEBUG_DEFAULT,
		.feature.safe_mode = SAFETY_NOT_SET,
		.feature.panic_level = PANIC_NOT_SET,
		.win.crt_linking = WIN_CRT_DEFAULT,
		.win.def = NULL,
		.linuxpaths.libc = LINUX_LIBC_NOT_SET,
		.switchrange_max_size = DEFAULT_SWITCHRANGE_MAX_SIZE,
		.switchjump_max_size = DEFAULT_SWITCH_JUMP_MAX_SIZE,
		.quiet = false,
};


extern const char *project_default_keys[][2];
extern const int project_default_keys_count;
extern const char *project_target_keys[][2];
extern const int project_target_keys_count;
extern const char *manifest_default_keys[][2];
extern const int manifest_default_keys_count;
extern const char *manifest_target_keys[][2];
extern const int manifest_target_keys_count;
extern const char *arch_os_target[ARCH_OS_TARGET_LAST + 1];
extern LinuxLibc default_libc;

void fetch_winsdk(BuildOptions *options);
void fetch_sdk(BuildOptions *options);
char *fetch_android_ndk(BuildOptions *options);
BuildOptions parse_arguments(int argc, const char *argv[]);
ArchOsTarget arch_os_target_from_string(const char *target);
bool command_accepts_files(CompilerCommand command);
bool command_passes_args(CompilerCommand command);
void update_build_target_with_opt_level(BuildTarget *target,
	OptimizationSetting level);
const char *check_dir(const char *path);
void create_project(BuildOptions *build_options);
void create_library(BuildOptions *build_options);
void resolve_libraries(BuildTarget *build_target);
void view_project(BuildOptions *build_options);
void add_target_project(BuildOptions *build_options);
void fetch_project(BuildOptions* options);
void print_build_env(void);
