/// <reference types="node" />
import { ClientInterface } from "pocket-sockets";
import EventEmitter from "eventemitter3";
import { SentMessage, Header, OutgoingQueue, IncomingQueue, EventType, SendReturn } from "./types";
export declare class Messaging {
    /**
     * Messages sent from here which are expecting replies.
     *
     */
    protected pendingReply: {
        [msgId: string]: SentMessage;
    };
    /**
     * Data read on socket and transformed to messages.
     */
    protected incomingQueue: IncomingQueue;
    /**
     * Messages transformed and sent.
     */
    protected outgoingQueue: OutgoingQueue;
    /**
     * The general event emitter for incoming messages and socket events.
     * Reply messages are not emitted using this object but are emitted on message specific event emitters.
     */
    protected eventEmitter: EventEmitter;
    /**
     * The given client socket to communicate with.
     */
    protected socket: ClientInterface;
    /**
     * Set to true if we have opened.
     */
    protected _isOpened: boolean;
    /**
     * Set to true if we have closed.
     */
    protected _isClosed: boolean;
    /** ID of the timeout in use for ping. */
    protected pingTimeout: ReturnType<typeof setTimeout> | undefined;
    /** Milliseconds to wait between each ping. */
    protected pingInterval: number;
    /** Keep track of pending ping, pong resets it. */
    protected pingTimestamp: number;
    /**
     * How many messages we allow through.
     * 0 means cork it up
     * -1 means unlimited.
     */
    protected dispatchLimit: number;
    protected isBusyOut: number;
    protected isBusyIn: number;
    protected instanceId: string;
    /**
     * @param socket the underlying socket to use. Socket must be in binary mode.
     * @param pingInterval in milliseconds, set to send frequent pings on the socket to detect silent disconnects.
     */
    constructor(socket: ClientInterface, pingInterval?: number);
    getInstanceId(): string;
    /**
     * Remove a stored pending message so that it cannot receive any more replies.
     */
    cancelPendingMessage: (msgId: Buffer) => void;
    /**
     * See if a specific msgId is pending a reply.
     * @param msgId the ID of the message to check if it is pending a reply.
     * @returns true if message identified by msgId is pending a reply.
     */
    isMessagePending: (msgId: Buffer) => boolean;
    /**
     * This pauses all timeouts for a message until the next message arrives then timeouts are re-activated (if set initially ofc).
     * This could be useful when expecting a never ending stream of messages where chunks could be time apart.
     */
    clearTimeout: (msgId: Buffer) => void;
    /**
     * Get the general event emitter object.
     * This is used to listen for incoming messages
     * and socket events such as close and error.
     */
    getEventEmitter(): EventEmitter;
    /**
     * Open this Messaging for inbound data.
     *
     * Do not open it until you have hooked the event emitter
     * to not loose any incoming data.
     *
     * It is technically allowed to send data before opening,
     * but the Messaging should be opened very shortly after
     * sending so that replies can come through and so that
     * timeouts are processed properly.
     */
    open(): void;
    isOpen(): boolean;
    isOpened(): boolean;
    isClosed(): boolean;
    /**
     * Close this Messaging object and it's socket.
     *
     */
    close(): void;
    cork(): void;
    uncork(limit?: number): void;
    /**
     * Send message to remote.
     *
     * The returned EventEmitter can be hooked as eventEmitter.on("reply", fn) or
     *  const data: ReplyEvent = await once(eventEmitter, "reply");
     *  Other events are "close" (CloseEvent) and "any" which trigger both for "reply", "close" and "error" (ErrorEvent). There is also "timeout" (TimeoutEvent).
     *
     * A timeouted message is removed from memory and a TIMEOUT is emitted.
     *
     * @param target: Buffer | string either set as routing target as string, or as message ID in reply to (as buffer).
     *  The receiving Messaging instance will check if target matches a msg ID which is waiting for a reply and in such case the message till be emitted on that EventEmitter,
     *  or else it will pass it to the router to see if it matches some route.
     * @param data: Buffer of data to be sent. Note that data (payload) cannot exceed MESSAGE_MAX_BYTES.
     * @param timeout milliseconds to wait for the first reply (defaults to -1)
     *     -1 means we are not expecting a reply
     *     0 or greater means that we are expecting a reply, 0 means wait forever
     * @param stream set to true if expecting multiple replies (defaults to false)
     *     This requires that timeout is set to 0 or greater
     * @param timeoutStream milliseconds to wait for secondary replies, 0 means forever (default).
     *     Only relevant if expecting multiple replies (stream = true).
     * @return SendReturn | undefined
     *     SendReturn.msgId is always set
     *     SendReturn.eventEmitter property is set if expecting reply
     *     undefined is returned as a silent error when the Messaging is closed and data cannot be sent again on this Messaging instance.
     * @throws on malformed input
     */
    send(target: Buffer | string, data?: Buffer, timeout?: number, stream?: boolean, timeoutStream?: number): SendReturn | undefined;
    /**
     * Enable to send frequent pings on the socket.
     * This will help to detect silent disconnects.
     * @param pingInterval how many milliseconds to wait between each ping.
     * Default is 10000 (10 sec). 0 means disabled.
     */
    enablePing(pingInterval?: number): void;
    disablePing(): void;
    /**
     * @returns the underlaying socket client.
     */
    getClient(): ClientInterface;
    protected getNow(): number;
    static GenerateMsgId(): Buffer;
    /**
     * @throws on malformed input
     */
    static EncodeHeader(header: Header): Buffer;
    /**
     * @throws on malformed input
     */
    static DecodeHeader(buffer: Buffer): [Header, Buffer];
    /**
    * Extract length as single buffer and modify the buffers array in place.
    *
    */
    protected extractBuffer(buffers: Buffer[], length: number): Buffer | undefined;
    protected emitEvent(eventEmitters: EventEmitter[], eventType: EventType, arg?: any): void;
    protected getAllEventEmitters(): EventEmitter[];
    /**
     * Notify all pending messages and the main emitter about an error.
     *
     * @param message the error message
     *
     */
    protected emitError: (message: string) => void;
    /**
     * Notify all pending messages about the close.
     */
    protected socketClose: (hadError: boolean) => void;
    /**
     * Send a ping to remote to force a disconnect event in the case
     * the socket has silently closed.
     * There is no reply expected on the ping.
     */
    protected sendPing: () => void;
    /**
     * Buffer incoming raw data from the socket and process it.
     */
    protected socketData: (data: Buffer | string) => void;
    protected processInqueue: () => Promise<void>;
    /**
     * Assemble messages from decrypted data and put to next queue.
     *
     */
    protected assembleIncoming: () => boolean;
    /**
     * Dispatch messages on event emitters.
     *
     */
    protected dispatchIncoming: () => void;
    protected processOutqueue: () => void;
    protected dispatchOutgoing: () => void;
    /**
     * Check every pending message to see which have timeouted.
     *
     */
    protected checkTimeouts: () => void;
    protected getTimeoutedPendingMessages(): SentMessage[];
}
/**
* Mimicking the async/await once function from the nodejs events module.
* Because EventEmitter3 module doesn't seem to support the async/await promise feature of nodejs events once() function.
*/
export declare function once(eventEmitter: EventEmitter, eventName: string | symbol): Promise<any>;
