import std::io;
import std::io::path;
import std::io::file;
import std::os::process;
import std::collections::object;
import std::encoding::json;
import std::collections::list;
import conf;


struct VirtualMachineDesc {
	Path config_path;
	Object* config;
	String name;
	StrList cmdline;
	Path disk_path;
	SubProcess process;
}


alias VMList = list::List{VirtualMachineDesc};


fn VirtualMachineDesc? new_from_path(Allocator allocator, Path path)
{
	VirtualMachineDesc desc;
	desc.config_path = path::new(allocator, path.str_view(), path.env)!;

	File file = file::open_path(path, "r")!;
	defer (void)file.close();

	desc.config = json::parse(allocator, &file)!;
	desc.name = desc.config.get_string("name")!;
	desc.disk_path = desc.config.get_string("disk").to_expanded_path(allocator)!;

	desc.cmdline = conf::get_qemu_cmdline(allocator, desc.config)!;

	return desc;
}


<* @param[&in] desc *>
fn bool VirtualMachineDesc.is_initialized(&desc)
{
	return desc.config_path.str_view() != ""
	       && desc.config != null
		   && desc.name != "" 
		   && desc.disk_path.str_view() != ""
		   && desc.cmdline.len() != 0;
}


<* @param[&inout] desc *>
fn void VirtualMachineDesc.free(&desc)
{
	if (!desc.is_initialized()) return;
	(void)desc.stop();
	desc.config_path.free();
	desc.config.free();
	// desc.name.free() name is just a string view into desc.config
	desc.disk_path.free();
	desc.cmdline.free();

	*desc = {};
}


<* @param[&inout] desc *>
fn void? VirtualMachineDesc.start(&desc)
{
	if (!desc.is_initialized()) return;
	if ((desc.process.is_running() ?? false)) return;

	desc.process = process::create(desc.cmdline.array_view(), {.inherit_stdio=true, .inherit_environment=true})!;
}


<* @param[&inout] desc *>
fn void? VirtualMachineDesc.stop(&desc)
{
	if (!desc.is_initialized()) return;
	if (!(desc.process.is_running() ?? false)) return;

	desc.process.terminate()!;
	desc.process.join()!;
}


<* @param[&in] desc *>
fn bool VirtualMachineDesc.is_running(&desc)
{
	if (!desc.is_initialized()) return false;
	return desc.process.is_running() ?? false;
}
