// Type definitions for socket.io 2.1
// Project: http://socket.io/
// Definitions by: PROGRE <https://github.com/progre>
//                 Damian Connolly <https://github.com/divillysausages>
//                 Florent Poujol <https://github.com/florentpoujol>
//                 KentarouTakeda <https://github.com/KentarouTakeda>
//                 Alexey Snigirev <https://github.com/gigi>
//                 Ezinwa Okpoechi <https://github.com/BrainMaestro>
//                 Marek Urbanowicz <https://github.com/murbanowicz>
//                 Kevin Kam <https://github.com/kevinkam>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

///<reference types="node" />

declare const SocketIO: SocketIOStatic;
export = SocketIO;
/** @deprecated Available as a global for backwards-compatibility. */
export as namespace SocketIO;

interface SocketIOStatic {
	/**
	 * Default Server constructor
	 */
	(): SocketIO.Server;

	/**
	 * Creates a new Server
	 * @param srv The HTTP server that we're going to bind to
	 * @param opts An optional parameters object
	 */
	(srv: any, opts?: SocketIO.ServerOptions): SocketIO.Server;

	/**
	 * Creates a new Server
	 * @param port A port to bind to, as a number, or a string
	 * @param An optional parameters object
	 */
	(port: string|number, opts?: SocketIO.ServerOptions): SocketIO.Server;

	/**
	 * Creates a new Server
	 * @param A parameters object
	 */
	(opts: SocketIO.ServerOptions): SocketIO.Server;

	/**
	 * Backwards compatibility
	 * @see io().listen()
	 */
    listen: SocketIOStatic;
}

declare namespace SocketIO {
	interface Server {
		engine: { ws: any };

		/**
		 * A dictionary of all the namespaces currently on this Server
		 */
		nsps: {[namespace: string]: Namespace};

		/**
		 * The default '/' Namespace
		 */
		sockets: Namespace;

		/**
		 * Sets the 'json' flag when emitting an event
		 */
		json: Server;

		/**
		 * Sets a modifier for a subsequent event emission that the event data may be lost if the clients are not ready to receive messages
		 */
		volatile: Server;

		/**
		 * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node
		 */
		local: Server;

		/**
		 * Server request verification function, that checks for allowed origins
		 * @param req The http.IncomingMessage request
		 * @param fn The callback to be called. It should take one parameter, err,
		 * which will be null if there was no problem, and one parameter, success,
		 * of type boolean
		 */
		checkRequest( req:any, fn:( err: any, success: boolean ) => void ):void;

		/**
		 * Gets whether we're serving the client.js file or not
		 * @default true
		 */
		serveClient(): boolean;

		/**
		 * Sets whether we're serving the client.js file or not
		 * @param v True if we want to serve the file, false otherwise
		 * @default true
		 * @return This Server
		 */
		serveClient( v: boolean ): Server;

		/**
		 * Gets the client serving path
		 * @default '/socket.io'
		 */
		path(): string;

		/**
		 * Sets the client serving path
		 * @param v The path to serve the client file on
		 * @default '/socket.io'
		 * @return This Server
		 */
		path( v: string ): Server;

		/**
		 * Gets the adapter that we're going to use for handling rooms
		 * @default typeof Adapter
		 */
		adapter(): any;

		/**
		 * Sets the adapter (class) that we're going to use for handling rooms
		 * @param v The class for the adapter to create
		 * @default typeof Adapter
		 * @return This Server
		 */
		adapter( v: any ): Server;

		/**
		 * Gets the allowed origins for requests
		 * @default "*:*"
		 */
		origins(): string|string[];

		/**
		 * Sets the allowed origins for requests
		 * @param v The allowed origins, in host:port form
		 * @default "*:*"
		 * return This Server
		 */
		origins( v: string|string[] ): Server;

		/**
		 * Provides a function taking two arguments origin:String
		 * and callback(error, success), where success is a boolean
		 * value indicating whether origin is allowed or not. If
		 * success is set to false, error must be provided as a string
		 * value that will be appended to the server response, e.g. “Origin not allowed”.
		 * @param fn The function that will be called to check the origin
		 * return This Server
		 */
		origins( fn: ( origin: string, callback: ( error: string | null, success: boolean ) => void ) => void ): Server;

		/**
		 * Attaches socket.io to a server
		 * @param srv The http.Server that we want to attach to
		 * @param opts An optional parameters object
		 * @return This Server
		 */
		attach( srv: any, opts?: ServerOptions ): Server;

