package llvmbackend

/*
#cgo CXXFLAGS: -std=c++17
#include <stdlib.h>
#include "bridge.h"
*/
import "C"

import (
	"fmt"
	"unsafe"
)

type OutputKind int

const (
	OutputObject OutputKind = iota
	OutputAssembly
)

type Options struct {
	TargetTriple    string
	CPU             string
	Features        string
	TargetABI       string
	OptLevel        string
	RelocationModel string
	CodeModel       string
	Verbose         bool
}

func Compile(input, output, optimizedIR string, kind OutputKind, options Options) error {
	inputCString := C.CString(input)
	defer C.free(unsafe.Pointer(inputCString))
	outputCString := C.CString(output)
	defer C.free(unsafe.Pointer(outputCString))
	optimizedIRCString := C.CString(optimizedIR)
	defer C.free(unsafe.Pointer(optimizedIRCString))
	targetCString := C.CString(options.TargetTriple)
	defer C.free(unsafe.Pointer(targetCString))
	cpuCString := C.CString(options.CPU)
	defer C.free(unsafe.Pointer(cpuCString))
	featuresCString := C.CString(options.Features)
	defer C.free(unsafe.Pointer(featuresCString))
	abiCString := C.CString(options.TargetABI)
	defer C.free(unsafe.Pointer(abiCString))
	optCString := C.CString(options.OptLevel)
	defer C.free(unsafe.Pointer(optCString))
	relocationCString := C.CString(options.RelocationModel)
	defer C.free(unsafe.Pointer(relocationCString))
	codeModelCString := C.CString(options.CodeModel)
	defer C.free(unsafe.Pointer(codeModelCString))

	var errorMessage *C.char
	result := C.qk_compile_llvm(
		inputCString,
		C.size_t(len(input)),
		outputCString,
		optimizedIRCString,
		targetCString,
		cpuCString,
		featuresCString,
		abiCString,
		optCString,
		relocationCString,
		codeModelCString,
		C.int(kind),
		C.int(boolToInt(options.Verbose)),
		&errorMessage,
	)
	if errorMessage != nil {
		defer C.qk_dispose_error(errorMessage)
	}
	if result != 0 {
		if errorMessage == nil {
			return fmt.Errorf("libLLVM compilation failed")
		}
		return fmt.Errorf("libLLVM compilation failed: %s", C.GoString(errorMessage))
	}
	return nil
}

func boolToInt(value bool) int {
	if value {
		return 1
	}
	return 0
}