import { ObservableSet } from 'mobx';
import { Observable } from 'rxjs';
import { Type, UpdateType } from './constants.js';
import { Schema, Component, HasQueryFragment, NotQueryFragment, ComponentValue, HasValueQueryFragment, NotValueQueryFragment, ProxyReadQueryFragment, ProxyExpandQueryFragment, EntityIndex, QueryFragment, ComponentUpdate } from './types.js';
import 'type-fest';

/**
 * Create a {@link HasQueryFragment}.
 *
 * @remarks
 * The {@link HasQueryFragment} filters for entities that have the given component,
 * independent from the component value.
 *
 * @example
 * Query for all entities with a `Position`.
 * ```
 * runQuery([Has(Position)]);
 * ```
 *
 * @param component Component this query fragment refers to.
 * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.
 */
declare function Has<T extends Schema>(component: Component<T>): HasQueryFragment<T>;
/**
 * Create a {@link NotQueryFragment}.
 *
 * @remarks
 * The {@link NotQueryFragment} filters for entities that don't have the given component,
 * independent from the component value.
 *
 * @example
 * Query for all entities with a `Position` that are not `Movable`.
 * ```
 * runQuery([Has(Position), Not(Movable)]);
 * ```
 *
 * @param component Component this query fragment refers to.
 * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.
 */
declare function Not<T extends Schema>(component: Component<T>): NotQueryFragment<T>;
/**
 * Create a {@link HasValueQueryFragment}.
 *
 * @remarks
 * The {@link HasValueQueryFragment} filters for entities that have the given component
 * with the given component value.
 *
 * @example
 * Query for all entities at Position (0,0).
 * ```
 * runQuery([HasValue(Position, { x: 0, y: 0 })]);
 * ```
 *
 * @param component Component this query fragment refers to.
 * @param value Only include entities with this (partial) component value from the result.
 * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.
 */
declare function HasValue<T extends Schema>(component: Component<T>, value: Partial<ComponentValue<T>>): HasValueQueryFragment<T>;
/**
 * Create a {@link NotValueQueryFragment}.
 *
 * @remarks
 * The {@link NotValueQueryFragment} filters for entities that don't have the given component
 * with the given component value.
 *
 * @example
 * Query for all entities that have a `Position`, except for those at `Position` (0,0).
 * ```
 * runQuery([Has(Position), NotValue(Position, { x: 0, y: 0 })]);
 * ```
 *
 * @param component Component this query fragment refers to.
 * @param value Exclude entities with this (partial) component value from the result.
 * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.
 */
declare function NotValue<T extends Schema>(component: Component<T>, value: Partial<ComponentValue<T>>): NotValueQueryFragment<T>;
/**
 * Create a {@link ProxyReadQueryFragment}.
 *
 * @remarks
 * The {@link ProxyReadQueryFragment} activates the "proxy read mode" for the rest of the query.
 * This means that for all remaining fragments in the query not only the entities themselves are checked, but also
 * their "ancestors" up to the given `depth` on the relationship chain defined by the given `component`.
 *
 * @example
 * Query for all entities that have a `Position` and are (directly or indirectly) owned by an entity with `Name` "Alice".
 * ```
 * runQuery([Has(Position), ProxyRead(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: "Alice" })]);
 * ```
 *
 * @param component Component this query fragment refers to.
 * @param depth Max depth in the relationship chain to traverse.
 * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.
 */
declare function ProxyRead(component: Component<{
    value: Type.Entity;
}>, depth: number): ProxyReadQueryFragment;
/**
 * Create a {@link ProxyExpandQueryFragment}.
 *
 * @remarks
 * The {@link ProxyExpandQueryFragment} activates the "proxy expand mode" for the rest of the query.
 * This means that for all remaining fragments in the query not only the matching entities themselves are included in the intermediate set,
 * but also all their "children" down to the given `depth` on the relationship chain defined by the given `component`.
 *
 * @example
 * Query for all entities (directly or indirectly) owned by an entity with `Name` "Alice".
 * ```
 * runQuery([ProxyExpand(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: "Alice" })]);
 * ```
 *
 * @param component Component to apply this query fragment to.
 * @param depth Max depth in the relationship chain to traverse.
 * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.
 */
declare function ProxyExpand(component: Component<{
    value: Type.Entity;
}>, depth: number): ProxyExpandQueryFragment;
/**
 * Recursively compute all direct and indirect child entities up to the specified depth
 * down the relationship chain defined by the given component.
 *
 * @param entity Entity to get all child entities for up to the specified depth
 * @param component Component to use for the relationship chain.
 * @param depth Depth up to which the recursion should be applied.
 * @returns Set of entities that are child entities of the given entity via the given component.
 */
