"use strict";

/*global module, require*/

var _ = require("lodash"),
    d3 = require("d3"),
    sharejs = require('../node_modules/share/lib/client/index.js'),
    BCSocket = require('../node_modules/browserchannel/dist/bcsocket-uncompressed.js').BCSocket,
    helpers = require("./helpers.js"),
    isNum = helpers.isNum,
    callbacks = helpers.callbackHandler,

    connected = "connected",
    connecting = "connecting",
    disconnected = "disconnected",
    stopped = "stopped";

/*
 Connects to a sharejs server.

 Exposes some functions about it.
 */
module.exports = function(url) {
    var ensureConnected = function() {
	if (isDown()) {
	    connection = new sharejs.Connection(
		new BCSocket(
		    url,
		    {
			reconnect: true
		    })
	    );

	    connection.debug = debug;

	    [connected, connecting, disconnected, stopped]
		.forEach(function(state) {
		    connection.on(state, function() {
			if (state !== lastState) {
			    lastState = state;
			    switch(state) {
			    case connected:
				onUp();
				break;
			    case connecting:
				// We don't yet know whether the server is up or down.
				break;
			    case disconnected:
			    case stopped:
				onDown();
				break;
			    default:
				throw new Error("Unknown connection state " + state);
			    }
			}
			if (isUp()) {
			    onUp();
			} else {
			    onDown();
			}
		    });
		});
	}
    },

	maybeDiscardConnection = function() {
	    if (!stayConnected && !isDown()) {
		connection.disconnect();
	    }
	},

	stayConnected = false,
	connection,
	onUp = callbacks(),
	onDown = callbacks(),
	lastState,

	debug = false,

	isUp = function() {
	    return connection && connection.state === connected;
	},

	/*
	 There is no connection at all, or the connection there is has fallen right over.
	 */
	isDown = function() {
	    return !connection || connection.state === stopped;
	},

	m = {
	    search: function(coll, text, callback, errback) {
		d3.json(
		    [url, "search", coll].join("/") + "?q=" + text,
		    function(error, results) {
			if (error) {
			    errback(error);
			} else {
			    callback(_.pluck(results, "name"));
			}
		    }
		);
	    },

	    load: function(coll, name, callback) {
		ensureConnected();
		var doc = connection.get(coll, name.toLowerCase());
		if (!doc.subscribed) {
		    doc.subscribe();
		}
		doc.whenReady(function() {
		    callback(doc);
		    maybeDiscardConnection();
		});
	    },

	    /*
	     Object returns has 3 properties:
	     doc - the deserialized json snapshot
	     v - the version of that snapshot
	     latestV - the most recent version of the document available
	     */
	    loadVersion: function(coll, name, version, callback, errback) {
		if (!isNum(version)) {
		    throw new Error("Not a valid version " + version);
		}
		
		d3.json(
		    [url, "history", coll, name, version].join("/"),
		    function(error, json) {
			if (error) {
			    errback(error.response);
			} else {
			    callback(json);
			}
		    }
		);
	    },

	    /*
	     When the server is not connected, it's still OK to get a document if we know that it doesn't already exist.

	     To ensure this, the name passed here should be a GUID.
	     */
	    loadOffline: function(coll, name) {
		ensureConnected();
		var doc = connection.get(coll, name.toLowerCase());
		if (!doc.subscribed) {
		    doc.subscribe();
		}
		maybeDiscardConnection();
		return doc;
	    },

	    deleteDoc: function(coll, name) {
		ensureConnected();
		var toDelete = connection.get(coll, name.toLowerCase());
		if (!toDelete.subscribed) {
		    toDelete.subscribe();
		}
		toDelete.whenReady(function() {
		    toDelete.del();
		    toDelete.destroy();
		    maybeDiscardConnection();
		});
	    },

	    onUp: onUp.add,
	    onDown: onDown.add,
	    isUp: isUp,

	    stayConnected: function() {
		stayConnected = true;
		ensureConnected();
	    },

	    debug: function() {
		debug = true;
		if (connection) {
		    connection.disconnect();
		}
		ensureConnected();
	    }
	};
    return m;
};

@@ -478,18 +478,200 @@
 stopped%22
+,%0A%0A    /*%0A     When the commands counter is 0, everything that was trying to use the backend has stopped, so we are allowed to discard our connection.%0A     */%0A    commandsCounter = 0
 ;%0A
-
 %0A/*%0A Con
@@ -804,19 +804,87 @@
 unction(
-) %7B
+dontIncrement) %7B%0A%09if (!dontIncrement) %7B%0A%09    commandsCounter += 1;%0A%09%7D%0A%09
 %0A%09if (is
@@ -1684,22 +1684,98 @@
 if (
-!stayConnected
+commandsCounter %3E 0) %7B%0A%09%09commandsCounter -= 1;%0A%09    %7D%0A%09    %0A%09    if (commandsCounter === 0
  && 
@@ -1830,32 +1830,8 @@
 %7D,%0A%0A
-%09stayConnected = false,%0A
 %09con
@@ -4077,16 +4077,153 @@
  isUp,%0A%0A
+%09    /*%0A%09     Since this ensureConnected is not paired with a maybeDiscardConnection, it will keep the connection open forever.%0A%09     */%0A
 %09    sta
@@ -4251,32 +4251,8 @@
 ) %7B%0A
-%09%09stayConnected = true;%0A
 %09%09en
@@ -4373,16 +4373,16 @@
 ();%0A%09%09%7D%0A
-
 %09%09ensure
@@ -4383,32 +4383,37 @@
 ensureConnected(
+false
 );%0A%09    %7D%0A%09%7D;%0A  

Put in a counter for how many things are using our connection to make sure that we don't prematurely close it.