import { IComputedValue, IObservableValue } from 'mobx';
import * as util from 'util';
import * as rxjs from 'rxjs';
import { OperatorFunction, Observable } from 'rxjs';

/**
 * TypeScript type guard to assert the type of a non-empty array
 * @param array Any array to check for non-emptiness
 * @returns True if the empty is non-empty, else false. TypeScript accepts the array as non-empty after the assertion.
 */
declare function isNotEmpty<T>(array: T[]): array is [T, ...T[]];
/**
 * Filters undefined values from an array and lets TypeScript know the resulting array
 * does not have undefined values
 * @param array Array potentially including undefined values
 * @returns Array without undefined values
 */
declare function filterNullishValues<T>(array: (T | undefined)[]): T[];

/**
 * A convenient way to create a promise with resolve and reject functions.
 * @returns Tuple with resolve function, reject function and promise.
 */
declare function deferred<T>(): [(t: T) => void, (t: Error) => void, Promise<T>];

/**
 * @param comp Computed/Observable value that is either defined or undefined
 * @returns promise that resolves with the first truthy computed value
 */
declare function awaitValue<T>(comp: IComputedValue<T | undefined> | IObservableValue<T | undefined>): Promise<T>;

declare function enableLogger(): {
    log: (...logs: string[]) => void;
    logWithTopic: (topic: string, ...logs: string[]) => void;
    enableFilters: () => void;
    disableFilters: () => void;
    addTopic: (topic: string) => void;
    removeTopic: (topic: string) => void;
    resetTopics: () => void;
    assert(condition?: boolean | undefined, ...data: any[]): void;
    assert(value: any, message?: string | undefined, ...optionalParams: any[]): void;
    clear(): void;
    clear(): void;
    count(label?: string | undefined): void;
    count(label?: string | undefined): void;
    countReset(label?: string | undefined): void;
    countReset(label?: string | undefined): void;
    debug(...data: any[]): void;
    debug(message?: any, ...optionalParams: any[]): void;
    dir(item?: any, options?: any): void;
    dir(obj: any, options?: util.InspectOptions | undefined): void;
    dirxml(...data: any[]): void;
    dirxml(...data: any[]): void;
    error(...data: any[]): void;
    error(message?: any, ...optionalParams: any[]): void;
    group(...data: any[]): void;
    group(...label: any[]): void;
    groupCollapsed(...data: any[]): void;
    groupCollapsed(...label: any[]): void;
    groupEnd(): void;
    groupEnd(): void;
    info(...data: any[]): void;
    info(message?: any, ...optionalParams: any[]): void;
    table(tabularData?: any, properties?: string[] | undefined): void;
    table(tabularData: any, properties?: readonly string[] | undefined): void;
    time(label?: string | undefined): void;
    time(label?: string | undefined): void;
    timeEnd(label?: string | undefined): void;
    timeEnd(label?: string | undefined): void;
    timeLog(label?: string | undefined, ...data: any[]): void;
    timeLog(label?: string | undefined, ...data: any[]): void;
    timeStamp(label?: string | undefined): void;
    timeStamp(label?: string | undefined): void;
    trace(...data: any[]): void;
    trace(message?: any, ...optionalParams: any[]): void;
    warn(...data: any[]): void;
    warn(message?: any, ...optionalParams: any[]): void;
    Console: console.ConsoleConstructor;
    profile(label?: string | undefined): void;
    profileEnd(label?: string | undefined): void;
};

type Func<A extends any[], R> = (...args: A) => R;
type AsyncFunc<A extends any[], R> = R extends Promise<unknown> ? (...args: A) => R : (...args: A) => Promise<R>;
type CachedValue<V, Proxied extends boolean> = Proxied extends true ? V extends Func<infer A, infer B> ? AsyncFunc<A, B> : V extends Record<string, any> ? Cached<V> & {
    proxied: true;
} : {
    proxied: true;
} : V extends Func<infer A, infer B> ? Func<A, B> : V extends Record<string, any> ? V : V & {
    proxied: false;
};
type Cached<C> = ({
    proxied: false;
} & {
    [key in keyof C]: CachedValue<C[key], false>;
}) | ({
    proxied: true;
} & {
    [key in keyof C]: CachedValue<C[key], true>;
});
/**
 * @deprecated Use Awaited<T> instead
 */
