import { EventEmitter } from "../events/event-emitter.js";
import { Event, type IEvent } from "../events/shared.js";
import { BinaryExpression } from "../parser/expressions/binary-expression.js";
import { CallExpression } from "../parser/expressions/call-expression.js";
import { ConstantExpression } from "../parser/expressions/constant-expression.js";
import { ExpressionVisitor } from "../parser/expressions/visitors/expression-visitor.js";
import type { Expressions, StrictExpressions } from "../parser/expressions/expression.js";
import { MemberExpression } from "../parser/expressions/member-expression.js";
import { NewExpression } from "../parser/expressions/new-expression.js";
import { ParameterExpression } from "../parser/expressions/parameter-expression.js";
import { TernaryExpression } from "../parser/expressions/ternary-expression.js";
import { type Subscription } from "../teardown-manager.js";
import { watcher, type Watcher, WatcherFormatter } from './shared.js';
import { AssignmentExpression } from "../parser/expressions/assignment-expression.js";
import { ObservableArray } from "./array.js";
import { FormatExpression } from "../parser/parser.js";
import { AllEventKeys } from "../events/index.js";
export interface ObjectEvent<T, TKey extends keyof T> {
    readonly property: TKey;
    readonly value: T[TKey];
    readonly oldValue: T[TKey];
}
export declare class AsyncFormatter extends WatcherFormatter {
    private promise;
    private value;
    /**
     * Formats the value.
     * @param {unknown} value - The value to format.
     * @returns {unknown} The formatted value.
     */
    format(value: unknown): unknown;
    /**
     * Creates an instance of AsyncFormatter.
     * @param {Watcher} [watcher] - The watcher instance.
     */
    constructor(watcher?: Watcher);
}
export declare class EventFormatter<T extends unknown[]> extends WatcherFormatter {
    private event;
    private value;
    private sub?;
    /**
     * Formats the value.
     * @param {unknown} value - The value to format.
     * @returns {T} The formatted value.
     */
    format(value: unknown): T;
    /**
     * Creates an instance of EventFormatter.
     * @param {Watcher} watcher - The watcher instance.
     */
    constructor(watcher: Watcher);
}
export default class Watch<T extends object> extends WatcherFormatter {
    private value;
    /**
     * Formats the value.
     * @param {T} value - The value to format.
     * @returns {T} The formatted value.
     */
    format(value: T): T;
}
export declare class BindingFormatter extends WatcherFormatter {
    private binding;
    private sub;
    private value;
    /**
     * Formats the value.
     * @param {unknown} value - The value to format.
     * @returns {unknown} The formatted value.
     */
    format(value: unknown): unknown;
    /**
     * Creates an instance of BindingFormatter.
     * @param {Watcher} watcher - The watcher instance.
     */
    constructor(watcher: Watcher);
}
export type IWatched<T extends object> = T & {
    [watcher]: ObservableObject<T> | ObservableArray<T>;
};
export type Getter<TSource, TResult> = (target: TSource) => TResult;
export type WatchGetter<TSource, TResult> = (target: TSource, watcher: Watcher) => TResult;
export type Setter<TSource, TValue> = (target: TSource, value: TValue) => void;
export type IWatchable<T extends object> = {
    [watcher]?: ObservableObject<T>;
};
export declare class BuildWatcherAndSetter<T> extends ExpressionVisitor {
    target: ParameterExpression<T>;
    value: ParameterExpression<unknown>;
    private static memberWatcher;
    private static formatWatcher;
    /**
     * Evaluates the expression.
     * @param {Expressions} expression - The expression to evaluate.
     * @returns {{ watcher: WatchGetter<T, TValue>, setter?: Setter<T, TValue> }} The watcher and setter.
     */
    eval<TValue>(expression: Expressions): {
        watcher: WatchGetter<T, TValue>;
        setter?: Setter<T, TValue>;
    };
    private boundObservables;
    private getter;
    /**
     * Visits a constant expression.
     * @param {ConstantExpression<unknown>} arg0 - The constant expression.
     * @returns {StrictExpressions} The visited expression.
     */
    visitConstant(arg0: ConstantExpression<unknown>): StrictExpressions;
    /**
     * Visits a format expression.
     * @param {FormatExpression<TOutput>} expression - The format expression.
     * @returns {FormatExpression<TOutput>} The visited expression.
     */
    visitFormat<TOutput>(expression: FormatExpression<TOutput>): FormatExpression<TOutput>;
    /**
     * Visits a member expression.
     * @param {MemberExpression<T1, TMember, T1[TMember]>} arg0 - The member expression.
     * @returns {StrictExpressions} The visited expression.
     */
    visitMember<T1, TMember extends keyof T1>(arg0: MemberExpression<T1, TMember, T1[TMember]>): StrictExpressions;
    /**
     * Visits a ternary expression.
     * @param {TernaryExpression<T>} expression - The ternary expression.
     * @returns {TernaryExpression<Expressions>} The visited expression.
     */
    visitTernary<T extends Expressions = StrictExpressions>(expression: TernaryExpression<T>): TernaryExpression<Expressions>;
    /**
     * Visits a call expression.
     * @param {CallExpression<T, TMethod>} arg0 - The call expression.
     * @returns {StrictExpressions} The visited expression.
     */
    visitCall<T, TMethod extends keyof T>(arg0: CallExpression<T, TMethod>): StrictExpressions;
    /**
     * Visits a new expression.
     * @param {NewExpression<T>} expression - The new expression.
     * @returns {StrictExpressions} The visited expression.
     */
    visitNew<T>(expression: NewExpression<T>): StrictExpressions;
    /**
     * Visits a binary expression.
     * @param {BinaryExpression<T>} expression - The binary expression.
     * @returns {BinaryExpression<Expressions>} The visited expression.
     */
    visitAssign<T extends Expressions = StrictExpressions>(expression: AssignmentExpression<T>): AssignmentExpression<Expressions>;
    /**
     * Visits a binary expression.
     * @param {BinaryExpression<T>} expression - The binary expression.
     * @returns {BinaryExpression<Expressions>} The visited expression.
     */
    visitBinary<T extends Expressions = StrictExpressions>(expression: BinaryExpression<T>): BinaryExpression<Expressions>;
}
type ObservableType<T extends object> = {
    [key in Exclude<keyof T, typeof allProperties>]: IEvent<[ObjectEvent<T, key>], void>;
} & {
    [allProperties]: IEvent<[ObjectEvent<T, keyof T>], void>;
};
export type BindingChangedEvent<T> = {
    value: T;
    oldValue: T;
};
export declare const BindingsProperty: unique symbol;
type Unbound<T> = T extends Binding<infer X> ? X : T;
type UnboundObject<T extends object> = {
    [K in keyof T]?: Unbound<T[K]>;
};
export declare class Binding<T> extends EventEmitter<{
    change: Event<[BindingChangedEvent<T>]>;
}> {
    target: unknown;
    readonly expression: Expressions;
    /**
     * Combines named bindings.
     * @param {T} obj - The object with named bindings.
     * @returns {Binding<UnboundObject<T>>} The combined binding.
     */
    static combineNamed<T extends {
        [K in keyof T]?: T[K] | Binding<T[K]>;
    }>(obj: T): Binding<UnboundObject<T>>;
    /**
     * Combines multiple bindings.
     * @param {...(T[K] | Binding<T[K]>)[]} bindings - The bindings to combine.
     * @returns {Binding<T>} The combined binding.
     */
    static combine<T extends unknown[]>(...bindings: {
        [K in keyof T]?: T[K] | Binding<T[K]>;
    }): Binding<T>;
    /**
     * Checks if a target has a bound property.
     * @param {T} target - The target object.
     * @param {PropertyKey} property - The property key.
     * @returns {boolean} True if the target has a bound property, false otherwise.
     */
    static hasBoundProperty<T extends {}>(target: T, property: PropertyKey): boolean;
    /**
     * Defines a property on the target object.
     * @param {object} target - The target object.
     * @param {PropertyKey} property - The property key.
     * @param {T} [value] - The initial value.
     * @returns {Binding<T>} The defined binding.
     */
    static defineProperty<T = unknown>(target: object, property: PropertyKey, value?: T): Binding<T>;
    /**
     * Pipes the binding to another expression.
     * @param {TKey | string | Expressions | ((ev: BindingChangedEvent<T>) => U)} expression - The expression to pipe to.
     * @returns {Binding<U>} The piped binding.
     */
    pipe<const TKey extends keyof T>(expression: TKey): Binding<T[TKey]>;
    pipe<U>(expression: string | keyof T | Expressions | ((ev: BindingChangedEvent<T>) => U)): Binding<U>;
    watcher: Watcher;
    [Symbol.dispose](): void;
    /**
     * Creates an instance of Binding.
     * @param {unknown} target - The target object.
     * @param {Expressions} expression - The expression.
     */
    constructor(target: unknown, expression: Expressions);
    /**
     * Simplifies the binding.
     * @param {Binding<any>} target - The target binding.
     * @param {Expressions} expression - The expression.
     * @returns {Binding<T> | null} The simplified binding.
     */
    static simplify<T>(target: Binding<any>, expression: Expressions): Binding<T> | null;
    private readonly attachWatcher;
    /**
     * Unwraps the element.
     * @param {T} element - The element to unwrap.
     * @returns {Partial<T>} The unwrapped element.
     */
    static unwrap<T>(element: T): Partial<T>;
    private _setter?;
    /**
     * Registers a handler for the change event.
     * @param {(ev: { value: T, oldValue: T }) => void} handler - The event handler.
     * @param {boolean} [triggerOnRegister] - Whether to trigger the handler on registration.
     * @returns {Subscription} The subscription.
     */
    onChanged(handler: (ev: {
        value: T;
        oldValue: T;
    }) => void, triggerOnRegister?: boolean): Subscription;
    /**
     * Sets the value.
     * @param {T} value - The value to set.
     */
    setValue(value: T): void;
    get canSet(): boolean;
    /**
     * Gets the value.
     * @returns {T} The value.
     */
    getValue(): T;
}
export declare class EmptyBinding<T> extends Binding<T> {
    /**
     * Creates an instance of EmptyBinding.
     * @param {T} [initialValue] - The initial value.
     */
    constructor(initialValue?: T);
    get canSet(): boolean;
    /**
     * Gets the value.
     * @returns {T} The value.
     */
    getValue(): T;
    /**
     * Sets the value.
     * @param {T} newValue - The value to set.
     */
    setValue(newValue: T): void;
}
export declare const allProperties: unique symbol;
/**
 * Observable object implementation.
 * @param {Object} initialObject - The initial object.
 */
