var Message = Class.create({
	type: 0,
	text: "",
	initialize: function(type, text){
		if (typeof type != "undefined") this.type = type;
		if (typeof text != "undefined") this.text = text;
	},

	GetType: function(){
		return (typeof(this.type) == "undefined" ? "Unknown type" : Message.Types[this.type]);
	},

	GetText: function(){
		return this.text;
	},

	toString: function(){
		return ("Message:\ntype=" + this.GetType() + "\ntext=" + this.GetText());
	},

	Serialize: function(serializer){
		serializer.BeginType("Message");
		serializer.Serialize(Number(this.type));
		serializer.Serialize(String(this.text));
		serializer.EndType("Message");
	},

	Restore: function(restorer) {
		restorer.BeginType("Message");
		this.type = restorer.Restore();
		this.text = restorer.Restore();
		restorer.EndType("Message");
	}
});

var MessageList = Class.create({
	initialize: function(){
		this.messages_ = new Array();
	},
	GetMessages: function(){ return this.messages_; },
	AddMessage: function(msg){ this.messages_.push(msg); },
	Clear: function(){ this.messages_.clear(); },

	Serialize: function(serializer){
		serializer.BeginType("list");
		this.messages_.invoke("Serialize", serializer);
		serializer.EndType("list");
	},

	Restore: function(restorer){
		this.messages_.clear();
		restorer.BeginType("list");
		while (restorer.GetType() == "Message") this.messages_.push(restorer.Restore());
		restorer.EndType("list");
	},

	toString: function(){
		return this.messages_.invoke("toString").join(", ");
	}
});

Message.Types = ["MESSAGE", "WARNING", "EXCEPTION"];

var Herald = Class.create({
	messages: [],
	visible: false,
	htmlCreated: false,
	container: null,
	parentType: "none",

	Show: function(){
		if (!this.container) return;
		var contentsWidth = Element.getWidth(this.container);
		var documentWidth = Element.getWidth(document.body);
		Element.setStyle(this.container, {
			"left": Math.max(.5 * (documentWidth - contentsWidth), 0) + "px",
			"visibility": "inherit"
		});
	},

	Hide: function(){
		if (!this.container) return;
		Element.setStyle(this.container, {"visibility": "hidden"});
	},

	Append: function(heraldContainer){
		document.body.appendChild(heraldContainer);
	},

	initialize: function(params){
		if (typeof(params) == "undefined") return;
		if (typeof(params.parentContainer) != "undefined"){
			var parentContainer = $(params.parentContainer);
			if (!parentContainer) return;
			this.parentType = "element";
			this.Append = function(heraldContainer){
				parentContainer.appendChild(heraldContainer);
			};
			this.Show = function(){
				Element.setStyle(parentContainer, {"display": ""});
			};
			this.Hide = function(){
				Element.setStyle(parentContainer, {"display": "none"});
			};
			return;
		}
		if (typeof(params.Append) == "function" && typeof(params.Show) == "function" && typeof(params.Hide) == "function"){
			this.parentType = "object";
			this.Append = params.Append.bind(params);
			this.Show = params.Show.bind(params);
			this.Hide = params.Hide.bind(params);
		}
	},

	addMessage: function(message){
		var li = new Element("li");
// 		alert(message.type + " " + message.GetType())
		li.appendChild(new Element("input", {"type": "button", "class": message.GetType()}));
		li.appendChild(document.createTextNode(message.GetText()));
		this.createHTML();
		this.container.appendChild(li);
		this.messages.push({"message": message, "htmlNodes": [li]});
		this.show(true);
// 		this.removeFirstMessage.bind(this).delay(Herald.messageLifeTime);
	},

	show: function(force){
		if (!force && this.visible) return;
		this.createHTML();
		this.Show();
		this.visible = true;
	},

	removeFirstMessage: function(){
		if (this.messages.length == 0) return;
		var firstMessage = this.messages.shift();
		if (this.messages.length == 0) this.hide();
		firstMessage.htmlNodes.invoke("remove");
	},

	hide: function(){
		if (!this.visible) return;
		this.Hide();
		this.container.update();
		this.messages.length = 0;
		this.visible = false;
	},

	getAdditionalClassName: function(){
		switch (this.parentType){
			case "none": return "Standalone";
			case "element": return "Static";
			case "object": return "Embedded";
			default: return "";
		}
	},

	createHTML: function(){
		if (this.htmlCreated) return;
		this.container = new Element("ul", {"id": this.ID(),
				"class": "Herald " + this.getAdditionalClassName()});
		this.Append(this.container);
		Event.observe(document, "click", this.hide.bind(this));
		JSControl.AddWindowChangeListener(this, this.hide);
		this.htmlCreated = true;
	},

	ID: function(){
		if (typeof this.id == "undefined") this.id = Herald.generateID();
		return this.id;
	}
});

Herald.messageLifeTime = 5;
Herald.counter = 0;
Herald.GetInstance = function(params){
	if (typeof(params) == "undefined" || typeof(params.parentContainer) != "undefined" && !$(params.parentContainer) || //
			(typeof(params.Append) != "function" || typeof(params.Show) != "function" || typeof(params.Hide) != "function")){
		if (typeof(Herald.standaloneInstance) == "undefined") Herald.standaloneInstance = new Herald();
		return Herald.standaloneInstance;
	}
	return new Herald(params);
}

Herald.idPrefix = "Herald-";
Herald.generateID = function(){
	var id = Herald.idPrefix + (Herald.counter++);
	while($(id)) id = Herald.idPrefix + (Herald.counter++);
	return id;
}

var MessageProcessor = Class.create({
	initialize: function(eventType, params){
		this.herald = Herald.GetInstance(params);
		SubscribeOnEvent(eventType, "document", this.ProcessMessageEvent, this);
	},

	ProcessMessageEvent: function(event){
		var data = event.Data();
		if (data instanceof Message) return this.ProcessMessage(data);
		else return this.ProcessMessage(event.Data().args["Message"]);
	},

	ProcessMessage: function(message){
		if (typeof(message) == "undefined") return;
		this.herald.addMessage(message);
	}
});

var GlobalMessageProcessor = new MessageProcessor("WE_APP_MESSAGE");