type PromiseValue<T> = Awaited<T>;
type ValueOf<T extends object> = T[keyof T];
type Area = {
    x: number;
    y: number;
    width: number;
    height: number;
};
type Coord = {
    x: number;
    y: number;
};
type VoxelCoord = {
    x: number;
    y: number;
    z: number;
};
type Logger = ReturnType<typeof enableLogger>;

declare function isObject(c: unknown): c is Record<string, any>;
declare function isFunction(c: unknown): c is Func<any, any>;

/**
 * Caches any function calls to the target until the target is ready.
 * @param target T extends Cachable
 * @returns Cached<T>
 */
declare function cacheUntilReady<T extends Record<string, any>>(target: IObservableValue<T | undefined> | IComputedValue<T | undefined>): Cached<T>;

/**
 * @param enm Numeric enum
 * @returns Number array containing the enum values
 */
declare function numValues(enm: object): number[];

/**
 * Utility function to map a source object to an object with the same keys but mapped values
 * @param source Source object to be mapped
 * @param valueMap Mapping values of the source object to values of the target object
 * @returns An object with the same keys as the source object but mapped values
 */
declare function mapObject<S extends {
    [key: string]: unknown;
}, T extends {
    [key in keyof S]: unknown;
}>(source: S, valueMap: (value: S[keyof S], key: keyof S) => T[keyof S]): T;

/**
 *
 * @param to Upper bound (included)
 * @param from Lower bound (included). Default 0.
 * @returns A random integer between from and to.
 */
declare function random(to: number, from?: number): number;
/**
 * @param array Array to pick a random element from.
 * @returns Random element from the given array.
 */
declare function pickRandom<T>(array: [T, ...T[]]): T;

declare function filterNullish<T>(): OperatorFunction<T, NonNullable<T>>;
declare function awaitPromise<T extends Promise<unknown>>(): OperatorFunction<T, Awaited<T>>;
/**
 * RxJS operator to stretch out an event stream by a given delay per event
 * @param spacingDelayMs Delay between each event in ms
 * @returns stream of events with at least spacingDelayMs spaceing between event
 */
declare function stretch<T>(spacingDelayMs: number): rxjs.UnaryFunction<Observable<T>, Observable<T>>;
declare function observableToComputed<T>(obs: IObservableValue<T>): IComputedValue<T>;
declare function computedToStream<T>(comp: IComputedValue<T> | IObservableValue<T>): Observable<T>;
declare function observableToStream<T>(obs: T): Observable<T>;
declare function streamToComputed<T>(stream$: Observable<T>): IComputedValue<T | undefined>;
declare function streamToDefinedComputed<T>(stream$: Observable<T>): Promise<IComputedValue<T>>;
/**
 *
 * @param stream$ RxJS observable to check for the given value
 * @param predicate Predicate to check
 * @returns A promise that resolves with the requested value once the predicate is true
 */
declare function awaitStreamValue<T>(stream$: Observable<T>, predicate?: (value: T) => boolean): Promise<T>;
/**
 * Turns a stream into an updating object for easy access outside of rxjs
 * @param stream$ Stream to turn into a wrapped value
 * @returns Object with `current` key corresponding to last stream value
 */
declare function streamToWrappedValue<T>(stream$: Observable<T>): Promise<{
    current: T;
}>;

/**
 * UUID.core.js - UUID.js for Minimalists
 *
 * @file
 * @author  LiosK
 * @version v4.2.0
 * @license Apache License 2.0: Copyright (c) 2010-2018 LiosK
 * @url https://github.com/LiosK/UUID.js/blob/master/src/uuid.core.js
 */
/**
 * @class
 * @classdesc {@link UUID} object.
 * @hideconstructor
 */
/**
 * Generates a version 4 UUID as a hexadecimal string.
 * @returns {string} Hexadecimal UUID string.
 */
declare const uuid: () => string;

declare const range: (total?: number, step?: number, from?: number) => Generator<number, void, unknown>;
declare function rejectAfter<T>(ms: number, msg: string): Promise<T>;
declare const timeoutAfter: <T>(promise: Promise<T>, ms: number, timeoutMsg: string) => Promise<T>;
declare const callWithRetry: <T>(fn: (...args: any[]) => Promise<T>, args?: any[], maxRetries?: number, retryInterval?: number) => Promise<T>;

declare function sleep<T>(timeout: number, returns?: T): Promise<T>;

