﻿/*----------------------------------------------------------------------------*/
// Broker class definition

function Broker() {
    this.commands = [];
    this.lsCommand = null;

    this.callback = {
    	/*Number*/loadTopic: 0,
    	/*Object*/loadObject: null,
    	/*String*/loadFunc: '',
    	/*Number*/errorTopic: 0,
    	/*Object*/errorObject: null,
    	/*String*/errorFunc: ''
    };
}

Broker.prototype.splitCommand = function(/*String*/str, /*Function*/handler, /*Int*/writeNeeded, /*Array*/commands ,/*Array*/lsCommand, /*String*/splitSign){
	var BOUNDARY = 1024 * 1; // bytes
	var commandLength = str.length;
	var newCommandLength = 0;
	var pBuffer = [];

	if(commandLength > BOUNDARY){
		dojo.raise("CLI is too long to be accepted by Zysh");
	}

	for(var parameter in lsCommand){
		if(lsCommand[parameter] == ""){
			continue;
		}

		newCommandLength += (lsCommand[parameter].length + 1);

		if(commandLength + newCommandLength < BOUNDARY){
			pBuffer.push(lsCommand[parameter]);
		}
		else{
			commands.push(new Cmd(str.replace(/\%longString/, pBuffer.join(splitSign)), handler, writeNeeded));

			pBuffer = [];
			pBuffer.push(lsCommand[parameter]);
			newCommandLength = lsCommand[parameter].length;
		}
	}

	commands.push(new Cmd(str.replace(/\%longString/, pBuffer.join(splitSign)), handler, writeNeeded));
}

// Returns how many arguments are replaced
Broker.prototype.addCmd = function(/*String*/key) {
    // Must exist in cmdPool
    if (dojo.lang.isUndefined(cmdPool[key])) {
    	dojo.raise("cmdPool[" + key + "] undefined");
    }
    if(dojo.lang.isUndefined(cmdPool[key].writeNeeded)){
    	dojo.raise("The flag of writeNeeded is undefined");
    }

    var /*Cmd*/cmd = cmdPool[key];

    var ary = [];
    for (var i = 1, len = arguments.length; i < len; i++) {
        ary.push(this.utf8_encode(arguments[i].toString()));
    }

    var shiftNum = 0;
    var count = 0;

    // Fill in parameters
    var str = cmd.cmd;

    //check %ls can't more than one
    while((shiftNum = str.indexOf("%ls", shiftNum)) > 0){
    	shiftNum += 2;
    	count++;
    }

    if(count > 1){
    	dojo.raise("Only one %ls can be accepted.");
    }
    else{
    	shiftNum = 0;
    }

    var s_index = str.indexOf("%s");
    var ls_index = str.indexOf("%ls");
    var splitSign;

    while (s_index > 0 || ls_index > 0) {
	    if (ary.length > 0) {

	    	if(s_index == -1){
	    		splitSign = str.match(/\%ls.{1}/)[0].charAt(3);
	    		this.lsCommand = ary.shift().split(splitSign);
	    		str = str.replace(/\%ls.?/, "%longString");
	    	}
	    	else if(ls_index == -1){
	    		str = str.replace(/\%s/, ary.shift());
	    	}
	    	else if(s_index < ls_index){
	    		str = str.replace(/\%s/, ary.shift());
	    	}
	    	else{
	    		splitSign = str.match(/\%ls.{1}/)[0].charAt(3);
	    		this.lsCommand = ary.shift().split(splitSign);
	    		str = str.replace(/\%ls.?/, "%longString");
	    	}

				if(s_index != -1){
					s_index = str.indexOf("%s");
				}

				if(ls_index != -1){
					ls_index = str.indexOf("%ls");
				}

	      shiftNum++;
	    }
	    else { // Exception
	      dojo.raise(key, "Not enought parameter(s)");
	    }
	  }

		if(this.lsCommand != null){
			this.splitCommand(str, cmd.handler, cmd.writeNeeded, this.commands, this.lsCommand, splitSign);
			this.lsCommand = null;
		}
		else{
	  	this.commands.push(new Cmd(str, cmd.handler, cmd.writeNeeded));
	  }

	  return shiftNum;
};

