/// <reference types="node" />
import * as net from 'net';
import { CustomError } from 'ts-custom-error';
export declare const exception_codes: {
    AuthRequiredError: number;
    AuthWrongCredentialsError: number;
    AuthMissingCredentials: number;
    ServerError: number;
    AuthError: number;
    ClientError: number;
    ConnectionError: number;
    ServerDisconnectError: number;
};
export interface CustomLogger {
    debug: (m: string) => void;
    info: (m: string) => void;
    warning: (m: string) => void;
    error: (m: string) => void;
}
/**
 * The logger
 * @group Logging
 */
export declare const log: {
    /**
     * Enable or disable logging
     */
    enabled: boolean;
    /**
     * Can be set to a custom logger that will be used instead of console
     */
    logger: CustomLogger;
    d: (msg: string) => void;
    i: (msg: string) => void;
    w: (msg: string) => void;
    e: (msg: string) => void;
};
declare class EError extends CustomError {
    code: number;
    constructor(message: string);
}
/**
 * Base class for all server errors
 * @extends Error
 * @category Errors
 */
export declare class ServerError extends EError {
    constructor(message: string);
}
/**
 * Base class for all authentication errors
 * @extends ServerError
 * @category Errors
 */
export declare class AuthError extends ServerError {
    constructor(message: string);
}
/**
 * Wrong credentials error
 * @extends AuthError
 * @category Errors
 */
export declare class AuthWrongCredentialsError extends AuthError {
    constructor(message: string);
}
/**
 * Authentication required error
 * @extends AuthError
 * @category Errors
 */
export declare class AuthRequiredError extends AuthError {
    constructor(message: string);
}
/**
 * Missing credentials error
 * @extends AuthError
 * @category Errors
 */
export declare class AuthMissingCredentials extends AuthError {
    constructor(message: string);
}
/**
 * Base class for all client errors
 * @extends ServerError
 * @category Errors
 */
export declare class ClientError extends ServerError {
    constructor(message: string);
}
/**
 * Timeout error
 * @extends ClientError
 * @category Errors
 */
export declare class TimeoutError extends ClientError {
    constructor(message: string);
}
/**
 * Base class for all connection errors
 * @extends ClientError
 * @category Errors
 */
export declare class ConnectionError extends ClientError {
    constructor(message: string);
}
/**
 * Server disconnect error
 * @extends ConnectionError
 * @category Errors
 */
export declare class ServerDisconnectError extends ConnectionError {
}
export type AnyJson = boolean | number | string | null | JsonArray | JsonMap;
export type JsonMap = {
    [key: string]: AnyJson;
};
export interface JsonArray extends Array<AnyJson> {
}
export type Msg = JsonMap;
export type JsonMapCompatible<TKeys extends string = string> = {
    [Key in TKeys]: AnyJson;
};
export type ServerCommand = "handshake" | "call" | "requestauth" | "dropauth" | "serverquit" | "serverrestart" | 1 | 2 | 3 | 4 | 5 | 6;
/**
 * A helper function that will wrap your message up like this:
 * ```
 * msg = {
 *      'session': session_id,
 *      'name': name,
 *      'command': command, <--- your command is put here
 *      'data': data, # <--- your message is put here
 *   }
 * ```
 * @param  {ServerCommand} command - message to wrap
 * @param  {AnyJson} data - message to wrap
 * @param  {string} session_id - optional, session id
 * @param  {string} name - name of client
 * @param  {AnyJson} msg_id - optional message id
 * @return {ServerMsg}
 */
export declare function finalize<C extends ServerCommand>(command: C, data: ClientMsg<C>["data"], session_id?: string | null, name?: string, msg_id?: ClientMsg<C>["__id__"]): ClientMsg<C>;
type Version = {
    core: [number, number, number];
    db: [number, number, number];
    torrent: [number, number, number];
};
export type ServerErrorMsg = {
    code: number;
    msg: string;
};
export interface ClientMsgBase<D = AnyJson> {
    __id__?: string | number;
    session: string;
    command: ServerCommand | AnyJson;
    name: string;
    data?: D;
}
export interface ClientMsg<C extends ServerCommand = "call"> extends ClientMsgBase {
    command: C;
    data: C extends "call" ? ClientFunctionMsg[] : C extends "handshake" ? {
        username: string;
        password: string;
    } | {} | undefined : AnyJson;
}
type ServerInfo = {
    version: Version;
    guest_allowed: boolean;
};
export interface ServerMsgBase<D = AnyJson> {
    __id__?: string | number;
    session: string;
    name: string;
    data: D;
    error?: ServerErrorMsg;
}
export interface ServerMsg<C extends ServerCommand> extends ServerMsgBase {
    data: C extends "call" ? ServerFunctionMsg[] : C extends "handshake" ? "Authenticated" | null : C extends "requestauth" | "dropauth" ? ServerInfo | null : C extends "serverquit" | "serverrestart" ? "ok" | null : AnyJson;
}
export type ServerFunctionMsg = {
    fname: string;
    data: AnyJson;
    error?: ServerErrorMsg;
};
export type ClientFunctionMsg = {
    fname: string;
    [arg: string]: AnyJson;
};
/**
 * A class representing a HappyPanda X client
 */
