import { NameAnd } from "@focuson/utils";
export declare const identityOptics: <State>(description?: string) => Iso<State, State>;
export interface GetOptioner<Main, Child> {
    getOption: (m: Main) => Child | undefined;
}
export interface SetOptioner<Main, Child> {
    setOption: (m: Main, c: Child) => Main | undefined;
}
export interface Getter<Main, Child> {
    get: (m: Main) => Child;
}
export interface Setter<Main, Child> {
    set: (m: Main, c: Child) => Main;
}
export interface HasOptional<Main, T> {
    optional: Optional<Main, T>;
}
/** An Optional is like a lens, except that it is not guaranteed to 'work'. Specifically if you ask for a child... maybe that child isn't there.
 *
 * This is great for things like 'optional values' which are often written as 'name?: type' in typescript.
 *
 * It is rare that you create one directly. Usually it is created using 'focusQuery' on a lens
 */
export declare class Optional<Main, Child> implements GetOptioner<Main, Child>, SetOptioner<Main, Child> {
    getOption: (m: Main) => Child | undefined;
    setOption: (m: Main, c: Child) => Main | undefined;
    description: string;
    constructor(getOption: (m: Main) => (Child | undefined), optionalSet: (m: Main, c: Child) => Main | undefined, description?: string);
    set: (m: Main, c: Child) => Main;
    map: (m: Main, fn: (c: Child | undefined) => Child) => Main;
    mapDefined: (m: Main, fn: (c: Child) => Child) => Main;
    /** This is identical to this.setOption(m, undefined) */
    clearJson(m: Main): Main;
    /** Allows us to change the focuson-ed child based on it's existing value
     * @fn a function that will be given the old value and will calculate the new
     * @returns a function that given a Main will return a new main with the child transformed as per  'fn' */
    transform(fn: (oldChild: Child) => Child): (m: Main) => Main;
    /** This is used when the 'parameter' points to definite value. i.e. it isn't 'x: X | undefined' or 'x?: X'. If you want to
     * walk through those you probably want to use 'focusQuery'
     *
     * If the type system is complaining and you are sure that it should be OK, check if the previous focusOn should be a focusQuery
     * @param k
     */
    focusOn<K extends keyof Child>(k: K): Optional<Main, Child[K]>;
    /** Used to focus onto a child that might not be there. If you don't use this, then the type system is likely to complain if you try and carry on focusing. */
    focusQuery<K extends keyof Child, Req extends Required<Child>>(k: K): Optional<Main, Req[K]>;
    chain<T>(o: Optional<Child, T>): Optional<Main, T>;
    combine<OtherChild>(other: Optional<Main, OtherChild>): Optional<Main, [Child, OtherChild]>;
    /** If you desire to change the description this will do that. It is rarely called outside the Lens code itself */
    withDescription(description: string): Optional<Main, Child>;
    combineAs<OtherChild, NewChild>(other: Optional<Main, OtherChild>, iso: Iso<[Child, OtherChild], NewChild>): Optional<Main, NewChild>;
    chainIntoArray(a: string[]): Optional<Main, string>;
    chainNthFromPath(pathL: Optional<Main, number>): Optional<Main, any>;
    chainCalc(pathL: Optional<Main, keyof Child>): Optional<Main, Child[keyof Child]>;
    toString(): string;
}
export declare function optional<Main, Child>(getOption: (m: Main) => Child | undefined, setOption: (m: Main, c: Child) => Main | undefined, description?: string): Optional<Main, Child>;
export declare function identityOptional<Main>(): Optional<Main, Main>;
export declare function orUndefined<T>(description?: string): Optional<T | undefined, T>;
export declare function castIfOptional<T, T1>(cond: (t: T) => boolean, description?: string): Optional<T, T1>;
/**
 * Creates a lens with two generics. Lens<Main,Child>. Main is the main 'object' that we start with, and Child is the part of Main that the lens is focuson-ed
 * @param get should be a sideeffect free function that goes from 'Main' to the focuson-ed child. When called it 'gets' the Child from the Main
 * @param set should be a sideeffect free function that creates a new Main out of an old main and a new child. It returns the old main with the 'focuson-ed' part replaced by the new child
 * @param description should probably be the string representation of the class 'Main'. If the main object is of type Dragon, this could be the string 'dragon'.
 *
 * Usually these are created by code like
 *
 * identityOptics<Dragon>().focuson('head')
 */