		/**
		 * Attaches socket.io to a port
		 * @param port The port that we want to attach to
		 * @param opts An optional parameters object
		 * @return This Server
		 */
		attach( port: number, opts?: ServerOptions ): Server;

		/**
		 * @see attach( srv, opts )
		 */
		listen( srv: any, opts?: ServerOptions ): Server;

		/**
		 * @see attach( port, opts )
		 */
		listen( port: number, opts?: ServerOptions ): Server;

		/**
		 * Binds socket.io to an engine.io instance
		 * @param src The Engine.io (or compatible) server to bind to
		 * @return This Server
		 */
		bind( srv: any ): Server;

		/**
		 * Called with each incoming connection
		 * @param socket The Engine.io Socket
		 * @return This Server
		 */
		onconnection( socket: any ): Server;

		/**
		 * Looks up/creates a Namespace
		 * @param nsp The name of the NameSpace to look up/create. Should start
		 * with a '/'
		 * @return The Namespace
		 */
		of( nsp: string | RegExp | Function ): Namespace;

		/**
		 * Closes the server connection
		 */
		close( fn ?: () => void ):void;

		/**
		 * The event fired when we get a new connection
		 * @param event The event being fired: 'connection'
		 * @param listener A listener that should take one parameter of type Socket
		 * @return The default '/' Namespace
		 */
		on( event: 'connection', listener: ( socket: Socket ) => void ): Namespace;

		/**
		 * @see on( 'connection', listener )
		 */
		on( event: 'connect', listener: ( socket: Socket ) => void ): Namespace;

		/**
		 * Base 'on' method to add a listener for an event
		 * @param event The event that we want to add a listener for
		 * @param listener The callback to call when we get the event. The parameters
		 * for the callback depend on the event
		 * @return The default '/' Namespace
		 */
		on( event: string, listener: Function ): Namespace;

		/**
		 * Targets a room when emitting to the default '/' Namespace
		 * @param room The name of the room that we're targeting
		 * @return The default '/' Namespace
		 */
		to( room: string ): Namespace;

		/**
		 * @see to( room )
		 */
		in( room: string ): Namespace;

		/**
		 * Registers a middleware function, which is a function that gets executed
		 * for every incoming Socket, on the default '/' Namespace
		 * @param fn The function to call when we get a new incoming socket. It should
		 * take one parameter of type Socket, and one callback function to call to
		 * execute the next middleware function. The callback can take one optional
		 * parameter, err, if there was an error. Errors passed to middleware callbacks
		 * are sent as special 'error' packets to clients
		 * @return The default '/' Namespace
		 */
		use( fn: ( socket:Socket, fn: ( err?: any ) => void ) =>void ): Namespace;

		/**
		 * Emits an event to the default Namespace
		 * @param event The event that we want to emit
		 * @param args Any number of optional arguments to pass with the event. If the
		 * last argument is a function, it will be called as an ack. The ack should
		 * take whatever data was sent with the packet
		 * @return The default '/' Namespace
		 */
		emit( event: string, ...args: any[]): Namespace;

		/**
		 * Sends a 'message' event
		 * @see emit( event, ...args )
		 * @return The default '/' Namespace
		 */
		send( ...args: any[] ): Namespace;

		/**
		 * @see send( ...args )
		 */
		write( ...args: any[] ): Namespace;

		/**
		 * Gets a list of clients
		 * @return The default '/' Namespace
		 */
		clients( ...args: any[] ): Namespace;

		/**
		 * Sets the compress flag
		 * @return The default '/' Namespace
		 */
		compress( ...args: any[] ): Namespace;
	}

	/**
	 * Options to pass to our server when creating it
	 */
	interface ServerOptions {

		/**
		 * The path to server the client file to
		 * @default '/socket.io'
		 */
		path?: string;

		/**
		 * Should we serve the client file?
		 * @default true
		 */
		serveClient?: boolean;

		/**
		 * The adapter to use for handling rooms. NOTE: this should be a class,
		 * not an object
		 * @default typeof Adapter
		 */
		adapter?: Adapter;

		/**
		 * Accepted origins
		 * @default '*:*'
		 */
		origins?: string|string[];

		/**
		 * How many milliseconds without a pong packed to consider the connection closed (engine.io)
		 * @default 60000
		 */
		pingTimeout?: number;

		/**
		 * How many milliseconds before sending a new ping packet (keep-alive) (engine.io)
		 * @default 25000
		 */
		pingInterval?: number;

