Aphaline = {

	ids : [],

	Direct : function(query, callback) {
		jQuery.ajax({
			url: '/?port=direct',
			type: 'POST',
			processData: false,
			contentType: 'text/xml',
			data: query,
			success: callback //function(response) { Aphaline.ProcessResponse(response); }
		});
	},
	Port : function(port, query, callback) {
		$.post('/?port=' + port, query, callback);
	},
	QQ : function(atom, callback) {
		Aphaline.Direct(AphML.Query('qq', [atom]), callback);
	},
	ProcessResponse : function(response) {
		
		root = $(response).children()[0];
		root_id = $(root).attr('id');
		
		atoms = [];
		types = [];
		
		$(root).children().each(function() {
			atoms.push($(this).attr('id'));
			types.push($(this).get(0).nodeName);
		});
		
		
		
		//alert(types);
		
		//$(response).children().each(function() { alert($(this).attr('id')); })
	
		id = $(response).find('dataset').attr('id');
		if (typeof(this.ids[id]) != 'undefined') this.ids[id](response);
	},
	Assign : function(id, func) { 
		this.ids[id] = func;
	}
};

AphML = {

	Query : function(id, atoms) {
		
		var xml = (document.implementation && document.implementation.createDocument) 
			? document.implementation.createDocument('', '', null)
			: new ActiveXObject("MSXML2.DOMDocument");
			
		var root = xml.createElement('hquery');
		root.setAttribute('id', id);
		xml.appendChild(root);
		
		var addAtom = function(name, id, module) {
			var atom = xml.createElement(name);
			atom.setAttribute('id', id);
			atom.setAttribute('module', module);
			root.appendChild(atom);
			return atom;
		};
		
		var addField = function(atom, name, value) {
			var field = xml.createElement('field');
			field.setAttribute('name', name);
			fval = xml.createTextNode(value);
			field.appendChild(fval);
			atom.appendChild(field);
		};
		
		var addCondition = function(atom, field, operator, value) {
			var condition = xml.createElement('condition');
			condition.setAttribute('field', field);
			condition.setAttribute('operator', operator);
			condition.setAttribute('value', value);
			atom.appendChild(condition);
		};
		
		for (i=0, len=atoms.length; i<len; i++) {

			currentAtom = atoms[i];
			
			if (currentAtom instanceof QSelect) {
				var atom = addAtom('select', currentAtom.id, currentAtom.module);
				for (j=0, jlen=currentAtom.fields.length; j<jlen; j++) {
					cur = currentAtom.fields[j];
					addField(atom, cur.name, cur.value);
				}
				for (j=0, jlen=currentAtom.conditions.length; j<jlen; j++) {
					cur = currentAtom.conditions[j];
					addCondition(atom, cur.field, cur.operator, cur.value);
				}
				if (typeof(currentAtom.limit) != 'undefined' && currentAtom.limit != null) {
					var limit = xml.createElement('limit');
					limit.setAttribute('count', currentAtom.limit.count);
					limit.setAttribute('offset', currentAtom.limit.offset);
					atom.appendChild(limit);
				}
			}
			else if (currentAtom instanceof QUpdate) {
				var atom = addAtom('update', currentAtom.id, currentAtom.module);
				for (j=0, jlen=currentAtom.fields.length; j<jlen; j++) {
					cur = currentAtom.fields[j];
					addField(atom, cur.name, cur.value);
				}
				for (j=0, jlen=currentAtom.conditions.length; j<jlen; j++) {
					cur = currentAtom.conditions[j];
					addCondition(atom, cur.field, cur.operator, cur.value);
				}
			}
			else if (currentAtom instanceof QInsert) {
				var atom = addAtom('insert', currentAtom.id, currentAtom.module);
				for (j=0, jlen=currentAtom.fields.length; j<jlen; j++) {
					cur = currentAtom.fields[j];
					addField(atom, cur.name, cur.value);
				}
			}
			else if (currentAtom instanceof QDelete) {
				var atom = addAtom('delete', currentAtom.id, currentAtom.module);
				for (j=0, jlen=currentAtom.conditions.length; j<jlen; j++) {
					cur = currentAtom.conditions[j];
					addCondition(atom, cur.field, cur.operator, cur.value);
				}
			}
			else if (currentAtom instanceof QExecute) {
				var atom = addAtom('execute', currentAtom.id, currentAtom.module);
				var action = xml.createElement('action');
				action.setAttribute('name', currentAtom.name);
				aval = xml.createTextNode(currentAtom.value);
				for (var a_key in currentAtom.attributes) action.setAttribute(a_key, currentAtom.attributes[a_key]);
				action.appendChild(aval);
				atom.appendChild(action);
			}	
		}
		
		return xml;
				
	},

	Select : function(id, module, data) {
		fields = [];
		conditions = [];
		limit = null;
		for (var i=0, len=data.length; i<len; i++) {
			if (data[i] instanceof QDField) fields.push(data[i]);
			else if (data[i] instanceof QDFieldSet) {
				for (var j=0, jlen=data[i].fields.length; j<jlen; j++) fields.push(data[i].fields[j]);
			}
			else if (data[i] instanceof QDCondition) conditions.push(data[i]);
			else if (data[i] instanceof QDLimit) limit = data[i];
		}
		return new QSelect(id, module, fields, conditions, limit);
	},
	
	Update : function(id, module, data) {
		fields = [];
		conditions = [];
		for (var i=0, len=data.length; i<len; i++) {
			if (data[i] instanceof QDField) fields.push(data[i]);
			else if (data[i] instanceof QDFieldSet) {
				for (var j=0, jlen=data[i].fields.length; j<jlen; j++) fields.push(data[i].fields[j]);
			}
			else if (data[i] instanceof QDCondition) conditions.push(data[i]);
		}
		return new QUpdate(id, module, fields, conditions);
	},
	
	Insert : function(id, module, data) {
		fields = [];
		for (var i=0, len=data.length; i<len; i++) {
			if (data[i] instanceof QDField) fields.push(data[i]);
			else if (data[i] instanceof QDFieldSet) {
				for (var j=0, jlen=data[i].fields.length; j<jlen; j++) fields.push(data[i].fields[j]);
			}
		}
		return new QInsert(id, module, fields);
	},
	
	Delete : function(id, module, data) {
		conditions = [];
		for (var i=0; i<data.length; i++) {
			if (data[i] instanceof QDCondition) conditions.push(data[i]);
		}
		return new QDelete(id, module, conditions);
	},
	
	Execute: function(id, module, name, value, attributes) {
		return new QExecute(id, module, name, value, attributes);
	},

	Field : function(name, value) {
		return new QDField(name, value);
	},
	
	FieldSet : function(array_of_names) {
		array_of_fields = [];
		for (var i=0; i<array_of_names.length; i++) {
			array_of_fields.push(new QDField(array_of_names[i], ''));
		}
		return new QDFieldSet(array_of_fields);
	},
	
	Condition : function(field, operator, value) {
		return new QDCondition(field, operator, value);
	},
	
	Limit : function(count, offset) {
		return new QDLimit(count, offset);
	}
		
};