export declare function lens<Main, Child>(get: (main: Main) => Child, set: (main: Main, newChild: Child) => Main, description?: string): Lens<Main, Child>;
/** This is the class that represents a Lens<Main,Child> which focuses on Child which is a part of the Main */
export declare class Lens<Main, Child> extends Optional<Main, Child> implements Getter<Main, Child>, Setter<Main, Child> {
    set: (m: Main, c: Child) => Main;
    get: (m: Main) => Child;
    constructor(get: (m: Main) => (Child), set: (m: Main, c: Child) => Main, description: string);
    /** this is the 'normal' focuson. We use it when we know that the result is there. i.e. if we have
     *
     * interface AB{
     *      a: string,
     *      b?: SomeInterface | undefined
     * }
     *
     * In this case focusOn('a') will give us a Lens<AB,string> but focusOn('b') will give a lens<AB, SomeInterface|undefined>.
  
     * @param k
     */
    focusOn<K extends keyof Child>(k: K): Lens<Main, Child[K]>;
    /** interface AB{
     *      a: string,
     *      b?: SomeInterface | undefined
     * }
     *
     * In this case it would be redundant to have  focusWithDefault('a', "someA") because 'a' should never be undefined.
     * However (if someValue is a SomeInterface)  focusWithDfeault('b', someValue) return Lens<AB,SomeInterface>  and if we do a get, and b was undefined, we use 'someValue'
     * @param k
     */
    focusWithDefault<K extends keyof Child, Req extends Required<Child>>(k: K, def: Child[K]): Lens<Main, Req[K]>;
    chainLens: <T>(o: Lens<Child, T>) => Lens<Main, T>;
    /** @deprecated */
    chainWith: <T>(o: Lens<Child, T>) => Lens<Main, T>;
    combineLens<OtherChild>(other: Lens<Main, OtherChild>): Lens<Main, [Child, OtherChild]>;
    /** If you desire to change the description this will do that. It is rarely called outside the Lens code itself */
    withDescription(description: string): Lens<Main, Child>;
    toString(): string;
}
/** A factory class that allows us to create new Lens. Every method on it is static, so you would never create one of these
 *
 * This class will be removed and replaced with just 'plain functions'
 * */
export declare class Lenses {
    /** This is a the normal way to generate lens. It create a link that goes from Main to itself */
    static build<Main>(description: string): Lens<Main, Main>;
    /** Given a main which is an object, with a field name, this returns a lens that goes from the Main to the contents of the field name */
    static field: <Main, K extends keyof Main>(fieldName: K) => Lens<Main, Main[K]>;
    /** Given a main which is an object, with a field name, this returns a lens that goes from the Main to the contents of the field name */
    static identity<M>(description?: string): Iso<M, M>;
    /**This should no longer be needed. It was in fact the need for this method that drove the rewrite using Optionals/Prisms and Isos.
     *
     * Nowadays we can use focusQuery
     *
     * It should only be used when we 'know' that a Lens<Main,Child|undefined> is really a Lens<Main,Child>.
     * @deprecated */
    static define<T>(): Lens<T | undefined, T>;
    /** This returns a lens from an array of T to the last item of the array */
    static last<T>(): Lens<T[], T>;
    /** This returns a lens from an array of T to the next item of the array */
    static append<T>(): Lens<T[], T>;
    /** This returns a lens from an array of T to the nth member of the array */
    static nth<T>(n: number): Optional<T[], T>;
    static chainNthFromOptionalFn<From, To, NewTo>(lens: Optional<From, To>, newToFnL: (f: From) => Optional<To, NewTo>, newFragDescription: string): Optional<From, NewTo>;
    static chainNthRef<From, T>(lens: Optional<From, T[]>, lookup: (name: string) => any, name: string, description?: string): Optional<From, T>;
    /** Used to to go from ids to values and back again. Obvious if the mapping isn't one to one there will be loss of data */
    static chainLookup<Main, V extends string | number | boolean>(opt: Optional<Main, string>, lookupL: Optional<Main, NameAnd<V>>): Optional<Main, V>;
    /** Used to to go from ids to values and back again. Obvious if the mapping isn't one to one there will be loss of data */
    static chainLookupTable<Main, L, K extends keyof L, V extends keyof L>(opt: Optional<Main, string>, lookupL: Optional<Main, L[]>, idName: K, valueName: V): Optional<Main, L[V]>;
    static constant: <Main, Child>(value: Child, description?: string) => Lens<Main, Child>;
    static safeList<T>(): Lens<T[] | undefined, T[]>;
    static if<Main, T>(cond: Optional<Main, boolean>, trueL: Optional<Main, T>, falseL: Optional<Main, T>): Optional<Main, T>;
    static condition<Main, Cond, T>(cond: Optional<Main, Cond>, fn: (c: Cond) => boolean, trueL: Optional<Main, T>, falseL: Optional<Main, T>): Optional<Main, T>;
    static calculatedNth<Main, T>(nL: Optional<Main, number>, opt: Optional<Main, T[]>): Optional<Main, T>;
}
export type FocusOnPathItem = string | FocusOnPathSimpleItem | FocusOnPathNthItem | FocusOnPathPathItem;
export interface FocusOnPathSimpleItem {
    action: '$last' | '$append';
}
export declare function isFOPSingle(f: FocusOnPathItem): f is FocusOnPathSimpleItem;
export interface FocusOnPathNthItem {
    action: '[n]';
    n: number;
}
export declare function isFOPNth(f: FocusOnPathItem): f is FocusOnPathNthItem;
export interface FocusOnPathPathItem {
    action: '[path]';
    path: FocusOnPathItem[];
}
export declare function isFOPPath(f: FocusOnPathItem): f is FocusOnPathPathItem;
export declare function prism<Main, Child>(getOption: (m: Main) => Child | undefined, reverseGet: (c: Child) => Main, description?: string): Prism<Main, Child>;
export declare function dirtyPrism<Main, Child>(getOption: (m: Main) => Child | undefined, reverseGet: (c: Child) => Main, description?: string): DirtyPrism<Main, Child>;
export declare class DirtyPrism<Main, Child> extends Optional<Main, Child> {
    reverseGet: (c: Child) => Main;
    constructor(getOption: (m: Main) => (Child | undefined), reverseGet: (c: Child) => Main, description: string);
    toString(): string;
}
export declare class Prism<Main, Child> extends DirtyPrism<Main, Child> {
    constructor(getOption: (m: Main) => (Child | undefined), reverseGet: (c: Child) => Main, description: string);
    toString(): string;
}
export declare function iso<Main, Child>(get: (m: Main) => Child, reverseGet: (c: Child) => Main, description?: string): Iso<Main, Child>;
export declare class Iso<Main, Child> extends Lens<Main, Child> {
    reverseGet: (c: Child) => Main;
    optional: Optional<Main, Child>;
    constructor(get: (m: Main) => Child, reverseGet: (c: Child) => Main, description: string);
    toString(): string;
}
/** This 'changes' two parts of Main simultaneously.
 *
 * @param lens1 This is focused in on a part of main that we want to change
 * @param lens2 This is focused in on a second part of main that we want to change
 * @param fn1  Given the old values that lens1 and lens2 are focused on, this gives us a new value for the part of main that lens1 is focused on
 * @param fn2  Given the old values that lens1 and lens2 are focused on, this gives us a new value for the part of main that lens2 is focused on
 * @returns a function that given a Main will return a new main with the two functions used to modify the parts of Main that the two lens are focused in on
 *
 */
