var Serializer = Class.create();
Serializer.prototype = {

	hash: null,
	prefixStack: null,

	initialize: function(object, options){
		this.hash = $H();
		this.prefixStack = [];

		this.setOptions(options);
		this.addObject(object);

		if(this.options.appendMilis){
			this.hash.set("milis", String(new Date().getTime()));
		}
	},

	addObject: function(object){
		if(object != null){
			this.serializeObject(object);
		}
	},

	setOptions: function(options){
		this.options = {
			rootName: "",
            onBeforeProperty: Prototype.K,
            onSerializeProperty: Prototype.K.bind(window, true),
            transformDate: function(date){ return date.getTime(); },
            appendMilis: false
        };

        Object.extend(this.options, options || {});
        this.options.rootName = (String.isNotEmpty(this.options.rootName) ? (this.options.rootName+".") : "");
	},

	toString: function(){
		return this.hash.toQueryString();
	},

	toHash: function(){
		return this.hash.clone();
	},
	
	serializeObject: function(object){
		var completePrefix = this.getPrefix();

		var callback = function(pair){
			if(pair == null || pair.key == null || pair.value == null || typeof(pair.value) == "function") return;
			pair = this.options.onBeforeProperty(pair, completePrefix);
			if(pair == null || pair.key == null || pair.value == null) return;

			var prop = pair.key;
			var value = pair.value;

			if(value instanceof Array){
				var entry = new Serializer.PrefixEntry(prop);
				this.prefixStack.push(entry);
				for(var i=0; i < value.length; i++){
					entry.index = i;
					this.serializeObject(value[i]);
				}
				this.prefixStack.pop();
			}else if(typeof(value) == "string" || typeof(value) == "number" || typeof(value) == "boolean" || value instanceof Date){
				var shouldSerialize = this.options.onSerializeProperty(completePrefix, prop, value);
				if(shouldSerialize){
					if(value instanceof Date){
						value = this.options.transformDate(value);
					}
					this.hash.set(this.options.rootName + completePrefix + prop,  value);
				}
			}else{
				var entry = new Serializer.PrefixEntry(prop);
				this.prefixStack.push(entry);
				this.serializeObject(value);
				this.prefixStack.pop();
			}
		};

		$H(object).each(callback.bind(this));
	},

	getPrefix: function(){
		if(this.prefixStack.length == 0){
			return "";
		}

		var prefix = [];

		for(var i=0, array=this.prefixStack, l=array.length; i < l; i++){
			prefix.push(array[i].toString());
			prefix.push(".");
		}

		return prefix.join("");
	}
};

Serializer.PrefixEntry = Class.create();
Serializer.PrefixEntry.prototype = {
	initialize: function(propName, index){
		this.propName = propName;
		this.index = index;
	},

	toString: function(){
		return this.propName + (this.index != null ? "["+this.index+"]" : "" );
	}
};

/* Tool used to export some object through text format: 
var TableData = Class.create();
TableData.prototype = {
	initialize: function(offset){
		this.lines = $A();
		this.lengthsByColumn = $A();
		this.maxTableColumns = 0;
		this.offset = offset || 2;
	},

	addLine: function(line){
  		if(line == null) return;

		this.lines.push(line);
  		this._addLength(line);
  	},

	addOffset: function(offset){
		if(offset == null || offset == 0) return;

		this.maxTableColumns = 0;
		for(var i=0; i < this.lengthsByColumn.length; i++){
			this.lengthsByColumn[i] = this.lengthsByColumn[i] + offset;
			this.maxTableColumns += this.lengthsByColumn[i];
		}
	},

	formatHTML: function(){
		var html = [];
		html.push("<font face='Courier New, Courier, Monospace'>");
		html.push(this.format().replaceAll(" ", "&nbsp").replaceAll("\n", "<br>"));
		html.push("</font>");
		return html.join("");
	},

	format: function(){
		this.addOffset(this.offset);
		var buffer = [];
		for(var i=0; i < this.lines.length; i++){
			var line = this.lines[i];
	
			if(i == 1){
				buffer.push(this._fillSpaces(this.maxTableColumns-1, "-"));
				buffer.push("\n");
			}
	
			for(var j=0; j < line.cols.length; j++){
				var currColumnMaxLength = this.lengthsByColumn[j];
				var columnContent = line.get(j);
				var currColumnLength = columnContent.length;
				var diff = currColumnMaxLength - currColumnLength;
	
				buffer.push(columnContent);
				buffer.push(this._fillSpaces(diff));
			}
	
			buffer.push("\n");
		}
	
		return buffer.join("");
	},

	toString: function(){
		return "<tableData lines=["+this.lines+"],lengthsByColumn=["+this.lengthsByColumn+"]>";
	},

	_fillSpaces: function(num, str){
		str = str || " ";
	  	var buff = [];
	  	for(var i=0; i < num; i++)
	  		buff.push(str);
	  	return buff.join("");
	},

	_addLength: function(line){
		this.maxTableColumns = 0;
		for(var i=0; i < line.cols.length; i++){
			var currLength = this.lengthsByColumn[i] || 0;
			var maxLength = Math.max(currLength, line.getLength(i));
			this.lengthsByColumn[i] = maxLength;
			this.maxTableColumns += maxLength;
		}
	}
};

TableData.LineInfo = Class.create();
TableData.LineInfo.prototype = {
	initialize: function(cols){
		this.cols = $A(cols).collect(function(c){
			return (c || "");
		});
	},

  	get: function(i){
  		var c = "";
  		if(i >= 0 && i < this.cols.length)
  			c = this.cols[i]
  		return c;
  	},

  	getLength: function(i){
  		return this.get(i).length;
  	},

  	toString: function(){
  		return "<LineInfo cols=["+this.cols+"]>";
  	}
};
*/