export declare class Client {
    name: string;
    endpoint: [string, number];
    /**
     * Whether to resolve localhost to IPv4 (default: true), see https://github.com/nodejs/node/issues/40702
     */
    resolve_IPV4_localhost: boolean;
    version: Version | null;
    guest_allowed: boolean;
    session: string;
    private _id_counter;
    private _alive;
    private _disconnected;
    private _ready;
    private _last_user;
    private _last_pass;
    private _stream;
    private _timeout;
    private _sock;
    private _encoder;
    private _decoder;
    private _connecting;
    private __data_promises_order;
    private __data_promises;
    /**
     * @param  {Object} params - optional params
     * @param  [params.name=js-client] {string} - name of client
     * @param  [params.host] {string} - server host
     * @param  [params.port] {integer} - server port
     * @param  [params.user] {string} - username
     * @param  [params.password] {string} - password
     * @param  [params.session_id] {string} - a server session id
     * @param  [params.timeout] {integer} - connection timeout
     */
    constructor({ name, host, port, session_id, timeout, user, password, }: Partial<{
        name: string;
        host: string;
        user: string | null;
        password: string | null;
        port: number;
        session_id: string;
        timeout: number;
    }>);
    private _create_socket;
    /**
     *  Add a listener to the underlying socket
     * @param  {string} event - event name
     * @param  {Function} listener - listener function
     * @return {Client}
     */
    on(event: 'connect' | 'close' | 'end' | 'error' | 'timeout', listener: (...args: any[]) => void): this;
    /**
     * Add a listener to the underlying socket
     * @param  {string} event - event name
     * @param  {Function} listener - listener function
     * @return {Client}
     */
    once(...args: Parameters<net.Socket["once"]>): this;
    /**
     * Remove a listener from the underlying socket
     * @param  {string} event - event name
     * @param  {Function} listener - listener function
     * @return {Client}
     */
    off(...args: Parameters<net.Socket["off"]>): this;
    /**
     * Check if server is still alive
     * @return {boolean}
     */
    alive(): boolean;
    /**
     * Check if client is ready to exchange messages with server
     * @return {boolean}
     */
    ready(): boolean;
    /**
     * Get the timeout value
     * @return {number}
     */
    get timeout(): number;
    set timeout(t: number);
    private get _connect_msg_id();
    private get _close_msg_id();
    private _next_id;
    private _add_data_promise;
    private _get_data_promise;
    private _get_earliest_data_promise;
    private _server_info;
    /**
     * Set server address
     * @param  {string} host - server host
     * @param  {integer} port - server port
     */
    set_endpoint(host: string, port: number): void;
    /**
     * Perfom a handshake with the HPX server
     * @category async
     * @param  {object} params - optinal params
     * @param  [params.user] {string} - username
     * @param  [params.password] {integer} - password
     * @param  [params.ignore_err] {boolean} - ignore error
     * @throws {AuthError}
     * @returns {Promise}
     */
    handshake(params: Partial<{
        user: string | null;
        password: string | null;
        ignore_err: boolean;
    }>): Promise<boolean>;
    /**
     * Forces client to request a new handshake, but doesn't invalidate previous session
     * @category async
     * @returns {Promise}
     */
    request_auth(): Promise<void>;
    /**
     * Logout and invalidates the session
     * @category async
     * @returns {Promise}
     */
    drop_auth(): Promise<void>;
    /**
     * Send the serverquit command
     * @category async
     * @returns {Promise}
     */
    server_quit(): Promise<ServerMsg<"serverquit">>;
    /**
     * Send the serverrestart command
     * @category async
     * @returns {Promise}
     */
    server_restart(): Promise<ServerMsg<"serverrestart">>;
    /**
     * Call a single function, this is even more of a shortcut than send
     * @category async
     * @param  {ClientMsg<'call'>['data']} data - data
     * @returns {Promise}
     */
    call(data: ClientMsg<'call'>['data']): Promise<ServerMsg<"call">>;
    /**
     * Call a single function, this is even more of a shortcut than send
     * @category async
     * @param  {string} fname - function name
     * @param  [args] {object} - function arguments
     * @returns {Promise}
     */
    call_function(fname: string, args?: Record<string, AnyJson>): Promise<ServerMsg<"call">>;
    /**
     * Check if the client is still connected to the server
     * @return {boolean}
     */
    is_connected(): boolean;
    /**
     * Connect to HPX server
     * @category async
     * @param  {object} params - optional params
     * @param  [params.host] {string} - server host
     * @param  [params.port] {integer} - server port
     * @returns {Promise}
     */
    connect(params?: {
        host?: string;
        port?: number;
    }): Promise<ServerMsgBase<ServerInfo> | undefined>;
    private _on_timeout;
    private _on_connect;
    private _on_error;
    private _on_disconnect;
    private _disconnect;
    /**
     * Like {@link send_raw}, but as a convenience, this method will wrap your message into the required message structure HPX expects and automatically sets the session and name
     * @category async
     * @param  {JsonMap} data - the data part of the message
     * @param  {ServerCommand} command - the command, defaults to 'call'
     * @returns {Promise}
     * @fullfil {Object} - message from server
     */
    send<C extends ServerCommand>(command: C, data: ClientMsg<C>["data"]): Promise<ServerMsg<C>>;
    /**
     * Send json-compatible Object to server. Receive json-compatible Object from server.
     *
     * Note that this method will not modify your message and expects you to add the name and session yourself. See the {@link finalize} function.
     * @category async
     * @param  {Object} msg - message to send to the server
     * @returns {Promise}
     * @fullfil {Object} - message from server
     */
    send_raw<C extends ServerCommand>(msg: ClientMsg<C>): Promise<ServerMsg<C>>;
    private _send;
    private _recv;
    /**
     * Close the connection
     * @returns {Promise}
     */
    close(): Promise<void>;
}
export default Client;
