import { Subject } from 'rxjs';
import { Type } from './constants.js';
import { Opaque } from 'type-fest';

/**
 * Used to refer to the index of an entity in a {@link World}.
 */
type EntityIndex = Opaque<number, "EntityIndex">;
/**
 * Used to refer to the string id of an entity (independent from a {@link World}).
 */
type EntityID = Opaque<string, "EntityID">;
/**
 * Used to define the schema of a {@link Component}.
 * Uses {@link Type} enum to be able to access the component type in JavaScript as well as have TypeScript type checks.
 */
type Schema = {
    [key: string]: Type;
};
/**
 * Used to add arbitrary metadata to components.
 * (Eg `contractId` for components that have a corresponding solecs component contract.)
 */
type Metadata = {
    [key: string]: unknown;
} | undefined;
/**
 * Mapping between JavaScript {@link Type} enum and corresponding TypeScript type.
 */
type ValueType<T = undefined> = {
    [Type.Boolean]: boolean;
    [Type.Number]: number;
    [Type.String]: string;
    [Type.NumberArray]: number[];
    [Type.StringArray]: string[];
    [Type.Entity]: EntityID;
    [Type.EntityArray]: EntityID[];
    [Type.OptionalNumber]: number | undefined;
    [Type.OptionalString]: string | undefined;
    [Type.OptionalNumberArray]: number[] | undefined;
    [Type.OptionalStringArray]: string[] | undefined;
    [Type.OptionalEntity]: EntityID | undefined;
    [Type.OptionalEntityArray]: EntityID[] | undefined;
    [Type.T]: T;
    [Type.OptionalT]: T | undefined;
};
/**
 * Used to infer the TypeScript type of a component value corresponding to a given {@link Schema}.
 */
type ComponentValue<S extends Schema = Schema, T = undefined> = {
    [key in keyof S]: ValueType<T>[S[key]];
};
/**
 * Type of a component update corresponding to a given {@link Schema}.
 */
type ComponentUpdate<S extends Schema = Schema, T = undefined> = {
    entity: EntityIndex;
    value: [ComponentValue<S, T> | undefined, ComponentValue<S, T> | undefined];
    component: Component<S, Metadata, T>;
};
/**
 * Type of component returned by {@link defineComponent}.
 */
interface Component<S extends Schema = Schema, M extends Metadata = Metadata, T = undefined> {
    id: string;
    values: {
        [key in keyof S]: Map<EntityIndex, ValueType<T>[S[key]]>;
    };
    schema: S;
    metadata: M;
    entities: () => IterableIterator<EntityIndex>;
    world: World;
    update$: Subject<ComponentUpdate<S, T>> & {
        observers: any;
    };
}
type Override<S extends Schema, T = undefined> = {
    entity: EntityIndex;
    value: Partial<ComponentValue<S, T>> | null;
};
/**
 * Type of indexer returned by {@link createIndexer}.
 */
type Indexer<S extends Schema, M extends Metadata = Metadata, T = undefined> = Component<S, M, T> & {
    getEntitiesWithValue: (value: ComponentValue<S, T>) => Set<EntityIndex>;
};
type Components = {
    [key: string]: Component;
};
interface ComponentWithStream<S extends Schema, T = undefined> extends Component<S, Metadata, T> {
    stream$: Subject<{
        entity: EntityIndex;
        value: ComponentValue<S, T> | undefined;
    }>;
}
type AnyComponentValue = ComponentValue<Schema>;
type AnyComponent = Component<Schema>;
/**
 * Type of World returned by {@link createWorld}.
 */
type World = {
    registerEntity: (options?: {
        id?: EntityID;
        idSuffix?: string;
    }) => EntityIndex;
    registerComponent: (component: Component) => void;
    components: Component[];
    entities: EntityID[];
    entityToIndex: Map<EntityID, EntityIndex>;
    getEntityIndexStrict: (entity: EntityID) => EntityIndex;
    dispose: () => void;
    registerDisposer: (disposer: () => void) => void;
    hasEntity: (entity: EntityID) => boolean;
};
declare enum QueryFragmentType {
    Has = 0,
    HasValue = 1,
    Not = 2,
    NotValue = 3,
    ProxyRead = 4,
    ProxyExpand = 5
}
type HasQueryFragment<T extends Schema> = {
    type: QueryFragmentType.Has;
    component: Component<T>;
};
type HasValueQueryFragment<T extends Schema> = {
    type: QueryFragmentType.HasValue;
    component: Component<T>;
    value: Partial<ComponentValue<T>>;
};
type NotQueryFragment<T extends Schema> = {
    type: QueryFragmentType.Not;
    component: Component<T>;
};
type NotValueQueryFragment<T extends Schema> = {
    type: QueryFragmentType.NotValue;
    component: Component<T>;
    value: Partial<ComponentValue<T>>;
};
type ProxyReadQueryFragment = {
    type: QueryFragmentType.ProxyRead;
    component: Component<{
        value: Type.Entity;
    }>;
    depth: number;
};
type ProxyExpandQueryFragment = {
    type: QueryFragmentType.ProxyExpand;
    component: Component<{
        value: Type.Entity;
    }>;
    depth: number;
};
type QueryFragment<T extends Schema = Schema> = HasQueryFragment<T> | HasValueQueryFragment<T> | NotQueryFragment<T> | NotValueQueryFragment<T> | ProxyReadQueryFragment | ProxyExpandQueryFragment;
type EntityQueryFragment<T extends Schema = Schema> = HasQueryFragment<T> | HasValueQueryFragment<T> | NotQueryFragment<T> | NotValueQueryFragment<T>;
type SettingQueryFragment = ProxyReadQueryFragment | ProxyExpandQueryFragment;
type QueryFragments = QueryFragment<Schema>[];
type SchemaOf<C extends Component<Schema>> = C extends Component<infer S> ? S : never;
type OptionalType = Type.OptionalNumber | Type.OptionalString | Type.OptionalEntity | Type.OptionalNumberArray | Type.OptionalStringArray | Type.OptionalEntityArray;
declare function isOptionalType(t: Type): t is OptionalType;
type ArrayType = Type.NumberArray | Type.OptionalNumberArray | Type.StringArray | Type.OptionalStringArray | Type.EntityArray | Type.OptionalEntityArray;
declare function isArrayType(t: Type): t is ArrayType;
type NumberType = Type.Number | Type.OptionalNumber;
declare function isNumberType(t: Type): t is NumberType;
type EntityType = Type.Entity | Type.OptionalEntity;
declare function isEntityType(t: Type): t is EntityType;
type Layer = {
    world: World;
    components: Record<string, Component<Schema>>;
};
type Layers = Record<string, Layer>;

export { type AnyComponent, type AnyComponentValue, type ArrayType, type Component, type ComponentUpdate, type ComponentValue, type ComponentWithStream, type Components, type EntityID, type EntityIndex, type EntityQueryFragment, type EntityType, type HasQueryFragment, type HasValueQueryFragment, type Indexer, type Layer, type Layers, type Metadata, type NotQueryFragment, type NotValueQueryFragment, type NumberType, type OptionalType, type Override, type ProxyExpandQueryFragment, type ProxyReadQueryFragment, type QueryFragment, QueryFragmentType, type QueryFragments, type Schema, type SchemaOf, type SettingQueryFragment, type ValueType, type World, isArrayType, isEntityType, isNumberType, isOptionalType };