QDField = function(name, value) {
	this.name = name;
	this.value = value;
};

QDFieldSet = function(array_of_fields) {
	this.fields = array_of_fields;
};

QDCondition = function(field, operator, value) {
	this.field = field;
	this.operator = operator;
	this.value = value;
};

QDLimit = function(count, offset) {
	this.count = count;
	this.offset = offset || 0;
};

QSelect = function(id, module, fields, conditions, limit) {
	this.id = id;
	this.module = module;
	this.fields = fields;
	this.conditions = conditions;
	this.limit = limit;
};

QUpdate = function(id, module, fields, conditions) {
	this.id = id;
	this.module = module;
	this.fields = fields;
	this.conditions = conditions;
};

QInsert = function(id, module, fields) {
	this.id = id;
	this.module = module;
	this.fields = fields;
};

QDelete = function(id, module, conditions) {
	this.id = id;
	this.module = module;
	this.conditions = conditions;
};

QExecute = function(id, module, name, value, attributes) {
	this.id = id;
	this.module = module;
	this.name = name;
	this.value = value;
	this.attributes = attributes;
};

ROk = function(id, module) {
	this.id = id;
	this.module = module;	
};

RNull = function(id, module) {
	this.id = id;
	this.module = module;
};

RString = function(id, module, str) {
	this.id = id;
	this.module = module;
	this.string = str;
};

RArray = function(id, module, arr) {
	this.id = id;
	this.module = module;
	this.array = arr;
};

RErrata = function(id, module, code, description, attributes) {
	this.id = id;
	this.module = module;
	this.code = code;
	this.description = description;
	this.attributes = attributes;
};

RInstruction = function(id, module, name, value, attributes) {
	this.id = id;
	this.module = module;
	this.name = name;
	this.value = value;
	this.attributes = attributes;
};

RDataSet = function(id, module, node) {
	this.id = id;
	this.module = module;
	this.node = node;
}

RDNode = function(name, value, attributes, nodes) {
	this.name = name;
	this.value = value;
	this.attributes = attributes;
	this.nodes = nodes;
};

// Tools
$.fn.XML2String = function(){
	var node = this.get(0);
	return window.XMLSerializer ? new XMLSerializer().serializeToString(node) : node.xml;
};