export declare const transformTwoValues: <Main, C1, C2>(lens1: Optional<Main, C1>, lens2: Optional<Main, C2>) => (fn1: (c1: C1, c2: C2) => C1, fn2: (c1: C1, c2: C2) => C2) => (main: Main) => Main;
/** This 'changes' two parts of Main simultaneously.
 *
 * @param lens1 This is focused in on a part of main that we want to change
 * @param lens2 This is focused in on a second part of main that we want to change
 * @param main  A value that is to be 'changed' by the method. Changed means that we will make a copy of it with changes
 * @param c1  The new value for the part that lens1 is focused on
 * @param c2  The new value for the part that lens2 is focused on
 * @returns a new main with the parts the two lens are focused on changed by the new values
 *
 */
export declare const updateTwoValues: <Main, C1, C2>(lens1: Optional<Main, C1>, lens2: Optional<Main, C2>) => (main: Main, c1: C1, c2: C2) => Main;
/** This 'changes' three parts of Main simultaneously.
 *
 * @param lens1 This is focused in on a part of main that we want to change
 * @param lens2 This is focused in on a second part of main that we want to change
 * @param lens3 This is focused in on a third part of main that we want to change
 * @param main  A value that is to be 'changed' by the method. Changed means that we will make a copy of it with changes
 * @param c1  The new value for the part that lens1 is focused on
 * @param c2  The new value for the part that lens2 is focused on
 * @param c3  The new value for the part that lens3 is focused on
 * @returns a new main with the parts the three lens are focused on changed by the new values
 *
 */
export declare const updateThreeValues: <Main, C1, C2, C3>(lens1: Optional<Main, C1>, lens2: Optional<Main, C2>, lens3: Optional<Main, C3>) => (main: Main, c1: C1, c2: C2, c3: C3) => Main;
export declare function nthItem<T>(n: number): Optional<T[], T>;
export declare function firstIn2<T1, T2>(): Optional<[T1, T2], T1>;
export declare function secondIn2<T1, T2>(): Optional<[T1, T2], T2>;
export type Transform<Main, Child> = [Optional<Main, Child>, (c: Child | undefined) => Child];
interface DisplayTransform {
    opt: string;
    value: any;
}
export declare function displayTransformsInState<S>(main: S, txs: Transform<S, any>[]): DisplayTransform[];
export declare function massTransform<Main>(main: Main, ...transforms: Transform<Main, any>[]): Main;
export declare function nameLensFn<S, T extends NameAnd<any>>(lens: Optional<S, T>): GetNameFn<S, any>;
export declare function asGetNameFn<S, T>(nl: NameAndLens<S>): GetNameFn<S, T>;
export type GetNameFn<Main, T> = (name: string) => GetOptioner<Main, T>;
export interface NameAndLens<S> {
    [name: string]: Optional<S, any>;
}
export interface NameAndLensFn<S> {
    [name: string]: (o: Iso<S, S>) => Optional<S, any>;
}
export {};
