/*!
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
import { Signal } from './rvx.js';

/**
 * An object that is used to convert between reactive wrappers and their targets.
 */
interface Barrier {
	/**
	 * Get a reactive wrapper for the specified value.
	 *
	 * This should always return the same wrapper for the same value.
	 *
	 * @param value The target.
	 * @returns The wrapper or the value itself if it was already wrapped.
	 */
	wrap<T>(value: T): T;
	/**
	 * Get the target for the specified reactive wrapper.
	 *
	 * This should always return the same target for the same value.
	 *
	 * @param value The wrapper or a non-wrapped value.
	 * @returns The target or the value itself if it was already unwrapped.
	 */
	unwrap<T>(value: T): T;
}
interface WrapInstanceFn<T> {
	(instance: T): T;
}
/**
 * The default barrier using {@link wrap} and {@link unwrap}.
 */
declare const BARRIER: Barrier;
/**
 * Get a deep reactive wrapper for the specified value.
 *
 * This always returns the same wrapper for the same value.
 *
 * @param value The value to wrap.
 * @returns The wrapper or the value itself if it was already wrapped.
 */
declare function wrap<T>(value: T): T;
/**
 * Get the target for a reactive wrapper.
 *
 * This always returns the same target for the same value.
 *
 * @param value The value to unwrap.
 * @returns The target or the value itself if it was already unwrapped.
 */
declare function unwrap<T>(value: T): T;
/**
 * Allow instances of the specified target class to be wrapped.
 *
 * @param targetClass The target class.
 * @param wrap A function to wrap an instance. By default `createReactiveProxy` is used with `wrap` and `unwrap` for inner values.
 *
 * @example
 * ```tsx
 * class Example {
 *   static {
 *     // Using the default "createReactiveProxy":
 *     wrapInstancesOf(this);
 *
 *     // Or a custom wrapper:
 *     wrapInstancesOf(this, instance => {
 *       return createSomeWrapperFor(instance);
 *     });
 *   }
 * }
 * ```
 */
declare function wrapInstancesOf<T extends object>(targetClass: new (...args: any) => T, wrap?: (instance: T) => T): void;

/**
 * A signal for tracking accesses to values that may not exist.
 */
declare class ProbeSignal<T> extends Signal<T> {
	#private;
	/**
	 * Create a new probe signal.
	 *
	 * @param onDisposable A function to call when it is guaranteed that this signal is no longer watched.
	 * @param value The initial value.
	 */
	constructor(onDisposable: () => void, value: T);
	notify(): void;
}
/**
 * A map for tracking keyed accesses to values that may not exist.
 */
declare class ProbeMap<K, V> {
	#private;
	/**
	 * Create a new probe map.
	 *
	 * @param get A function to get the current value for a key at any time.
	 */
	constructor(get: (key: K) => V);
	/**
	 * Access the specified key.
	 *
	 * If an expression is currently evaluated to track signal accesses, this will create and access a probe signal for the specified key. That probe signal is automatically removed when it is guaranteed that this key is no longer watched.
	 */
	access(key: K): void;
	/**
	 * Update a key-value pair and notify potential observers.
	 */
	update(key: K, value: V): void;
	/**
	 * Set the value of all existing probe signals in this map.
	 */
	fill(value: V): void;
}

declare function createReactiveArrayProxy<T>(target: T[], barrier: Barrier): T[];

/**
 * A reactive wrapper for a map.
 */
declare class ReactiveMap<K, V> extends Map<K, V> {
	#private;
	/**
	 * Create a new wrapper.
	 *
	 * @param target The target.
	 * @param barrier The barrier to convert values. Keys are not reactive.
	 */
	constructor(target: Map<K, V>, barrier: Barrier);
	get size(): number;
	get(key: K): V | undefined;
	has(key: K): boolean;
	set(key: K, value: V): this;
	delete(key: K): boolean;
	clear(): void;
	entries(): MapIterator<[K, V]>;
	keys(): MapIterator<K>;
	values(): MapIterator<V>;
	forEach(callback: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void;
	[Symbol.iterator](): MapIterator<[K, V]>;
	get [Symbol.toStringTag](): string;
}

/**
 * Create a reactive proxy for an arbitrary object.
 *
 * @param target The target.
 * @param barrier The barrier to convert values.
 * @returns The proxy.
 */
declare function createReactiveProxy<T extends object>(target: T, barrier: Barrier): T;

/**
 * A reactive wrapper for a set.
 */
declare class ReactiveSet<T> extends Set<T> {
	#private;
	/**
	 * Create a new wrapper.
	 *
	 * @param target The target.
	 * @param barrier The barrier to convert values.
	 */
	constructor(target: Set<T>, barrier: Barrier);
	get size(): number;
	has(value: T): boolean;
	add(value: T): this;
	delete(value: T): boolean;
	clear(): void;
	entries(): SetIterator<[T, T]>;
	keys(): SetIterator<T>;
	values(): SetIterator<T>;
	forEach(callback: (value: T, value2: T, set: Set<T>) => void, thisArg?: unknown): void;
	[Symbol.iterator](): SetIterator<T>;
	get [Symbol.toStringTag](): string;
}

/**
 * Create a signal that reflects a property of an arbitrary object.
 *
 * @param target The target object.
 * @param key The property key.
 * @returns The signal.
 */
declare function reflect<T, K extends keyof T>(target: T, key: K): Signal<T[K]>;

export { BARRIER, ProbeMap, ProbeSignal, ReactiveMap, ReactiveSet, createReactiveArrayProxy, createReactiveProxy, reflect, unwrap, wrap, wrapInstancesOf };
export type { Barrier, WrapInstanceFn };
