/*
 * Cettia
 * http://cettia.io/projects/cettia-protocol/
 * 
 * Copyright 2015 The Cettia Project
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */
var events = require("events");
var url = require("url");

// This function is exposed to the module's `createServer` as a factory to
// create a server which consumes transport and produces socket.
module.exports = function() {
    // A server object.
    var self = new events.EventEmitter();
    // Options to configure server and client.
    var options = {
        // A heartbeat interval in milliseconds.
        heartbeat: 20000,
        // This is just to speed up heartbeat test and not required generally.
        // It means the time to wait for the server's response. The default
        // value is `5000`.
        _heartbeat: 5000
    };
    self.setHeartbeat = function(heartbeat) {
        options.heartbeat = +heartbeat;
    };
    self.set_heartbeat = function(_heartbeat) {
        options._heartbeat = +_heartbeat;
    };
    // A link between Cettia protocol and Cettia transport protocol. `transport` is
    // expected to be passed from Cettia transport server.
    self.handle = function(transport) {
        // Builds a socket on top of a transport and fires `socket` event to
        // server.
        self.emit("socket", createSocket(transport, options));
    };
    return self;
};

function createSocket(transport, options) {
    // A socket object representing the client.
    var self = new events.EventEmitter();
    // When the transport has received a message from the client.
    transport.on("text", function(text) {
        // Converts JSON text to an event object.
        // 
        // It should have the following properties:
        // * `id: string`: an event identifier.
        // * `type: string`: an event type.
        // * `data: any`: an event data.
        // * `reply: boolean`: true if this event requires the reply.
        var event = JSON.parse(text);
        // If the client sends a plain event, dispatch it.
        if (!event.reply) {
            self.emit(event.type, event.data);
        } else {
            var latch;
            // A function to create a function. 
            function reply(success) {
                // A controller function.
                return function(value) {
                    // The latch prevents double reply.
                    if (!latch) {
                        latch = true;
                        self.send("reply", {id: event.id, data: value, exception: !success});
                    }
                };
            }
            // Here, the controller is passed to the handler as 2nd argument and
            // calls the server's `resolved` or `rejected` callback by sending
            // `reply` event.
            self.emit(event.type, event.data, {resolve: reply(true), reject: reply(false)});
        }
    });
    // When any error has occurred.
    transport.on("error", function(error) {
        self.emit("error", error);
    });
    // When the transport has been closed for any reason.
    transport.on("close", function() {
        self.emit("close");
    });
    // An id for event. It should be unique among events to be sent to the
    // client and has nothing to do with one the client sent.
    var eventId = 0;
    // A map for reply callbacks for reply.
    var callbacks = {};
    self.send = function(type, data, resolved, rejected) {
        // It should have the following properties:
        // * `id: string`: an event identifier.
        // * `type: string`: an event type.
        // * `data: any`: an event data.
        // * `reply: boolean`: true if this event requires the reply.
        var event = {
            id: "" + eventId++, 
            type: type, 
            data: data, 
            reply: resolved != null || rejected != null
        };
        // Stores resolved and rejected callbacks if they are given.
        if (event.reply) {
            callbacks[event.id] = {resolved: resolved, rejected: rejected};
        }
        // Convert the event to a JSON message and sends it through the
        // transport.
        transport.send(JSON.stringify(event));
        return this;
    };
    // Delegate closing to the transport.
    self.close = function() {
        transport.close();
        return this;
    };
    // On the `reply` event, executes the stored reply callbacks with data.
    self.on("reply", function(reply) {
        if (reply.id in callbacks) {
            var cbs = callbacks[reply.id];
            var fn = reply.exception ? cbs.rejected : cbs.resolved;
            if (fn) {
                fn.call(this, reply.data);
            }
            delete callbacks[reply.id];
        }
    });
    // Sets a timer to close the socket after the heartbeat interval.
    var heartbeatTimer;
    function setHeartbeatTimer() {
        heartbeatTimer = setTimeout(function() {
            self.emit("error", new Error("heartbeat"));
            self.close();
        }, options.heartbeat);
    }
    setHeartbeatTimer();
    // The client will start to heartbeat on its `open` event and send the
    // heartbaet event periodically. Then, cancels the timer, sets it up
    // again and sends the heartbeat event as a response.
    self.on("heartbeat", function() {
        clearTimeout(heartbeatTimer);
        setHeartbeatTimer();
        self.send("heartbeat");
    })
    // To prevent a side effect of the timer, clears it on the close event.
    .on("close", function() {
        clearTimeout(heartbeatTimer);
    });
    // Starts handshake for the protocol. These params will be handled by
    // client-side socket, and client-side socket will fire `open` event. The
    // first message of transport is used to perform handshaking and should be
    // formatted in URI.
    transport.send(url.format({query: {heartbeat: options.heartbeat, _heartbeat: options._heartbeat}}));
    return self;
}
@@ -484,24 +484,236 @@
 tEmitter();%0A
+    // A repository of sockets consisting of opened socket and closed socket%0A    // only. A socket has id but it doesn't need to be public now so that array%0A    // is used to hide it. TODO%0A    self.sockets = %5B%5D;%0A
     // Optio
@@ -1482,45 +1482,165 @@
 port
- and fires %60socket%60 event to%0A
+%0A        var socket = createSocket(transport, options);%0A        // TODO%0A        self.sockets.push(socket);%0A        socket.on(%22close%22, function() %7B%0A  
         
 // s
@@ -1639,79 +1639,271 @@
     
+  
 // 
-server.%0A        self.emit(%22
+It equals to %60self.sockets.remove(socket)%60.%0A            self.sockets.splice(self.sockets.indexOf(
 socket
-%22, createSocket(transport, options)
+), 1);%0A        %7D);%0A        // Fires %60socket%60 event to server. This is the beginning of the socket%0A        // life cycle.%0A        self.emit(%22socket%22, socket
 );%0A 

Add a repository for sockets to server #1