import { Derivable, ReactorOptions } from '@politie/sherlock';
/**
 * The base interface for DerivableProxies. Defines only the $-properties and $-methods. Any property accessed with a number or
 * that doesn't start with a $-sign returns a new DerivableProxy.
 */
export interface DerivableProxy<V> {
    /** The current value that this Proxy represents. Can be expensive to calculate and is often writable. */
    $value: V;
    /** A string representation of this proxy's path from the root ProxyDescriptor. */
    $expression?: string;
    /**
     * An array representation of this proxy's path from the root ProxyDescriptor. Useful for programatically walking down the root
     * Descriptor's object tree to reacquire a target proxy.
     */
    $path?: Array<string | number>;
    /** {@see Derivable#and} */
    $and<W>(other: MaybePacked<W>): Derivable<V | W>;
    /** {@see Derivable#or} */
    $or<W>(other: MaybePacked<W>): Derivable<V | W>;
    /** {@see Derivable#not} */
    $not(): Derivable<boolean>;
    /** {@see Derivable#is} */
    $is(other: MaybePacked<any>): Derivable<boolean>;
    /** {@see Derivable#derive} */
    $derive<R>(f: (v: V) => R): Derivable<R>;
    $derive<R, P1>(f: (v: V, p1: P1) => R, p1: MaybePacked<P1>): Derivable<R>;
    $derive<R, P1, P2>(f: (v: V, p1: P1, p2: P2) => R, p1: MaybePacked<P1>, p2: MaybePacked<P2>): Derivable<R>;
    $derive<R, P>(f: (v: V, ...ps: P[]) => R, ...ps: Array<MaybePacked<P>>): Derivable<R>;
    /** {@see Derivable#react} */
    $react(reaction: (value: V, stop: () => void) => void, options?: Partial<ReactorOptions<V>>): () => void;
}
/**
 * Returns whether obj is a DerivableProxy.
 *
 * @param obj the object to test
 */
export declare function isDerivableProxy(obj: any): obj is DerivableProxy<any>;
/**
 * A ProxyDescriptor must be used to create DerivableProxies. It can be used in two ways, either create a new descriptor and
 * change any implementation details (if needed) or create a subclass to extend the behavior. Use the {@link #$create} method
 * to create a DerivableProxy.
 *
 * Note that `this` in methods points to the created proxy, so only methods and properties that start with a $-sign can be accessed
 * without trouble.
 *
 * Note also that properties that start with two $-signs are cleared on $create.
 */
export declare class ProxyDescriptor<V = any, T = V> {
    /**
     * The target derivable (the input to the proxy and the {@link #$create} method). The actual values that can be seen by methods
     * on the Proxy can be influenced by providing a {@link #$lens}.
     */
    $target: Derivable<T>;
    /**
     * The expression that represents the path to the current Proxy.
     */
    $expression?: string;
    /**
     * The path to the current Proxy.
     */
    $path?: Array<string | number>;
    /**
     * The derivable that is the input to all default methods on the Proxy and the {@link #$value} property.
     */
    get $derivable(): Derivable<V>;
    private $$derivable?;
    /**
     * The current value of the DerivableProxy. Can be expensive to calculate. When the target is settable (is an Atom) then $value
     * is writable.
     */
    get $value(): V;
    set $value(newValue: V);
    /**
     * The current value of the target Derivable that was used to create the DerivableProxy.
     */
    get $targetValue(): T;
    set $targetValue(newValue: T);
    /**
     * In methods of a ProxyDescriptor, `this` is bound to the Proxy Object. Therefore, only $-properties and $-methods can be
     * accessed safely. Use $proxyDescriptor to get access to the ProxyDescriptor Object to prevent the ProxyHandler from messing
     * with your logic.
     */
    protected get $proxyDescriptor(): this;
    /**
     * An optional method that can return an optional lens to this proxy. Is used to transform the values before accessed by the
     * consumer of the Proxy (either through $value or one of the other methods).
     */
    $lens?(): DerivableProxyLens<T, V> | undefined;
    /**
     * Wrap a Derivable as DerivableProxy using this ProxyDescriptor.
     *
     * @param obj the object to wrap
     * @param expression the new expression to the created DerivableProxy
     * @param path the new path to the created DerivableProxy
     */
    $create(obj: Derivable<T>, expression?: string, path?: Array<string | number>): DerivableProxy<V>;
    /**
     * The $pluck method is the implementation of the pluck mechanism of DerivableProxy. Replace this method to change the
     * pluck behavior. It should return a DerivableProxy.
     *
     * @param prop the property to pluck of the wrapped derivable
     */
    $pluck(prop: string | number): DerivableProxy<V>;
    /**
     * The $pluckableKeys returns a list of properties that can be plucked from this object. Returned keys are guaranteed to
     * result in a usable DerivableProxy when used with $pluck. Is used for `for ... in` and `Object.keys(...)` logic.
     */
    $pluckableKeys(): (string | symbol)[];
    /**
     * Method that determines whether the current object is iterable and if so, how many elements it contains. During iteration
     * {@link #pluck} is called with indices up to but not including the result of `$length()`.
     */
    $length(): number | undefined;
    $and(other: MaybePacked<any>): Derivable<any>;
    $or(other: MaybePacked<any>): Derivable<any>;
    $not(): Derivable<boolean>;
    $is(other: MaybePacked<any>): Derivable<boolean>;
    $derive(): Derivable<unknown>;
    $react(reaction: (value: V, stop: () => void) => void, options?: Partial<ReactorOptions<any>>): () => void;
    toJSON(): V;
    get [Symbol.toStringTag](): string;
    [Symbol.iterator](): IterableIterator<DerivableProxy<V>>;
    get length(): number | undefined;
}
export interface DerivableProxyLens<T, V> {
    get: (targetValue: T) => V;
    set?: (newValue: V, targetValue: T | undefined) => T;
}
export type MaybePacked<T> = T | Derivable<T> | DerivableProxy<T>;
export declare function unwrapProxy<W>(obj: MaybePacked<W>): W | Derivable<W>;
/**
 * Extends an expression with a property access. Automatically uses bracket notation where appropriate and escapes
 * strings in brackets to give a realistic combined expression.
 *
 * @param expression the (optional) expression to extend
 * @param property the property that should be appended to the expression
 */
export declare function extendExpression(expression: string | undefined, property: string | number): string;
/**
 * Extends a path with a property access.
 *
 * @param path the (optional) path to extend
 * @param property the property that should be appended to the path
 */
export declare function extendPath(path: Array<string | number> | undefined, property: string | number): (string | number)[];
