import { Schema, Metadata, World, Component, EntityIndex, ComponentValue, Indexer } from './types.js';
import 'rxjs';
import './constants.js';
import 'type-fest';

/**
 * Components contain state indexed by entities and are one of the fundamental building blocks in ECS.
 * Besides containing the state, components expose an rxjs update$ stream, that emits an event any time the value
 * of an entity in this component is updated.
 *
 * @param world {@link World} object this component should be registered onto.
 * @param schema {@link Schema} of component values. Uses Type enum as bridge between typescript types and javascript accessible values.
 * @param options Optional: {
 *    id: descriptive id for this component (otherwise an autogenerated id is used),
 *    metadata: arbitrary metadata (eg. contractId for solecs mapped components),
 *    indexed: if this flag is set, an indexer is applied to this component (see {@link createIndexer})
 * }
 * @returns Component object linked to the provided World
 *
 * @remarks
 * Components work with {@link EntityIndex}, not {@link EntityID}. Get the {@link EntityID} from a given {@link EntityIndex} via {@link World}.entities[EntityIndex].
 *
 * @example
 * ```
 * const Position = defineComponent(world, { x: Type.Number, y: Type.Number }, { id: "Position" });
 * ```
 */
declare function defineComponent<S extends Schema, M extends Metadata, T = undefined>(world: World, schema: S, options?: {
    id?: string;
    metadata?: M;
    indexed?: boolean;
}): Component<S, M, T>;
/**
 * Set the value for a given entity in a given component.
 *
 * @param component {@link defineComponent Component} to be updated.
 * @param entity {@link EntityIndex} of the entity whose value in the given component should be set.
 * @param value Value to set, schema must match the component schema.
 *
 * @example
 * ```
 * setComponent(Position, entity, { x: 1, y: 2 });
 * ```
 */
declare function setComponent<S extends Schema, T = undefined>(component: Component<S, Metadata, T>, entity: EntityIndex, value: ComponentValue<S, T>): void;
/**
 * Update the value for a given entity in a given component while keeping the old value of keys not included in the update.
 *
 * @param component {@link defineComponent Component} to be updated.
 * @param entity {@link EntityIndex} of the entity whose value in the given component should be updated.
 * @param value Partial value to be set, remaining keys will be taken from the existing component value.
 *
 * @remarks
 * This function fails silently during runtime if a partial value is set for an entity that
 * does not have a component value yet, since then a partial value will be set in the component for this entity.
 *
 * @example
 * ```
 * updateComponent(Position, entity, { x: 1 });
 * ```
 */
declare function updateComponent<S extends Schema, T = undefined>(component: Component<S, Metadata, T>, entity: EntityIndex, value: Partial<ComponentValue<S, T>>): void;
/**
 * Remove a given entity from a given component.
 *
 * @param component {@link defineComponent Component} to be updated.
 * @param entity {@link EntityIndex} of the entity whose value should be removed from this component.
 */
declare function removeComponent<S extends Schema, M extends Metadata, T>(component: Component<S, M, T>, entity: EntityIndex): void;
/**
 * Check whether a component contains a value for a given entity.
 *
 * @param component {@link defineComponent Component} to check whether it has a value for the given entity.
 * @param entity {@link EntityIndex} of the entity to check whether it has a value in the given component.
 * @returns true if the component contains a value for the given entity, else false.
 */
declare function hasComponent<S extends Schema, T = undefined>(component: Component<S, Metadata, T>, entity: EntityIndex): boolean;
/**
 * Get the value of a given entity in the given component.
 * Returns undefined if no value or only a partial value is found.
 *
 * @param component {@link defineComponent Component} to get the value from for the given entity.
 * @param entity {@link EntityIndex} of the entity to get the value for from the given component.
 * @returns Value of the given entity in the given component or undefined if no value exists.
 */
declare function getComponentValue<S extends Schema, T = undefined>(component: Component<S, Metadata, T>, entity: EntityIndex): ComponentValue<S, T> | undefined;
/**
 * Get the value of a given entity in the given component.
 * Throws an error if no value exists for the given entity in the given component.
 *
 * @param component {@link defineComponent Component} to get the value from for the given entity.
 * @param entity {@link EntityIndex} of the entity to get the value for from the given component.
 * @returns Value of the given entity in the given component.
 *
 * @remarks
 * Throws an error if no value exists in the component for the given entity.
 */
declare function getComponentValueStrict<S extends Schema, T = undefined>(component: Component<S, Metadata, T>, entity: EntityIndex): ComponentValue<S, T>;
/**
 * Compare two {@link ComponentValue}s.
 * `a` can be a partial component value, in which case only the keys present in `a` are compared to the corresponding keys in `b`.
 *
 * @param a Partial {@link ComponentValue} to compare to `b`
 * @param b Component value to compare `a` to.
 * @returns True if `a` equals `b` in the keys present in a or neither `a` nor `b` are defined, else false.
 *
 * @example
 * ```
 * componentValueEquals({ x: 1, y: 2 }, { x: 1, y: 3 }) // returns false because value of y doesn't match
 * componentValueEquals({ x: 1 }, { x: 1, y: 3 }) // returns true because x is equal and y is not present in a
 * ```
 */
declare function componentValueEquals<S extends Schema, T = undefined>(a?: Partial<ComponentValue<S, T>>, b?: ComponentValue<S, T>): boolean;
/**
 * Util to create a tuple of a component and value with matching schema.
 * (Used to enforce Typescript type safety.)
 *
 * @param component {@link defineComponent Component} with {@link ComponentSchema} `S`
 * @param value {@link ComponentValue} with {@link ComponentSchema} `S`
 * @returns Tuple `[component, value]`
 */
declare function withValue<S extends Schema, T = undefined>(component: Component<S, Metadata, T>, value: ComponentValue<S, T>): [Component<S, Metadata, T>, ComponentValue<S, T>];
/**
 * Get a set of entities that have the given component value in the given component.
 *
 * @param component {@link defineComponent Component} to get entities with the given value from.
 * @param value look for entities with this {@link ComponentValue}.
 * @returns Set with {@link EntityIndex EntityIndices} of the entities with the given component value.
 */
declare function getEntitiesWithValue<S extends Schema>(component: Component<S> | Indexer<S>, value: Partial<ComponentValue<S>>): Set<EntityIndex>;
/**
 * Get a set of all entities of the given component.
 *
 * @param component {@link defineComponent Component} to get all entities from
 * @returns Set of all entities in the given component.
 */
declare function getComponentEntities<S extends Schema, T = undefined>(component: Component<S, Metadata, T>): IterableIterator<EntityIndex>;
declare function clearLocalCache(component: Component, uniqueWorldIdentifier?: string): void;
declare function createLocalCache<S extends Schema, M extends Metadata, T = undefined>(component: Component<S, M, T>, uniqueWorldIdentifier?: string): Component<S, M, T>;

export { clearLocalCache, componentValueEquals, createLocalCache, defineComponent, getComponentEntities, getComponentValue, getComponentValueStrict, getEntitiesWithValue, hasComponent, removeComponent, setComponent, updateComponent, withValue };
