import type { Chunk, DataType, Scalar, TypedArray } from "../metadata.js";

export type Indices = [start: number, stop: number, step: number];

export interface Slice {
	start: number | null;
	stop: number | null;
	step: number | null;
}

export type Projection =
	| { from: null; to: number }
	| { from: number; to: null }
	| {
			from: Indices;
			to: Indices;
	  };

export type Prepare<D extends DataType, NdArray extends Chunk<D>> = (
	data: TypedArray<D>,
	shape: number[],
	stride: number[],
) => NdArray;

export type SetScalar<D extends DataType, NdArray extends Chunk<D>> = (
	target: NdArray,
	selection: (Indices | number)[],
	value: Scalar<D>,
) => void;

export type SetFromChunk<D extends DataType, NdArray extends Chunk<D>> = (
	a: NdArray,
	b: NdArray,
	proj: Projection[],
) => void;

export type Setter<D extends DataType, Arr extends Chunk<D>> = {
	prepare: Prepare<D, Arr>;
	setFromChunk: SetFromChunk<D, Arr>;
	setScalar: SetScalar<D, Arr>;
};

export type Options = {
	createQueue?: () => ChunkQueue;
	useSharedArrayBuffer?: boolean;
	signal?: AbortSignal;
	/**
	 * @deprecated Pass `signal` directly on the options object.
	 * Kept for one major version of back-compat; if both are set, the
	 * signals are merged via `AbortSignal.any`.
	 */
	opts?: { signal?: AbortSignal };
};

export type GetOptions = Options;

export type SetOptions = Options;

// Compatible with https://github.com/sindresorhus/p-queue
export type ChunkQueue = {
	add(fn: () => Promise<void>): void;
	onIdle(): Promise<Array<void>>;
};