		/**
		 * How many bytes or characters a message can be when polling, before closing the session
		 * (to avoid Dos) (engine.io)
		 * @default 10E7
		 */
		maxHttpBufferSize?: number;

		/**
		 * A function that receives a given handshake or upgrade request as its first parameter,
		 * and can decide whether to continue or not. The second argument is a function that needs
		 * to be called with the decided information: fn( err, success ), where success is a boolean
		 * value where false means that the request is rejected, and err is an error code (engine.io)
		 * @default null
		 */
		allowRequest?: (request:any, callback: (err: number, success: boolean) => void) => void;

		/**
		 * Transports to allow connections to (engine.io)
		 * @default ['polling','websocket']
		 */
		transports?: string[];

		/**
		 * Whether to allow transport upgrades (engine.io)
		 * @default true
		 */
		allowUpgrades?: boolean;

		/**
		 * parameters of the WebSocket permessage-deflate extension (see ws module).
		 * Set to false to disable (engine.io)
		 * @default true
		 */
		perMessageDeflate?: Object|boolean;

		/**
		 * Parameters of the http compression for the polling transports (see zlib).
		 * Set to false to disable, or set an object with parameter "threshold:number"
		 * to only compress data if the byte size is above this value (1024) (engine.io)
		 * @default true|1024
		 */
		httpCompression?: Object|boolean;

		/**
		 * Name of the HTTP cookie that contains the client sid to send as part of
		 * handshake response headers. Set to false to not send one (engine.io)
		 * @default "io"
		 */
		cookie?: string|boolean;

        /**
         * Whether to let engine.io handle the OPTIONS requests.
         * You can also pass a custom function to handle the requests
         * @default true
         */
        handlePreflightRequest?: ((req: any, res: any) => void) | boolean;
	}

	/**
	 * The Namespace, sandboxed environments for sockets, each connection
	 * to a Namespace requires a new Socket
	 */
	interface Namespace extends NodeJS.EventEmitter {

		/**
		 * The name of the NameSpace
		 */
		name: string;

		/**
		 * The controller Server for this Namespace
		 */
		server: Server;

		/**
		 * A dictionary of all the Sockets connected to this Namespace, where
		 * the Socket ID is the key
		 */
		sockets: { [id: string]: Socket };

		/**
		 * A dictionary of all the Sockets connected to this Namespace, where
		 * the Socket ID is the key
		 */
		connected: { [id: string]: Socket };

		/**
		 * The Adapter that we're using to handle dealing with rooms etc
		 */
		adapter: Adapter;

		/**
		 * Sets the 'json' flag when emitting an event
		 */
		json: Namespace;

		/**
		 * Registers a middleware function, which is a function that gets executed
		 * for every incoming Socket
		 * @param fn The function to call when we get a new incoming socket. It should
		 * take one parameter of type Socket, and one callback function to call to
		 * execute the next middleware function. The callback can take one optional
		 * parameter, err, if there was an error. Errors passed to middleware callbacks
		 * are sent as special 'error' packets to clients
		 * @return This Namespace
		 */
		use( fn: ( socket:Socket, fn: ( err?: any ) => void ) =>void ): Namespace;

		/**
		 * Targets a room when emitting
		 * @param room The name of the room that we're targeting
		 * @return This Namespace
		 */
		to( room: string ): Namespace;

		/**
		 * @see to( room )
		 */
		in( room: string ): Namespace;

		/**
		 * Sends a 'message' event
		 * @see emit( event, ...args )
		 * @return This Namespace
		 */
		send( ...args: any[] ): Namespace;

		/**
		 * @see send( ...args )
		 */
		write( ...args: any[] ): Namespace;

		/**
		 * The event fired when we get a new connection
		 * @param event The event being fired: 'connection'
		 * @param listener A listener that should take one parameter of type Socket
		 * @return This Namespace
		 */
		on( event: 'connection', listener: ( socket: Socket ) => void ): this;

		/**
		 * @see on( 'connection', listener )
		 */
		on( event: 'connect', listener: ( socket: Socket ) => void ): this;

		/**
		 * Base 'on' method to add a listener for an event
		 * @param event The event that we want to add a listener for
		 * @param listener The callback to call when we get the event. The parameters
		 * for the callback depend on the event
		 * @ This Namespace
		 */
		on( event: string, listener: Function ): this;

		/**
		 * Gets a list of clients.
		 * @return This Namespace
		 */
		clients( fn: Function ): Namespace;