export declare class ObservableObject<T extends object> extends EventEmitter<ObservableType<T>> {
    /**
     * Unwraps the target object.
     * @param {T} arg0 - The target object.
     * @returns {T extends ObservableObject<infer X> ? X : T} The unwrapped object.
     */
    static unwrap<T>(arg0: T): T extends ObservableObject<infer X> ? X : T;
    /**
     * Generates a dynamic proxy that gets and sets values from target, but triggers notifications on set.
     */
    static wrap<T extends object>(target: T): IWatched<T>;
    readonly target: T & IWatchable<T>;
    /**
     * Creates an instance of ObservableObject.
     * @param {T & { [watcher]?: ObservableObject<T> } | ObservableObject<T>} target - The target object.
     */
    constructor(target: (T & IWatchable<T>) | ObservableObject<T>);
    /**
     * Watches all properties of the object.
     * @param {T} obj - The object to watch.
     * @param {Watcher} watcher - The watcher instance.
     * @returns {Subscription} The subscription.
     */
    static watchAll<T extends object>(obj: T, watcher: Watcher): Subscription;
    /**
     * Watches a property of the object.
     * @param {Watcher} watcher - The watcher instance.
     * @param {TKey} property - The property to watch.
     * @returns {Subscription} The subscription.
     */
    watch<const TKey extends AllEventKeys<ObservableType<T>>>(watcher: Watcher, property: TKey): Subscription;
    /**
     * Checks if an object is watched.
     * @param {T} x - The object to check.
     * @returns {boolean} True if the object is watched, false otherwise.
     */
    static isWatched<T>(x: T): x is IWatched<T & object>;
    /**
     * Sets the value of a property.
     * @param {Binding<T> | T} target - The target object or binding.
     * @param {Expressions | PropertyKey} expression - The expression or property key.
     * @param {any} value - The value to set.
     */
    static setValue<T extends object, const TKey extends keyof T>(target: Binding<T>, expression: TKey, value: T[TKey]): any;
    static setValue<T extends object>(target: Binding<T>, expression: string, value: any): any;
    static setValue<T extends object>(target: Binding<T>, expression: Expressions, value: any): any;
    static setValue<T extends object, const TKey extends keyof T>(target: T, expression: TKey, value: T[TKey]): any;
    static setValue<T extends object>(target: T, expression: string, value: any): any;
    static setValue<T extends object>(target: T, expression: Expressions, value: any): any;
    /**
     * Sets the value of a property.
     * @param {TKey} property - The property key.
     * @param {T[TKey]} value - The value to set.
     * @returns {boolean} True if the value was set, false otherwise.
     */
    setValue<const TKey extends keyof T>(property: TKey, value: T[TKey]): boolean;
    /**
     * Gets the value of a property.
     * @param {TKey} property - The property key.
     * @returns {T[TKey]} The value of the property.
     */
    getValue<const TKey extends keyof T>(property: TKey): T[TKey];
    /**
     * Gets the value of a property.
     * @param {Binding<T> | T} target - The target object or binding.
     * @param {TKey} property - The property key.
     * @returns {T[TKey]} The value of the property.
     */
    static getValue<T, const TKey extends keyof T>(target: Binding<T>, property: TKey): T[TKey];
    static getValue<T, const TKey extends keyof T>(target: T, property: TKey): T[TKey];
    /**
     * Gets the observable object for a property.
     * @param {TKey} property - The property key.
     * @returns {ObservableObject<T[TKey]> | null} The observable object or null.
     */
    getObservable<const TKey extends keyof T>(property: TKey): T[TKey] extends object ? ObservableObject<T[TKey]> : null;
    /**
     * Gets the value of a property.
     * @param {T} target - The target object.
     * @param {keyof T} property - The property key.
     * @returns {T[keyof T]} The value of the property.
     */
    static get<T>(target: T, property: keyof T): T[keyof T];
}
export {};
