/**
 * Indicates where a change on a  {@link MergableObject} originates. Either remotely or locally.
 */
export declare enum MergableObjectEventSource {
    /** Remote (backend) modification of {@link MergableObject} */
    Remote = 1,
    /** Local (browser) modification of {@link MergableObject} */
    Local = 2
}
/**
 * Describes the states a mergable object may have. A mergable object is an object that has a local and a remote representation. Changes made to the local object
 * shall be synchronized to the remote object and vice versa. In same cases conflicts may arise because changes have been made to both the local and the remote
 * representation of the object.
 */
export declare enum MergableObjectState {
    /**
     * Object is not modified. Only possible if the object has had or have a remote counterpart.
     * */
    Unchanged = 0,
    /**
     * Object has been added to {@link MergableSet} via {@link MergableSet.add} but has not yet been saved (in which case it shall become {@link MergableObjectState.Unchanged}).
     * It is not possible to transition directly to {@link MergableObjectState.Modified} from this state as there is (probably) not yet a remote counterpart.
     * In the unlikely case there is a remote counterpart we will have to deal with a conflict. Somebody has added the exact same object remotely
     */
    Added = 1,
    /**
     * Local object has been modified. Only possible if the local object has had or have a remote counterpart. A object can only get this state if
     * {@link MergableSet.update} is called and the object previously existed in {@link MergableSet}. Do note that if a object is updated because
     * its remote counterpart has changed then the object will retain its {@link MergableObjectState.Unchanged} state.
     */
    Modified = 2,
    /**
     * Object is marked for deletion. If the object has no remote counterpart it is removed immediately. Otherwise it will be removed on next save. (at which point its remote counterpart will disappear).
     */
    Deleted = 4
}
/**
 * Reason for a conflict between a local and a remote object
 * Note: There is no state represented by 0.
 */
export declare enum MergableObjectConflict {
    /**
     * No conflict.
     */
    None = 0,
    /**
     * The local object differs from the remote object somehow
     */
    RemoteDiffers = 1,
    /**
     * The remote object was deleted while the local object was modified.
     */
    RemoteDeleted = 2
}
/**
 * Describes the relation between a local object and its remote counterpart. Also gives
 * the state for the local object and (if exists) a conflict reason between the local and the
 * remote object. A conflict typically arises when the local object has been modified and so has
 * the remote object.
 * Example: local object was modified but remote object was deleted. This conflict cannot be resolved automatically.
 */
export declare class MergableObjectWithState<P> {
    /**
     * local object.
     */
    local: P;
    private _state;
    private _conflictReason;
    /**
     * Remote object (if there is one)
     */
    remote?: P | undefined;
    onStateChange: (p: MergableObjectWithState<P>) => void;
    constructor(
    /**
     * local object.
     */
    local: P, _state: MergableObjectState, _conflictReason: MergableObjectConflict, 
    /**
     * Remote object (if there is one)
     */
    remote?: P | undefined);
    /**
     * If defined then there is a conflict between the local and the remote object.
     */
    get conflictReason(): MergableObjectConflict;
    /**
     * Sets conflict reason (if any).
     */
    set conflictReason(v: MergableObjectConflict);
    /**
     * `true` if a conflict exists. Otherwise `false`
     */
    get hasConflict(): boolean;
    /**
     * get current state of the local object.
     */
    get state(): MergableObjectState;
    /**
     * set current state of the local object.
     */
    set state(v: MergableObjectState);
}
export type ImmutableMergableObjectWithState<P> = Readonly<MergableObjectWithState<P>>;
/**
 * Represents event for change in local object.
 */
export interface MergableObjectLocalEventArgs<T> {
    /**
     * Determines if the event was raised due to a change in the local or a remote object.
     * Use in discriminant union checks to get type guard.
     */
    readonly eventSource: MergableObjectEventSource.Local;
    /**
     * Local representation of the object.
     */
    readonly local: T;
    /**
     * State of the local object.
     */
    readonly state: MergableObjectState;
}
/**
 * Represents event for change in remote object.
 */