		/**
		 * Sets the compress flag.
		 * @param compress If `true`, compresses the sending data
		 * @return This Namespace
		 */
		compress( compress: boolean ): Namespace;
	}

	interface Packet extends Array<any> {
		/**
		 * Event name
		 */
		[0]: string;
		/**
		 * Packet data
		 */
		[1]: any;
		/**
		 * Ack function
		 */
		[2]: (...args: any[]) => void;
	}

	/**
	 * The socket, which handles our connection for a namespace. NOTE: while
	 * we technically extend NodeJS.EventEmitter, we're not putting it here
	 * as we have a problem with the emit() event (as it's overridden with a
	 * different return)
	 */
	interface Socket extends NodeJS.EventEmitter{

		/**
		 * The namespace that this socket is for
		 */
		nsp: Namespace;

		/**
		 * The Server that our namespace is in
		 */
		server: Server;

		/**
		 * The Adapter that we use to handle our rooms
		 */
		adapter: Adapter;

		/**
		 * The unique ID for this Socket. Regenerated at every connection. This is
		 * also the name of the room that the Socket automatically joins on connection
		 */
		id: string;

		/**
		 * The http.IncomingMessage request sent with the connection. Useful
		 * for recovering headers etc
		 */
		request: any;

		/**
		 * The Client associated with this Socket
		 */
		client: Client;

		/**
		 * The underlying Engine.io Socket instance
		 */
		conn: EngineSocket;

		/**
		 * The list of rooms that this Socket is currently in, where
		 * the ID the the room ID
		 */
		rooms: { [id: string]: string };

		/**
		 * Is the Socket currently connected?
		 */
		connected: boolean;

		/**
		 * Is the Socket currently disconnected?
		 */
		disconnected: boolean;

		/**
		 * The object used when negociating the handshake
		 */
		handshake: Handshake;
		/**
		 * Sets the 'json' flag when emitting an event
		 */
		json: Socket;

		/**
		 * Sets the 'volatile' flag when emitting an event. Volatile messages are
		 * messages that can be dropped because of network issues and the like. Use
		 * for high-volume/real-time messages where you don't need to receive *all*
		 * of them
		 */
		volatile: Socket;

		/**
		 * Sets the 'broadcast' flag when emitting an event. Broadcasting an event
		 * will send it to all the other sockets in the namespace except for yourself
		 */
		broadcast: Socket;

		/**
		 * Targets a room when broadcasting
		 * @param room The name of the room that we're targeting
		 * @return This Socket
		 */
		to( room: string ): Socket;

		/**
		 * @see to( room )
		 */
		in( room: string ): Socket;

		/**
		 * Registers a middleware, which is a function that gets executed for every incoming Packet and receives as parameter the packet and a function to optionally defer execution to the next registered middleware.
		 *
		 * Errors passed to middleware callbacks are sent as special error packets to clients.
		 */
		use( fn: ( packet: Packet, next: (err?: any) => void ) => void ): Socket;

		/**
		 * Sends a 'message' event
		 * @see emit( event, ...args )
		 */
		send( ...args: any[] ): Socket;

		/**
		 * @see send( ...args )
		 */
		write( ...args: any[] ): Socket;

		/**
		 * Joins a room. You can join multiple rooms, and by default, on connection,
		 * you join a room with the same name as your ID
		 * @param name The name of the room that we want to join
		 * @param fn An optional callback to call when we've joined the room. It should
		 * take an optional parameter, err, of a possible error
		 * @return This Socket
		 */
		join( name: string|string[], fn?: ( err?: any ) => void ): Socket;

		/**
		 * Leaves a room
		 * @param name The name of the room to leave
		 * @param fn An optional callback to call when we've left the room. It should
		 * take on optional parameter, err, of a possible error
		 */
		leave( name: string, fn?: Function ): Socket;

		/**
		 * Leaves all the rooms that we've joined
		 */
		leaveAll(): void;

		/**
		 * Disconnects this Socket
		 * @param close If true, also closes the underlying connection
		 * @return This Socket
		 */
		disconnect( close?: boolean ): Socket;

		/**
		 * Returns all the callbacks for a particular event
		 * @param event The event that we're looking for the callbacks of
		 * @return An array of callback Functions, or an empty array if we don't have any
		 */
		listeners( event: string ):Function[];

		/**
		 * Sets the compress flag
		 * @param compress If `true`, compresses the sending data
		 * @return This Socket
		 */
		compress( compress: boolean ): Socket;

        /**
         * Emits the error
         * @param err Error message=
         */
        error(err: any): void;
	}