/**
 * _unsetCallback() unsubscribes to Dojo's event system
 *
 * Notice that _unsetCallback() is a private method, for internal management only
 */
Broker.prototype._unsetCallback = function() {
	if (this.callback.loadTopic !== 0) {
		dojo.event.topic.unsubscribe(this.callback.loadTopic, this.callback.loadObject, this.callback.loadFunc);
        dojo.event.topic.destroy(this.callback.loadTopic);
	    this.callback.loadTopic = 0;
	    this.callback.loadObject = null;
	    this.callback.loadFunc = '';
	}

	if (this.callback.errorTopic !== 0) {
		dojo.event.topic.unsubscribe(this.callback.errorTopic, this.callback.errorObject, this.callback.errorFunc);
        dojo.event.topic.destroy(this.callback.errorTopic);
	    this.callback.errorTopic = 0;
	    this.callback.errorObject = null;
	    this.callback.errorFunc = '';
	}
};


Broker.prototype.addAction = function(/*String*/key) {
    if (dojo.lang.isUndefined(actionPool[key])) { /* Must found in action pool */
    	dojo.raise("actionPool[" + key + "] undefined");
    }

    var ary = actionPool[key];

    var argArray = [];
    for (var i = 1; i < arguments.length; i++) {
        argArray.push(arguments[i]);
    }

    for (i = 0, len = ary.length; i < len; i++) {
        if (cmdPool[ary[i]] instanceof Cmd) {
            argArray.unshift(ary[i]);

            var shiftNum = this.addCmd.apply(this, argArray);

            argArray.shift();

            argArray.splice(0, shiftNum);
        }
        else {
            argArray.unshift(ary[i]);
            this.addAction.apply(this, argArray);
        }
    }

};

Broker.prototype.loadHandler = function(type, data, evt) {
    if (type == 'error') {
        return;
    }
    else if (type == 'timeout') {
        Navigator.setMessage('warning', utility.getText("I/O timeout"));
        return;
    }

    if (data === '<html><head><title></title><script type="text/javascript">location.replace("loginwrap.html");</script></head></html>'
) {
        Navigator.logout();
    }

    var js = null;

    try {
        js = eval(evt.responseText);
    } catch(e) {
        dojo.raise(evt.responseText, "invalid backend return");
    }

    var object = {}; // Return object
    var errFound = false;

		for (var i = 0; i < this.commands.length; i++) { // For each issued command
        var o = {
            errno: js['errno' + i],
            errmsg: js['errmsg' + i],
            data: js['zyshdata' + i]
        };

        // Did back-end return error?
        if (o.errno !== 0) { // Error
            if (this.callback.errorTopic !== 0) { //
                dojo.debug("Publish(error) id:", this.callback.errorTopic);
                dojo.event.topic.publish(this.callback.errorTopic, o.errmsg);
            }
            else if (this.errorHandler) {
                this.errorHandler(utility.getText(o.errmsg));
            }
            else {
                errFound = true;
                Navigator.setMessage('error', utility.getText(o.errmsg));
            }

            return;
        }

        /* Calls command handler one by one */
        if (this.commands[i].handler) {
            var r = this.commands[i].handler(o.data);

            /* Merge returns */
            for (var j in r) {
                object[j] = r[j];
            }
        }
    }

    if (errFound == false) {
        Navigator.setMessage('success', utility.getText("Ready"));
    }

    if (this.callback.loadTopic !== 0) {
        dojo.debug("Publish(load) id:", this.callback.loadTopic);
        dojo.event.topic.publish(this.callback.loadTopic, object);
    }

    if (this.handler) {
        this.handler(object);
    }
};