export interface MergableObjectRemoteEventArgs<T> {
    /**
     * Determines if the event was raised due to a change in the local or a remote object.
     * Use in discriminant union checks to get type guard.
     */
    readonly eventSource: MergableObjectEventSource.Remote;
    /**
     * Remote representation of object. If remote has been deleted
     * then it will be undefined.
     */
    readonly remote?: T;
    /**
     * Local representation of the object.
     */
    readonly local: T;
    /**
     * Describes conflict reason (if any).
     */
    readonly conflictReason: MergableObjectConflict;
    /**
     * State of the local object.
     */
    readonly state: MergableObjectState;
}
/**
 * Represents event for change in local or remote object. Use the {@link MergableObjectEventArgs<T>.eventSource} property to determine if
 * the event is local or remote.
 */
export type MergableObjectEventArgs<T> = MergableObjectLocalEventArgs<T> | MergableObjectRemoteEventArgs<T>;
/**
 * Properties that a MergableObject must implement.
 */
export interface MergableObjectProps {
    /** Unique id of the object. A uuid is recommended. */
    readonly id: string;
}
/**
 * Operations that a MergableObject must implement
 */
export interface MergableObjectOperations<T> {
    /**
     * Called when the object is first added to to {@link MergableSet<T>} by calling {@link MergableSet<T>.addOrUpdate} and if the object
     * does not already exist in {@link MergableSet}. In that case the local object will get {@link MergableObjectState.Added}.
     * Also called if a remote object, for which there is no corresponding local or remote object in {@link MergableSet}, is detected during
     * calls to {@link MergableSet<T>.saveAndMerge} or {@link MergableSet<T>.loadAndMerge}. In that case the local object get the state {@link MergableObjectState.Unchanged}.
     * @param o Gives both local and (optional) remote representation of object. Also gives (rare but possible) conflict reason
     * if it exists. A conflict may occur if a local object with same {@link MergableObject.id} has been added by calling  {@link MergableSet<T>.addOrUpdate} when no remote counterpart existed. Once
     * {@link MergableSet<T>.saveAndMerge} or {@link MergableSet<T>.loadAndMerge} is called a remote counter part (same id) is detected and their content differs. This should only happen if non unique id's are
     * assigned to objects.
     */
    onAdded(o: MergableObjectEventArgs<T>): void;
    /**
     * This event is called when local object already exists in {@link MergableSet<T>} and does not have state {@link MergableObjectState.Added} and one of the following holds true
     * 1.  If state is {@link MergableObjectState.Unchanged} and a call to {@link MergableSet<T>.loadAndMerge} or {@link MergableSet<T>.saveAndMerge} results in a detection that the remote object has changed and it differs from the local object
     *
     * 2. Calling {@link MergableSet<T>.addOrUpdate}
     * @param o Gives both local and (optional) remote representation of object. Also gives (rare but possible) conflict reason
     * if it exists. A conflict may occur if a local object with same {@link MergableObject.id} has been added by calling  {@link MergableSet<T>.addOrUpdate} when no remote counterpart existed. Once
     * {@link MergableSet<T>.saveAndMerge} or {@link MergableSet<T>.loadAndMerge} is called a remote counter part (same id) is detected and their content differs. This should only happen if non unique id's are
     * assigned to objects.
     */
    onUpdate(o: MergableObjectEventArgs<T>): void;
    /** Called when
     *  * {@link Layer<T>.delete} is called.
     *  * {@link Layer<T>.save} or {@link Layer<T>.load} is called and item exists in client (and is unchanged)
     *  but no longer in backend. That means that somebody has removed it and store that in the backend. Hence it must be removed
     * @param o Gives both local and (optional) remote representation of object. Also gives (rare but possible) conflict reason
     * if it exists. A conflict may occur if a local object with same {@link MergableObject.id} has been added by calling  {@link MergableSet<T>.addOrUpdate} when no remote counterpart existed. Once
     * {@link MergableSet<T>.saveAndMerge} or {@link MergableSet<T>.loadAndMerge} is called a remote counter part (same id) is detected and their content differs. This should only happen if non unique id's are
     * assigned to objects.
     * @returns `true` if there is no conflict or conflict could be resolved.
     * */
    onDelete(o: MergableObjectEventArgs<T>): void;
    /**
     * Returns `true` if objects are considered to be equal. Otherwise `false`
     * @param o Object to compare with.
     */
    isEqual(o: T): boolean;
    /**
     * Deep clone the object.
     */
    clone(): T;
}
/**
 * Makes it possible to work with local objects to which there are (possibly) remote counterparts. Will automatically resolve most conflicts
 * except those that occur when both the local and remote object have been modified in some way that will result in a conflict.
 * Example: Local object is modified (which means it must have existed remotely) but the remote no longer exists.
 */