declare function getChildEntities(entity: EntityIndex, component: Component<{
    value: Type.Entity;
}>, depth: number): Set<EntityIndex>;
/**
 * Execute a list of query fragments to receive a Set of matching entities.
 *
 * @remarks
 * The query fragments are executed from left to right and are concatenated with a logical `AND`.
 * For performance reasons, the most restrictive query fragment should be first in the list of query fragments,
 * in order to reduce the number of entities the next query fragment needs to be checked for.
 * If no proxy fragments are used, every entity in the resulting set passes every query fragment.
 * If setting fragments are used, the order of the query fragments influences the result, since settings only apply to
 * fragments after the setting fragment.
 *
 * @param fragments Query fragments to execute.
 * @param initialSet Optional: provide a Set of entities to execute the query on. If none is given, all existing entities are used for the query.
 * @returns Set of entities matching the query fragments.
 */
declare function runQuery(fragments: QueryFragment[], initialSet?: Set<EntityIndex>): Set<EntityIndex>;
/**
 * Create a query object including an update$ stream and a Set of entities currently matching the query.
 *
 * @remarks
 * `update$` stream needs to be subscribed to in order for the logic inside the stream to be executed and therefore
 * in order for the `matching` set to be updated.
 *
 * `defineQuery` should be strongly preferred over `runQuery` if the query is used for systems or other
 * use cases that repeatedly require the query result or updates to the query result. `defineQuery` does not
 * reevaluate the entire query if an accessed component changes, but only performs the minimal set of checks
 * on the updated entity to evaluate wether the entity still matches the query, resulting in significant performance
 * advantages over `runQuery`.
 *
 * The query fragments are executed from left to right and are concatenated with a logical `AND`.
 * For performance reasons, the most restrictive query fragment should be first in the list of query fragments,
 * in order to reduce the number of entities the next query fragment needs to be checked for.
 * If no proxy fragments are used, every entity in the resulting set passes every query fragment.
 * If setting fragments are used, the order of the query fragments influences the result, since settings only apply to
 * fragments after the setting fragment.
 *
 * @param fragments Query fragments to execute.
 * @param options Optional: {
 *   runOnInit: if true, the query is executed once with `runQuery` to build an iniital Set of matching entities. If false only updates after the query was created are considered.
 *   initialSet: if given, this set is passed to `runOnInit` when building the initial Set of matching entities.
 * }
 * @returns Query object: {
 *  update$: RxJS stream of updates to the query result. The update contains the component update that caused the query update, as well as the {@link UpdateType update type}.
 *  matching: Mobx observable set of entities currently matching the query.
 * }.
 */
declare function defineQuery(fragments: QueryFragment[], options?: {
    runOnInit?: boolean;
    initialSet?: Set<EntityIndex>;
}): {
    update$: Observable<ComponentUpdate & {
        type: UpdateType;
    }>;
    matching: ObservableSet<EntityIndex>;
};
/**
 * Define a query object that only passes update events of type {@link UpdateType}.Update to the `update$` stream.
 * See {@link defineQuery} for details.
 *
 * @param fragments Query fragments
 * @returns Stream of component updates of entities that had already matched the query
 */
declare function defineUpdateQuery(fragments: QueryFragment[], options?: {
    runOnInit?: boolean;
}): Observable<ComponentUpdate & {
    type: UpdateType;
}>;
/**
 * Define a query object that only passes update events of type {@link UpdateType}.Enter to the `update$` stream.
 * See {@link defineQuery} for details.
 *
 * @param fragments Query fragments
 * @returns Stream of component updates of entities matching the query for the first time
 */
declare function defineEnterQuery(fragments: QueryFragment[], options?: {
    runOnInit?: boolean;
}): Observable<ComponentUpdate>;
/**
 * Define a query object that only passes update events of type {@link UpdateType}.Exit to the `update$` stream.
 * See {@link defineQuery} for details.
 *
 * @param fragments Query fragments
 * @returns Stream of component updates of entities not matching the query anymore for the first time
 */
declare function defineExitQuery(fragments: QueryFragment[], options?: {
    runOnInit?: boolean;
}): Observable<ComponentUpdate>;

export { Has, HasValue, Not, NotValue, ProxyExpand, ProxyRead, defineEnterQuery, defineExitQuery, defineQuery, defineUpdateQuery, getChildEntities, runQuery };