Broker.prototype.exec = function() {
  var cmds = {}; /* Merges commands */
  var writeNeededCount = 0;

  for (var i = 0, len = this.commands.length; i < len; i++) {
		cmds["c" + i] = this.commands[i].cmd;
		writeNeededCount += this.commands[i].writeNeeded;
  }

	if(writeNeededCount > 0)
		cmds["write"] = 1;
	else
		cmds["write"] = 0;

	var bindArgs = {
		method: "POST",
		content: cmds,
		url: "../fakeBackend/fakeBackend.php",
		timeoutSeconds: 0,
		handler: dojo.lang.hitch(this, this.loadHandler),
		mimetype: "text/plain"
	};

	dojo.io.bind(bindArgs);
};

Broker.prototype.execSync = function() {
  var cmds = {}; /* Merges commands */
  var writeNeededCount = 0;

  for (var i = 0, len = this.commands.length; i < len; i++) {
		cmds["c" + i] = this.commands[i].cmd;
		writeNeededCount += this.commands[i].writeNeeded;
  }

	if(writeNeededCount > 0)
		cmds["write"] = 1;
	else
		cmds["write"] = 0;

	var bindArgs = {
		method: "POST",
		content: cmds,
		url: "../fakeBackend/fakeBackend.php",
		timeoutSeconds: 0,
		handler: dojo.lang.hitch(this, this.loadHandler),
		sync: true,
		mimetype: "text/plain"
	};

	dojo.io.bind(bindArgs);
};

Broker.prototype.registerHandler = function(/*Function*/func) {
    this._unsetCallback();

    this.handler = func;
}

Broker.prototype.registerErrorHandler = function(/*Function*/func) {
    this.errorHandler = func;
}


Broker.prototype.setCallback = function(/*Object*/args) {
	this._unsetCallback();

	if (dojo.lang.isUndefined(args)) { // No parameter passed in
		return;
	}

	// Set load (success) callback handler
	if (dojo.lang.isObject(args.loadObject) && ( dojo.lang.isString(args.loadFunc) || dojo.lang.isFunction( args.loadFunc ) ) ) {
		this.callback.loadTopic = utility.makeUniqueNumber();
		this.callback.loadObject = args.loadObject;
		this.callback.loadFunc = args.loadFunc;

		dojo.event.topic.subscribe(this.callback.loadTopic,
							       this.callback.loadObject,
							       this.callback.loadFunc);
		dojo.debug("Callback(load) id:", this.callback.loadTopic);
	}
	// Set error (failure) callback handler
	if (dojo.lang.isObject(args.errorObject) && dojo.lang.isString(args.errorFunc)) {
		this.callback.errorTopic = utility.makeUniqueNumber();
		this.callback.errorObject = args.errorObject;
		this.callback.errorFunc = args.errorFunc;

		dojo.event.topic.subscribe(this.callback.errorTopic,
							       this.callback.errorObject,
							       this.callback.errorFunc);
		dojo.debug("Callback(error) id:", this.callback.errorTopic);
	}
};


Broker.prototype.reset = function() {
    this._unsetCallback();

    while (this.commands.length > 0) {
        this.commands.shift();
    }
};

Broker.prototype.utf8_encode = function(/*String*/text) {

	str = text.replace(/\r\n/g,"\n");
	var utftext = [];

	for (var n = 0; n < str.length; n++) {

		var c = str.charCodeAt(n);

		if (c < 128) {
			utftext.push(String.fromCharCode(c));
		}
		else if((c > 127) && (c < 2048)) {
			utftext.push(String.fromCharCode((c >> 6) | 192));
			utftext.push(String.fromCharCode((c & 63) | 128));
		}
		else {
			utftext.push(String.fromCharCode((c >> 12) | 224));
			utftext.push(String.fromCharCode(((c >> 6) & 63) | 128));
			utftext.push(String.fromCharCode((c & 63) | 128));
		}

	}

	return utftext.join("");
};

