/*
 * Ami [http://x13n.com/alex]
 *
 *          file: ami.js
 *   description: Useful Javascript Functions and Patterns Library
 *       creator: Alex Macmillan
 *       created: 2006
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/ 
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is Ami Javascript Library, unreleased.
 * 
 * The Initial Developer of the Original Code is Alexander Macmillan.
 * Portions created by Alexander Macmillan are Copyright (C) 2009
 * Alexander Macmillan. All Rights Reserved.
 * 
 * Contributor(s): ______________________________________.
 */

var snewts_xml_stub = "/v1/urli/";

var DEBUG = false;

// If the URL contains the string "debug", enable debug.
if (window.location.href.indexOf("debug") > -1) { DEBUG = true; }

/* If the browser does not appear to be Firefox set up a dummy console object.
if (navigator.userAgent.indexOf("Firefox") == -1) {
    console = {
        log: function (str) {  },
        error: function (str) {  }
    }
} */

var Ami = {
		
	/* Memoise target for getXMLHttpRequest */
    _xhr: null,

    /* Some constants */
    ERROR: -1,
    INFO: 0,

	/* Abstraction layer for logging. */
	log: function (str) {
	    
	    if (!DEBUG) return;
	    
	    console.log(str);
	    
		// Built-in logging.
		c = document.getElementById('console');
		l = Builder.logMessage(str);
		c.appendChild(l);
		Ami.scrollLog();
		
	},
		
	timeLog: function (str) {

        if (!DEBUG) return;

		// Firebug logging.
		console.log(str);
		
		// Built-in logging.
		c = document.getElementById('console');
		l = Builder.timeLogMessage(str);
		c.appendChild(l);
		Ami.scrollLog();
	},
	
	logError: function (str) {
	    // Built-in
	    c = get('console')
	    l = Builder.timeLogMessage(str, this.ERROR);
	    c.appendChild(l)
	    Ami.scrollLog();
	},
    
    message: function (str) {
        var c = get('console');
        c.appendChild(Builder.timeLogMessage(str));
        Ami.scrollLog();
    },

    scrollLog: function () {
        var cf = document.getElementById('consoleframe');
        cf.scrollTop = cf.scrollHeight;
    },

	/* This function adapted from MochiKit compatibility layer. */
	getXMLHttpRequest: function () {
        if (!self._xhr) {
            var tryThese = [
                function () { return new XMLHttpRequest(); },
                function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
                function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
                function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
                function () {
                    self.log("Browser does not support XMLHttpRequest");
                }
            ];
            for (var i = 0; i < tryThese.length; i++) {
                var func = tryThese[i];
                try {
                    self._xhr = func;
                    return func();
                } catch (e) {
                    // pass
                }
            }
        }
        return self._xhr();
    },
	
	snewtsPath: function(path) {
		return snewts_xml_stub + path + "?uniq=" + (new Date()).getTime();
	},
	
	/* Sends a message to the server */
	doRequest: function(type, messages, oncomplete) {
		var push = this.getXMLHttpRequest();
		push.onreadystatechange = function () { Ami._pushHandler(push, oncomplete) };
		push.open("GET", Ami.snewtsPath(type) + (messages ? "&" + messages : ""), true);
		this.timeLog("Sending XML message [ "+type+" ] // " + messages);
		push.send(null);
	},
	
	/* Handles recieved pull messages from the server */
	_pushHandler: function(push, postfunc) {
		if (push.readyState == 4) {
			if (push.status == 200 || push.status == 304) {
			    if (push.responseXML.xml) {
				   Ami.timeLog(push.responseXML.xml); 
			    } else {
				   Ami.timeLog(push.responseText);
			    }
				var doc = push.responseXML;
				var element = doc.documentElement;
				
				// Execute the function we were given when this request was made
				postfunc(new Xel(element));						
			}
		}
	},
	
	doAction: function(procedure) {
	    // FIXME: Clumsy fudge -- append the Snewts password. This should be done by Snewts, not by Ami.
	    if (Snewts.adminPassword > "") {
    	    procedure.parameters.push('adminpassword');
    	    procedure.parameters.push(Snewts.adminPassword);
	    }
	    
	    // Run the Ajax request, and forward the response to the bottom half.
	    Ami.doRequest(procedure.command, querize(procedure.parameters), function(response) {
	        procedure.bottom(response, procedure);
	    });
	}
};

String.prototype.trimmed = function () {
	return this.replace(/^\s*/, '').replace(/\s*$/, '');
};

String.prototype.breaked = function () {
	return this.replace(/\n/, '<br/>');	
};

// Turn an array into a query string.
function querize (arr) {
    var result = "";
    for (var i = 0; i < arr.length; i += 2) {
        if (result > "") result += "&";
        result += arr[i] + "=" + arr[i+1];
    }
    return result;
}

/* Represents an XML element node and provides helper functions for clean, 
   abstract access. */
function Xel (element) {

	if (element == null) {
        Ami.logError("Xel construct called on null object.");
	}
    
	this.xmlelement = element;
	
	this.name = function () {
		return this.xmlelement.tagName.toLowerCase();
	};
	
	this.is = function (tagname) {
		return this.name() == tagname;
	}
	
	this.attribute = function (name) {
		return this.xmlelement.getAttribute(name);
	};
	
	this.attributeAsInt = function (name) {
	    return parseInt(this.attribute(name), 10);
	}
	
	this.children = function (name) {
		// If a name isn't supplied, default to the empty string.
		name = name || "";
		var children = [];
		for (var n = 0; n < this.xmlelement.childNodes.length; n++) {
			child = this.xmlelement.childNodes[n];
			if (child.nodeType == 1) {
				// If no name was specified or this element matches the name...
				if (name == "" || name == child.tagName) {
					children.push(new Xel(child));
				}
			}
		}
		return children;
	};
	
	this.child = function (name) {
		var el = this.xmlelement.getElementsByTagName(name)[0];
		if (el) {
			return new Xel(el);
		} else {
		    Ami.logError("Child ["+name+"] not found in Xel element ["+this.name()+"]");
			return null;
		}
	};
	
	this.text = function(name) {
		var text = "";
		for (var n = 0; n < this.xmlelement.childNodes.length; n++) {
			child = this.xmlelement.childNodes[n];
			if (child.nodeType == 3) {
				text += child.nodeValue;
			}
		}
		return text;
	};
	
	this.intValue = function(name) {
	    return parseInt(this.text());
	};
	
	this.objectify = function(name) {
	    var out = {};
	    // Expect the children of this node to be name/value pairs
	    for (var n = 0; n < this.xmlelement.childNodes.length; n++) {
	        child = this.xmlelement.childNodes[n];
	        if (child.nodeType == 1 && child.tagName == name) {
	            pair = new Xel(child);
	            out[pair.child("name").text()] = pair.child("value").text();
	        }
	    } 
	    return out;
	};
}

// Alias for Ami.log
function log (obj) {
	Ami.log(obj);
}

// Alias for Ami.timeLog
function tlog (obj) {
	Ami.timeLog(obj);
}

// Alias for Ami.logError
function elog (obj) {
	Ami.logError(obj);
}

// Alias for document.getElementById
function get (id) {
	return document.getElementById(id);
}