	interface Handshake {
		/**
		 * The headers passed along with the request. e.g. 'host',
		 * 'connection', 'accept', 'referer', 'cookie'
		 */
		headers: any;

		/**
		 * The current time, as a string
		 */
		time: string;

		/**
		 * The remote address of the connection request
		 */
		address: string;

		/**
		 * Is this a cross-domain request?
		 */
		xdomain: boolean;

		/**
		 * Is this a secure request?
		 */
		secure: boolean;

		/**
		 * The timestamp for when this was issued
		 */
		issued: number;

		/**
		 * The request url
		 */
		url: string;

		/**
		 * Any query string parameters in the request url
		 */
		query: any;
	}

	/**
	 * The interface describing a room
	 */
	interface Room {
		sockets: {[id: string]: boolean };
		length: number;
	}

	/**
	 * The interface describing a dictionary of rooms
	 * Where room is the name of the room
	 */

	 interface Rooms {
		[room: string]: Room;
	 }

	/**
	 * The interface used when dealing with rooms etc
	 */
	interface Adapter extends NodeJS.EventEmitter {

		/**
		 * The namespace that this adapter is for
		 */
		nsp: Namespace;

		/**
		 * A dictionary of all the rooms that we have in this namespace
		 */
		rooms: Rooms;

		/**
		 * A dictionary of all the socket ids that we're dealing with, and all
		 * the rooms that the socket is currently in
		 */
		sids: {[id: string]: {[room: string]: boolean}};

		/**
		 * Adds a socket to a room. If the room doesn't exist, it's created
		 * @param id The ID of the socket to add
		 * @param room The name of the room to add the socket to
		 * @param callback An optional callback to call when the socket has been
		 * added. It should take an optional parameter, error, if there was a problem
		 */
		add( id: string, room: string, callback?: ( err?: any ) => void ): void;

		/**
		 * Removes a socket from a room. If there are no more sockets in the room,
		 * the room is deleted
		 * @param id The ID of the socket that we're removing
		 * @param room The name of the room to remove the socket from
		 * @param callback An optional callback to call when the socket has been
		 * removed. It should take on optional parameter, error, if there was a problem
		 */
		del( id: string, room: string, callback?: ( err?: any ) => void ): void;

		/**
		 * Removes a socket from all the rooms that it's joined
		 * @param id The ID of the socket that we're removing
		 */
		delAll( id: string ):void;

		/**
		 * Broadcasts a packet
		 * @param packet The packet to broadcast
		 * @param opts Any options to send along:
		 * 	- rooms: An optional list of rooms to broadcast to. If empty, the packet is broadcast to all sockets
		 * 	- except: A list of Socket IDs to exclude
		 * 	- flags: Any flags that we want to send along ('json', 'volatile', 'broadcast')
		 */
		broadcast( packet: any, opts: { rooms?: string[]; except?: string[]; flags?: {[flag: string]: boolean} } ):void;
	}

	/**
	 * The client behind each socket (can have multiple sockets)
	 */
	interface Client {
		/**
		 * The Server that this client belongs to
		 */
		server: Server;

		/**
		 * The underlying Engine.io Socket instance
		 */
		conn: EngineSocket;

		/**
		 * The ID for this client. Regenerated at every connection
		 */
		id: string;

		/**
		 * The http.IncomingMessage request sent with the connection. Useful
		 * for recovering headers etc
		 */
		request: any;

		/**
		 * The dictionary of sockets currently connect via this client (i.e. to different
		 * namespaces) where the Socket ID is the key
		 */
		sockets: {[id: string]: Socket};

		/**
		 * A dictionary of all the namespaces for this client, with the Socket that
		 * deals with that namespace
		 */
		nsps: {[nsp: string]: Socket};
	}

	/**
	 * A reference to the underlying engine.io Socket connection.
	 */
	interface EngineSocket extends NodeJS.EventEmitter {
		/**
		 * The ID for this socket - matches Client.id
		 */
		id: string;

		/**
		 * The Engine.io Server for this socket
		 */
		server: any;

		/**
		 * The ready state for the client. Either 'opening', 'open', 'closing', or 'closed'
		 */
		readyState: string;

		/**
		 * The remote IP for this connection
		 */
		remoteAddress: string;

		/**
		 * whether the transport has been upgraded
		 */
		upgraded: boolean;

		/**
		 * (http.IncomingMessage): request that originated the Socket
		 */
		request: any;

		/**
		 * (Transport): transport reference
		 */
		transport: any;
	}
}