declare function makeIterable<T>(iterator: Iterator<T>): IterableIterator<T>;
declare function concatIterators<T>(first: Iterator<T>, second?: Iterator<T>): IterableIterator<T>;
declare function mergeIterators<A, B>(iteratorA: Iterator<A>, iteratorB: Iterator<B>): IterableIterator<[A, B]>;
declare function transformIterator<A, B>(iterator: Iterator<A>, transform: (value: A) => B): IterableIterator<B>;
/**
 * Turns an array into an iterator. NOTE: an iterator can only be iterated once.
 * @param array Array to be turned into an iterator
 * @returns Iterator to iterate through the array
 */
declare function arrayToIterator<T>(array: T[]): IterableIterator<T>;

declare function areaContains(area: Area, coord: Coord): boolean;
declare function coordsOf(area: Area): Coord[];

interface DoWork<In, Out> {
    work(input$: Observable<In>): Observable<Out>;
}
declare function fromWorker<I, O>(worker: Worker, input$: Observable<I>): Observable<O>;
declare function runWorker<I, O>(worker: DoWork<I, O>): void;

/**
 * Packs two unsigned integers in one 32 bit unsigned integer
 * @param numbers Unsigned integers to be packed in 32 bit integer
 * @param bitsPerNumber Bits for each number
 * @returns Packed 32 bit unsigned integer
 */
declare function pack(numbers: number[], bitsPerNumber: number[]): number;
/**
 * Unpacks a packed 32 bit unsigned integer into the original unsigned integers
 * @param packed Packed 32 bit unsigned integer
 * @param bitsPerNumber Bits for each unsigned integer
 * @returns Array of unpacked unsignd integers
 */
declare function unpack(packed: number, bitsPerNumber: number[]): number[];
declare function packTuple(numbers: [number, number]): number;
declare function unpackTuple(packed: number): [number, number];

declare function subtract(from: CoordMap<boolean>, subtract: CoordMap<boolean>): CoordMap<boolean>;
declare function coordToKey(coord: Coord): number;
declare function keyToCoord(key: number): Coord;
declare class CoordMap<T> {
    map: Map<number, T>;
    defaultValue?: T;
    constructor(props?: {
        defaultValue?: T;
    });
    static from<T>(coordMapLike: {
        map: Map<number, T>;
        defaultValue?: T;
    }): CoordMap<T>;
    set(coord: Coord, value: T): Map<number, T>;
    get(coord: Coord): T | undefined;
    keys(): IterableIterator<number>;
    coords(): IterableIterator<Coord>;
    entries(): IterableIterator<[number, T]>;
    toArray(): [Coord, T][];
    values(): IterableIterator<T>;
    delete(coord: Coord): boolean;
    has(coord: Coord): boolean;
    clear(): void;
    get size(): number;
}

declare class VoxelCoordMap<T> {
    map: Map<string, T>;
    defaultValue?: T;
    constructor(props?: {
        defaultValue?: T;
    });
    static from<T>(coordMapLike: {
        map: Map<string, T>;
        defaultValue?: T;
    }): VoxelCoordMap<T>;
    set(coord: VoxelCoord, value: T): Map<string, T>;
    get(coord: VoxelCoord): T | undefined;
    keys(): IterableIterator<string>;
    coords(): IterableIterator<VoxelCoord>;
    entries(): IterableIterator<[string, T]>;
    toArray(): [VoxelCoord, T][];
    values(): IterableIterator<T>;
    delete(coord: VoxelCoord): boolean;
    has(coord: VoxelCoord): boolean;
    clear(): void;
    get size(): number;
}

/**
 * Pads start of a hex string with 0 to create a bit string of the given length
 * @param input Hex string
 * @param bits Number of bits in the output hex string
 * @returns Hex string of specified length
 */
declare function padToBitLength(input: string, bits: number): string;
/**
 * Pads start of a hex string with 0 to create a 160 bit hex string
 * which can be used as an Ethereum address
 * @param input Hex string
 * @returns 160 bit hex string
 */
declare function toEthAddress(input: string): string;
/**
 * Pads start of a hex string with 0 to create a 256bit hex string
 * which can be used as an Ethereum address
 * @param input Hex string
 * @returns 256 bit hex string
 */
declare function to256BitString(input: string): string;
declare function extractEncodedArguments(input: string): string;