export type MergableObject<T extends MergableObjectProps> = MergableObjectProps & MergableObjectOperations<T>;
/**
 * Represents a collection of object that has a local and a remote representation.
 * Changes are always made to the local representation of the object. However it is possible
 * to merge remote representations of the objects. When a merge occurs new local objects may be added, updated and deleted
 * In some cases conflicts may arise. Such as when a local object has been modified but the remote object has been deleted.
 */
export declare class MergableSet<P extends MergableObject<P>> {
    private readonly _objectsById;
    private readonly _conflicts;
    private readonly _objectsByState;
    private readonly _onStateChangeBoundToThis;
    /**
     * Get number of conflicts in the set.
     * @returns Number of conflicts.
     */
    get conflictCount(): number;
    /**
     * Gets all local objects and their remote counterparts. Includes both state and conflict reasons (if any)
     * @returns All objects.
     */
    get objects(): IterableIterator<ImmutableMergableObjectWithState<P>>;
    /**
     * Gets all conflicts.
     * @returns All conflicts.
     */
    get conflicts(): ImmutableMergableObjectWithState<P>[];
    /**
     * Gets number of objects that are not marked as {@link MergableObjectState.Unchanged}.
     * @returns Number of changed objects.
     */
    get changeCount(): number;
    /**
     * Checks if there are any changed objects in the set.
     * @returns `true` if there areobject not marked as {@link MergableObjectState.Unchanged}.
     */
    get hasChanges(): boolean;
    /**
     * Clear set
     */
    clear(): void;
    /**
     * Get object with specified id.
     * @param id Id of object to get.
     */
    get(id: string): MergableObjectWithState<P> | undefined;
    /**
     * Check if object with specified id exists.
     * @param id Id of matching object.
     * @returns `true` if object exists. Otherwise `false`.
     */
    has(id: string): boolean;
    /**
     * Deletes a object
     * @param local Id of object to delete
     * @returns `true` if object was deleted. Otherwise `false`
     */
    delete(local: Pick<P, 'id'> | string): boolean;
    /**
     * Adds an object. If object already exists, it will not be added. An object exists for items where
     * {@link MergableObject.id} already exists.
     * @param o Object to add
     * @returns `true` if object was added, otherwise `false`.
     */
    add(o: P): boolean;
    /**
     * Updates an object that already exists.
     * @param id Id of object to update
     * @param updateAction function called with {@link MergableObject} instance of specified id.
     * Caller should make any required modfifications to the object in this function.
     * @returns `true` if object did exist, otherwise `false`.
     */
    update(id: string, updateAction: (existingItem: P) => void): boolean;
    /**
     * Merges remote objects.
     * @param remoteObjects Remote objects to merge.
     * @returns Number of conflicts that occured during the merge. May not be
     * same as {@link conflictCount} (which gives all conflicts from previous merges as well.).
     */
    mergeRemotes(remoteObjects: P[]): number;
    /**
     * Accepts changes in the set. Is only successful if no conflicts exist.
     * @returns `true` if no conflicts existed so changes can be accepted. Otherwise, `false`.
     */
    acceptChanges(): boolean;
    private onStateChange;
}
//# sourceMappingURL=MergableSet.d.ts.map