Documentation
/// Wasvy: Wasi interface for the Bevy ecs
package wasvy:ecs;

/// This is the world that the Bevy host (the game/app) implements
/// to give ECS functionality to the WASM guest (the mod).
world host {
	import app;
}

/// This is the world that the WASM guest (the mod) implements.
///
/// This is the mandatory interface that a WASM component
/// must implement for it to be called from the Bevy host (the game/app).
world guest {
	import app;
	use app.{app};

	/// This method is called once on startup for each WASM component (Not Bevy component).
	///
	/// In this method you should register and configure `system`s via the `app` resource
	/// passed as a parameter.
	export setup: func(app: app);
}

interface app {
	resource serialize {
		constructor();

		get-type: func() -> string;
	}

	/// This is an interface (similar to bevy::App) through which mods may interact with the Bevy App.
	///
	/// To access this, make sure to import the 'guest' world and implement `setup`.
	resource app {
		/// Adds systems to the mod
		add-systems: func(
			schedule: schedule,
			systems: list<borrow<system>>,
		);
	}

	/// An interface with which to define a new system for the host.
	///
	/// Usage:
	/// 1. Construct a new system, giving it a unique name
	/// 2. Add system-params by calling 0 or more add-* methods
	/// 3. Order the system relative to others
	/// 4. Add the system to a schedule
	resource system {
		/// Constructs a new system. Use the same name as exported in
		/// the guest world, otherwise the host won't be able to find it.
		constructor(name: string);

		/// Adds a commands system-param
		add-commands: func();

		/// Adds a query system-param
		add-query: func(query: list<query-for>);

		/// Schedules this system be run after another system
		after: func(other: borrow<system>);

		/// Schedules this system be run before another system
		before: func(other: borrow<system>);
	}

	/// A `command` queue system param to perform structural changes to the world.
	///
	/// Since each command requires exclusive access to the world,
	/// all queued commands are automatically applied in sequence.
	///
	/// Each command can be used to modify the world in arbitrary ways:
	/// - spawning or despawning entities
	/// - inserting components on new or existing entities
	/// - etc.
	resource commands {
		/// Spawns a new empty `entity` and returns its corresponding `entity-commands`.
		spawn-empty: func() -> entity-commands;

		/// Spawns a new `entity` with the given components
		/// and returns the entity's corresponding `entity-commands`.
		spawn: func(bundle: bundle) -> entity-commands;

		/// Returns the `entity-commands` for the given `entity`.
		///
		/// This method does not guarantee that commands queued by the returned `entity-commands`
		/// will be successful, since the entity could be despawned before they are executed.
		entity: func(entity: borrow<entity>) -> entity-commands;
	}

	/// A list of commands that will be run to modify an `entity`.
	resource entity-commands {
		/// Returns the identifier for this entity
		id: func() -> entity;

		/// Adds a `bundle` of components to the entity.
		///
		/// This will overwrite any previous value(s) of the same component type.
		insert: func(bundle: bundle);

		/// Removes a Bundle of components from the entity if it exists.
		remove: func(bundle: bundle-types);

		/// Despawns the entity.
		///
		/// This will emit a warning if the entity does not exist.
		despawn: func();

		/// Despawns the entity.
		///
		/// Unlike `despawn`, this will not emit a warning if the entity does not exist.
		try-despawn: func();
	}

	/// An identifier for an entity.
	resource entity {}

	/// A query system param
	resource query {
		/// Evaluates and returns the next query results
		iter: func() -> option<query-result>;
	}

	/// A query system param
	resource query-result {
		/// Returns the entity id for the query
		entity: func() -> entity;

		/// Gets the component at the specified index. Order is the same as declared
		/// during setup. Query filters do not count as components.
		///
		/// So for example:
		///
		/// ```rust,ignore
		/// spin_cube.add_query(&[
		///   QueryFor::Mut("A"),     // component index 0
		///   QueryFor::With("B"),    // none
		///   QueryFor::Ref("C"),     // component index 1
		///   QueryFor::Without("D"), // none
		/// ]);
		/// ```
		component: func(index: component-index) -> component;
	}

	resource component {
		/// Gets the value of a component
		get: func() -> serialized-component;

		/// Sets the value of a component
		///
		/// Traps if this component was not declared as mutable
		set: func(value: serialized-component);

		/// Invokes a method on this component.
		///
		/// `params` must be a JSON array string. The return value is JSON.
		invoke: func(method: string, params: serialized-component) -> serialized-component;
	}

	/// A fully-qualified type name
	type type-path = string;

	/// JSON serialized version of the actual component that is being passed between WASM and Bevy.
	/// So for every instance of `component` make sure you deserialize it yourself to the struct that it actually is.
	///
	/// Note: for components returned by query::optional this is an option<t>
	type serialized-component = list<u8>;

	/// Just a simple list of tuples composed of the type-path and the serialized component string
	type bundle = list<tuple<type-path, serialized-component>>;

	/// A bundle without the serialized components
	type bundle-types = list<type-path>;

	/// Each query supports up to 255 components
	type component-index = u8;

	variant schedule {
		/// A custom schedule that runs the first time a mod is loaded.
		///
		/// Note this is not the same as Bevy's Startup schedule
		mod-startup,

		/// Runs during the Bevy PreUpdate schedule
		pre-update,

		/// Runs during the Bevy Update schedule
		update,

		/// Runs during the Bevy PostUpdate schedule
		post-update,

		/// Runs during the Bevy FixedPreUpdate schedule
		fixed-pre-update,

		/// Runs during the Bevy FixedUpdate schedule
		fixed-update,

		/// Runs during the Bevy FixedPostUpdate schedule
		fixed-post-update,

		/// Runs during the Bevy Update schedule
		custom(string),
	}

	variant query-for {
		ref(type-path),
		mut(type-path),
		%with(type-path),
		without(type-path),
	}
}