package odin_parser

import "core:odin/tokenizer"
import "core:odin/ast"
import "core:path/filepath"
import "core:fmt"
import "core:os"
import "core:slice"

collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
	NO_POS :: tokenizer.Pos{};

	pkg_path, pkg_path_ok := filepath.abs(path);
	if !pkg_path_ok {
		return;
	}

	path_pattern := fmt.tprintf("%s/*.odin", pkg_path);
	matches, err := filepath.glob(path_pattern);
	defer delete(matches);

	if err != nil {
		return;
	}

	pkg = ast.new(ast.Package, NO_POS, NO_POS);
	pkg.fullpath = pkg_path;

	for match in matches {
		src: []byte;
		fullpath, ok := filepath.abs(match);
		if !ok {
			return;
		}
		src, ok = os.read_entire_file(fullpath);
		if !ok {
			delete(fullpath);
			return;
		}
		file := ast.new(ast.File, NO_POS, NO_POS);
		file.pkg = pkg;
		file.src = string(src);
		file.fullpath = fullpath;
		pkg.files[fullpath] = file;
	}

	success = true;
	return;
}

parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool {
	p := p;
	if p == nil {
		p = &Parser{};
		p^ = default_parser();
	}

	ok := true;

	files := make([]^ast.File, len(pkg.files), context.temp_allocator);
	i := 0;
	for _, file in pkg.files {
		files[i] = file;
		i += 1;
	}
	slice.sort(files);

	for file in files {
		if !parse_file(p, file) {
			ok = false;
		}
		if pkg.name == "" {
			pkg.name = file.pkg_decl.name;
		} else if pkg.name != file.pkg_decl.name {
			error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name);
		}
	}

	return ok;
}

parse_package_from_path :: proc(path: string, p: ^Parser = nil) -> (pkg: ^ast.Package, ok: bool) {
	pkg, ok = collect_package(path);
	if !ok {
		return;
	}
	ok = parse_package(pkg, p);
	return;
}