declare function randomize(seed: number, x: number, y: number): number;
declare function tile(coordinate: number, period: number): number;
declare function interpolate(a: number, b: number, c: number, d: number, x: number, s: number, scale: number): number;
/**
 * Config a cubic noise.
 * @param {Number} seed A seed in the range [0, 1].
 * @param {Number} [periodX] The number of units after which the x coordinate repeats.
 * @param {Number} [periodY] The number of units after which the y coordinate repeats.
 * @returns {Object} A configuration object used by noise functions.
 */
declare function cubicNoiseConfig(seed: number, octave: number, scale: number, periodX?: number, periodY?: number): {
    seed: number;
    periodX: number;
    periodY: number;
    octave: number;
    scale: number;
};
/**
 * Sample 1D cubic noise.
 * @param {Object} config A valid noise configuration.
 * @param {Number} x The X position to sample at.
 * @returns {Number} A noise value in the range [0, 1].
 */
declare function cubicNoiseSample1(config: ReturnType<typeof cubicNoiseConfig>, x: number): number;
/**
 * Sample 2D cubic noise.
 * @param {Object} config A valid noise configuration.
 * @param {Number} x The X position to sample at.
 * @param {Number} y The Y position to sample at.
 * @returns {Number} A noise value in the range [0, 1].
 */
declare function cubicNoiseSample2({ octave, periodX, periodY, seed, scale }: ReturnType<typeof cubicNoiseConfig>, x: number, y: number): number;

/**
 * Compute the Euclidean distance between two points
 * https://en.wikipedia.org/wiki/Euclidean_distance
 * @param a
 * @param b
 * @returns Euclidian distance between a and b
 */
declare function euclidean(a: number[], b: number[]): number;

/**
 * For positive inputs: returns the greatest integer less than or equal to its numeric argument.
 * For negative inputs: returns the smallest integer greater than or equal to its numeric argument.
 *
 * @param x A numeric expression.
 * @returns Input rounded towards zero.
 */
declare function roundTowardsZero(x: number): number;

declare function formatHex(hex: string): string;
declare function hexStringToUint8Array(hexString: string): Uint8Array;
declare function Uint8ArrayToHexString(data: Uint8Array): string;
declare function concatUint8Arrays(...arrays: Uint8Array[]): Uint8Array;
declare function splitUint8Arrays(data: Uint8Array, byteLengths: number[]): Uint8Array[];
declare function Int32ArrayToUint8Array(input: number[]): Uint8Array;
declare function Uint8ArrayToInt32Array(input: Uint8Array): number[];
declare function ethAddressToUint8Array(address: string): Uint8Array;
declare function createToInt(size: number): (value: number) => number;
declare const toInt32: (value: number) => number;

declare const arrayToHex: (array: Uint8Array | ArrayBuffer) => `0x${string}`;

declare const bytesToString: (bytes: Uint8Array) => string;

declare const hexToArray: (hex: string) => Uint8Array;

declare function isHex(hex: string): boolean;

declare const stringToBytes16: (str: string) => Uint8Array;
declare const stringToBytes32: (str: string) => Uint8Array;

export { Area, AsyncFunc, Cached, CachedValue, Coord, CoordMap, DoWork, Func, Int32ArrayToUint8Array, Logger, PromiseValue, Uint8ArrayToHexString, Uint8ArrayToInt32Array, ValueOf, VoxelCoord, VoxelCoordMap, areaContains, arrayToHex, arrayToIterator, awaitPromise, awaitStreamValue, awaitValue, bytesToString, cacheUntilReady, callWithRetry, computedToStream, concatIterators, concatUint8Arrays, coordToKey, coordsOf, createToInt, cubicNoiseConfig, cubicNoiseSample1, cubicNoiseSample2, deferred, enableLogger, ethAddressToUint8Array, euclidean, extractEncodedArguments, filterNullish, filterNullishValues, formatHex, fromWorker, hexStringToUint8Array, hexToArray, interpolate, isFunction, isHex, isNotEmpty, isObject, keyToCoord, makeIterable, mapObject, mergeIterators, numValues, observableToComputed, observableToStream, pack, packTuple, padToBitLength, pickRandom, random, randomize, range, rejectAfter, roundTowardsZero, runWorker, sleep, splitUint8Arrays, streamToComputed, streamToDefinedComputed, streamToWrappedValue, stretch, stringToBytes16, stringToBytes32, subtract, tile, timeoutAfter, to256BitString, toEthAddress, toInt32, transformIterator, unpack, unpackTuple, uuid };
