package main

TypeKind :: enum {
	Integer,
	Float,
	Pointer,
	Array,
	Struct,
}

StructType :: struct {
	name: [dynamic]u8,
}

Type :: struct {
	kind:        TypeKind,
	bit_size:    u8,
	is_signed:   bool,
	pointer_to:  ^Type,
	array_of:    ^Type,
	array_size:  u64,
	struct_type: ^StructType,
}

FunctionType :: struct {
	name:            [dynamic]u8,
	return_type:     ^Type,
	parameter_types: [dynamic]^Type,
}

compare_types :: proc(a: ^Type, b: ^Type) -> (ret: bool, cast_required: bool) {
	cast_required = false

	if (a.kind == .Integer || a.kind == .Float) && (a.bit_size > b.bit_size) {
		ret = true
		cast_required = true
		return
	}

	ret = a != nil && b != nil && a.kind == b.kind && a.bit_size == b.bit_size && a.is_signed == b.is_signed
	if ret == false {
		return
	}

	if a.kind == .Pointer {
		ret, _ = compare_types(a.pointer_to, b.pointer_to)
	} else if a.kind == .Array {
		ret, _ = compare_types(a.array_of, b.array_of)
		ret = a.array_size == b.array_size && ret
	}
	return
}

compare_function_types :: proc(a: ^FunctionType, b: ^FunctionType) -> (ret: bool) {
	ok, cast_ := compare_types(a.return_type, b.return_type)
	ret = a != nil && b != nil && (ok && !cast_)
	if ret {
		for &arg, i in a.parameter_types {
			ok, cast_ := compare_types(arg, b.parameter_types[i])
			if !(ok && !cast_) {
				ret = false
				break
			}
		}
	}
	return
}

function_type_create :: proc() -> (ret: ^FunctionType) {
	ret = new(FunctionType)
	return
}

type_create_integer :: proc(bit_size: u8, signed: bool) -> (ret: ^Type) {
	ret = new(Type)
	ret.kind = .Integer
	ret.bit_size = bit_size
	ret.is_signed = signed
	return
}

type_create_float :: proc(bit_size: u8) -> (ret: ^Type) {
	ret = new(Type)
	ret.kind = .Float
	ret.bit_size = bit_size
	ret.is_signed = true
	return
}

type_create_pointer :: proc(to: ^Type) -> (ret: ^Type) {
	ret = new(Type)
	ret.kind = .Pointer
	ret.pointer_to = to
	return
}

type_create_array :: proc(of: ^Type, size: u64) -> (ret: ^Type) {
	ret = new(Type)
	ret.kind = .Array
	ret.array_of = of
	ret.array_size = size
	return
}

type_create_struct :: proc(name: [dynamic]u8) -> (ret: ^Type) {
	ret = new(Type)
	ret.kind = .Struct
	ret.struct_type = new(StructType)
	ret.struct_type.name = name
	return
}
