Object.extend(beng, {
	/**
		@class
		@name beng.WidgetManager
		@description
			Provides functionality to create, delete, edit, ...
			widget instances
	*/
	WidgetManager : (
		/**
			@lends beng.WidgetManager
		*/
		{
			instances : {},
			instancesNG : {},
			descriptors : [],
			thumbnailCache : {},

			/* ----------------------------------------------------------
				public instance specific
			   ---------------------------------------------------------- */

			/**
				@private
				@description
					Registers a widget instance
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
			*/
			register : function(wcid, wiid, namespace, wvcid, wviid) {
			    namespace = namespace || "";
			    wvcid = wvcid || "";
			    wviid = wviid || "";
			    var key = wiid + ":" + wviid;

			    var ns = beng.WidgetManager.instances[namespace];
			    if (typeof ns == "undefined") {
			        ns = beng.WidgetManager.instances[namespace] = {};
			    }
				ns[key] = new beng.WidgetInstance(
					beng.WidgetManager.getDescriptor(wcid),
					wiid, namespace,
					wvcid, wviid
				);
			},

			registerNG : function(widgetNG) {
			    var key = widgetNG.id + ":" + (widgetNG.wviid || "");
				beng.WidgetManager.instancesNG[key] = widgetNG;
			},


			/**
				@description
					Returns a beng.WidgetInstance for the given wcid and wiid.
					If no beng.WidgetInstance for wcid/wiid exists, it's beeing created.
				@type
					beng.WidgetInstance
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
			*/
			getInstance : function(wcid, wiid, namespace, wvcid, wviid) {
				namespace = namespace || "";
			    wvcid = wvcid || "";
			    wviid = wviid || "";
			    var key = wiid + ":" + wviid;

				var ns = beng.WidgetManager.instances[namespace];
				if (!ns || !ns[key]) {
					beng.WidgetManager.register(wcid, wiid, namespace, wvcid, wviid);
				}
				return beng.WidgetManager.instances[namespace][key];
			},

			getInstanceNG : function(wiid, wviid) {
			    wviid = wviid || "";
			    var key = wiid + ":" + wviid;
				return beng.WidgetManager.instancesNG[key];
			},

			/**
				@description
					Calls a widget instances showNotifications method
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
				@param {boolean} fromMessageTab
					present and true iff called from the "message" tab ( editor repo, src/res/main/resources/xsl/widget.xsl)
			*/
			showNotifications : function(wcid, wiid, fromMessageTab) {
				var notificationCallback = null;
				if ( typeof ( fromMessageTab ) == "boolean" && fromMessageTab == true ) {
					/* we are coming from the message tab ( and not fromt he editor).
					  Reload the message tab when "Ok" is pressed in the config window
					  */
					notificationCallback = function() { getTopWindow().controlpanel.forceReloadMainBody("messages"); };
				}
				var instance = beng.WidgetManager.getInstance(wcid, wiid);
				instance.showNotifications( notificationCallback );
			},

			/**
				@description
					Calls a widget instances configure method
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
			*/
			configure : function(wcid, wiid, wvcid, wviid) {
				var instance = beng.WidgetManager.getInstance(wcid, wiid, "", wvcid, wviid);
				instance.configure();
			},

			/* ----------------------------------------------------------
				public class specific
			   ---------------------------------------------------------- */

			/**
				@description
					Registeres a beng.WidgetDescriptor for the given wcid with data.
				@type
					void
				@param {String} wcid
					widget class id
				@param {Object} data
					widget class id
			*/
			registerWidgetDescriptor : function(wcid, data) {
				beng.WidgetManager.descriptors[wcid] = new beng.WidgetDescriptor(wcid, data);
			},

			/**
				@description
					Returns a beng.WidgetDescriptor for the given wcid and wiid.
					If no beng.WidgetDescriptor for wcid/wiid exists, it's beeing created.
				@type
					beng.WidgetDescriptor
				@throws Error
					if no widget descriptor has been registered for this classId
				@param {String} wcid
					widget class id
			*/
			getDescriptor : function(wcid) {
				if (!beng.WidgetManager.descriptors[wcid]) {
					throw new Error("widget descriptor ["+wcid+"] not found!");
				}
				return beng.WidgetManager.descriptors[wcid];
			},

			/**
				@description
					Dynamically loads a thumbnail for a given widget class
					and appends it to refElem
				@type
					void
				@param {String} classId
					widget class id
				@param {String} wiid
					widget instance id
				@param {String|Element} refElem
					element to append the thumbnail to
			*/
			loadThumbnail : function(classId, wiid, refElem) {
				Common.log("loadThumbnail:" + wiid);
				var decriptor = beng.WidgetManager.getDescriptor(classId);
				var key = ((wiid && decriptor.hasInstanceThumbnail)?classId+"_"+wiid:classId);

			    refElem = $(refElem);
			    var cache;
                Try.these(
                    function() {cache = parent.beng.WidgetManager.thumbnailCache},
                    function() {cache = beng.WidgetManager.thumbnailCache}
                )

                if (cache[key]) {
                    refElem.update(cache[key]);
                    refElem.thumbnailLoaded = true;
                    return;
                }


				var url = createBengRequestUrl(
					"/beng/coma/WidgetMgr.cls",
					{
						action : "getThumbnail",
						wcid : classId,
						wiid : wiid
					}, null, null, true
				);

				var options = {
					method : 'get',
					onSuccess : function(transport) {
						var content = transport.responseText.replace(
							/<\?xml[^>]*>/, ""
						).replace(
							/<!DOCTYPE[^>]*>/, ""
						);
						cache[key] = content;
						refElem.update(content);
						refElem.thumbnailLoaded = true;
					},
					onFailure : function() { Common.log("Error loading: " + refElem); },
					onException : function() { Common.log("Exception loading: " + refElem); }
				};

				new Ajax.Request(url, options);
			},

			/* ----------------------------------------------------------
				lifecycle methods
			   ---------------------------------------------------------- */

			/**
				@description
					creates a new widget instance
				@param {String} wcid
					widget class id
				@param {Object|Hash} [optionsIn.metadata={}]
					Metadata for widget
				@param {Object} [optionsIn.initParams={}]
					Initialization params for the widget
				@param {Object} [optionsIn.ajaxOptions={}]
					Initialization params for the widget
			*/
			create : function(wcid, optionsIn) {
				optionsIn = optionsIn || {};

				var options = (optionsIn.ajaxOptions || {});
				var initParams = (optionsIn.initParams || "");

				var descriptor = beng.WidgetManager.getDescriptor(wcid);

                if (descriptor.isSingelton && !optionsIn.wvcid) {
                    for (var i=0; i < beng.widgetCatalog.length; i++) {
                        if (beng.widgetCatalog[i].wcid == wcid) {
                            var msg = "";
                            try {
                                msg = "Dieses Widget ist bereits auf der Seite <br /><b>"
                                        + beng.editor.editorNG.getSitetree().getPage(beng.widgetCatalog[i].pageId).getNavigationText()
                                        + "</b><br /> eingebunden.<br />";
                            } catch (e) {
                                msg =  "Dieses Widget kann nur einmal in Ihre Webseite eingefügt werden.";
                            }
                            messageBox(
                                  "Fehler",
                                  msg
                                + "<br /><br />"
                                + "Sie können das Widget über die Zwischenablage verschieben oder "
                                + " - falls anwendbar - eine Verknüpfung verwenden."
                            );

                            return;
                        }
                    }
                }


				// override method (beng-proxy)
				options.method = "get";
				options.origOnSuccess = optionsIn.onSuccess || Prototype.emptyFunction;

				options.onSuccess = function(res) {
					var doc = res.responseXML;
					var root = doc.documentElement;
					var isReady = (root.getAttribute("isReady") != "false");

					if (root.getAttribute("ERROR")) {
						alert("Beim Anlegen des Widgets ist ein Fehler aufgetreten.\n"
							+ "Bitte versuchen Sie es später erneut.");
						return;
					}

                    var wiid = root.getAttribute("wiid");
                    var wviid = root.getAttribute("wviid");
                    var wvcid = root.getAttribute("wvcid");

                    var widgetInstance = beng.WidgetManager.getInstance(
                							wcid,
                							wiid,
                							"",
                							wvcid,
                							wviid
                						);
//debugger;
					this.origOnSuccess(widgetInstance, isReady);
				}.bind(options);

                var reqOptions;

                if (optionsIn.wiid && optionsIn.wvcid) {
                    reqOptions = {
						action 	   : "createView",
						wcid	   : wcid,
						wiid       : optionsIn.wiid,
						wvcid       : optionsIn.wvcid,
						initParams : initParams
					}
                } else {
                    reqOptions = {
						action 	   : "create",
						wcid	   : wcid,
						initParams : initParams
					}
                }

				var url = createBengRequestUrl(
					"/beng/coma/WidgetMgr.cls",
					reqOptions,
					beng.WidgetManager.__metadata2WidgetHeader(optionsIn.metadata || {})
				);

				return new Ajax.Request(url, options);
			},
            /**
            @description
                makes a staged widget-configuration persistent
            @param {String} documentId
                the id of the document for which the widgets should be made persistent
            @param {Function} [optionsIn]
                Additional ajax-options
        */
            makePersistent :  function (documentId, ajaxOptions){
                ajaxOptions = ajaxOptions || {};

                var url = createBengRequestUrl(
                    "/beng/coma/WidgetMgr.cls",
                    {
                        "action" : "makePersistent",
                        "documentId" : documentId
                    },
                    {},
                    {
                        skipProcess : true
                    }
                );
                var options = Object.extend(ajaxOptions || {}, {
                    method : "post"
                });
		new Ajax.Request(url, options);
            },
            /**
            @description
                rolls back a staged widget-configuration
            @param {String} documentId
                the id of the document for which the widget-configuration should be rolled back
            @param {Function} [optionsIn]
                Additional ajax-options
        */
            rollbackParameters :  function (documentId, ajaxOptions){
                ajaxOptions = ajaxOptions || {};

                var url = createBengRequestUrl(
                    "/beng/coma/WidgetMgr.cls",
                    {
                        "action" : "rollbackParameters",
                        "documentId" : documentId
                    },
                    {},
                    {
                        skipProcess : true
                    }
                );
                var options = Object.extend(ajaxOptions || {}, {
                    method : "post"
                });
                new Ajax.Request(url, options);
            },
			/**
				@description
					Stores/updates a widgets widget parameters
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
				@param {Function} [optionsIn.onSuccess=null]
					An optinal callback function
				@param {Object} [optionsIn.params=null]
					The parameters to save
				@param {Object} [optionsIn.ajaxOptions={}]
					Initialization params for the widget
			*/
			updateParameters : function(wcid, wiid, optionsIn) {
				optionsIn = optionsIn || {};

				var url = createBengRequestUrl(
					"/beng/coma/WidgetMgr.cls",
					{
						action : "updateParameters",
						wiid : wiid
					},
					{},
					{
						skipProcess : true
					}
				);

				var options = Object.extend(optionsIn.ajaxOptions || {}, {
					method : "get",
					onSuccess : function() {
						// TODO: maybe this is not portable for TOI
						try {
							var topWnd = getTopWindow();
							if (topWnd.controlpanel) {
								var editor = topWnd.controlpanel.getEditorWnd().beng.editor;
								if(editor.editorNG && typeof editor.editorNG.setModified == "function"){
									editor.editorNG.setModified(true);
								}
							}
						} catch (ignored) {}

						if (Object.isFunction(optionsIn.onSuccess)) {
							optionsIn.onSuccess();
						}
					},
					evalScripts : true
				});

				if (optionsIn.params) {
					var doc = getTopWindow().createDocument("widget");
					var root = doc.documentElement;

					Object.keys(optionsIn.params).each(function(name) {
						var paramElem = doc.createElement("parameter");
						paramElem.setAttribute("name", name);
						paramElem.setAttribute("value", optionsIn.params[name].toString());
						root.appendChild(paramElem);
					});

					options.method = "post";
					options.postBody = doc;
					options.contentType = "text/xml";
				}
				new Ajax.Request(url, options);
			},

			/**
				@description
					Reloads a widget instance
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
				@param {Object|Hash} [optionsIn.metadata={}]
					Metadata for widget
				@param {boolean} [optionsIn.bodyonly=false]
					Indicates whether the widget should be loaded including
					decoration (bodyonly=false) or without any decoration (bodyonly=true).
					If optionsIn.refElement is set bodyonly defaults to false.
				@param {Element} [optionsIn.refElement=null]
					An optional element to be replaced by loaded widget content.
				@param {Function} [optionsIn.onSuccess=null]
					An optinal callback function
				@param {Function} [optionsIn.insertCallback=null]
					An optional callback which is called instead of replacing refElement
					or - which is the default - updating the instance.container. First paramater
					passed to the callback is the content loaded.
			*/
			updateContent : function(wcid, wiid, optionsIn, wvcid, wviid) {
				optionsIn = optionsIn || {};

				var refElement = (optionsIn.refElement || null);
				var instance = beng.WidgetManager.getInstance(wcid, wiid, "", wvcid, wviid);

				var metadata = optionsIn.metadata || {};
				var widgetRoot = $("widgetroot_" + wiid);
				if (widgetRoot) {
					metadata = beng.EditorNG.Metadata.getCalculated(widgetRoot).merge(metadata);
				}

				var url = createBengRequestUrl(
					"/beng/coma/WidgetMgr.cls",
					{
						action : "reload",
						wcid : wcid,
						wiid : wiid,
						wvcid : wvcid || "",
						wviid : wviid || "",
						bodyonly : "" + (
							refElement ? false : (optionsIn.bodyonly || false)
						)
					},
					beng.WidgetManager.__metadata2WidgetHeader(metadata)
				);

				new Ajax.Request(url, {
					method : 'get',
					onSuccess : function(transport) {
						var content = transport.responseText.replace(
							/<\?xml[^>]*>/, ""
						).replace(
							/<!DOCTYPE[^>]*>/, ""
						);

						if (Object.isFunction(optionsIn.insertCallback)) {
							optionsIn.insertCallback(content);
						} else if (refElement) {
							$(refElement).replace(content);
						} else {
//alert(3 + "\n" + content);
						    try{
						        $(instance.container).update(content);
						    }catch(e){

						    }
						}

						if (Object.isFunction(optionsIn.onSuccess)) {
							optionsIn.onSuccess();
						}
					}
				});
			},

			/**
				@param {String} wcid
					widget class id
				@param {String} wiid
					widget instance id
				@param {Object|Hash} [optionsIn.metadata={}]
					widget metadata
				@param {Function} [optionsIn.callbackFn=null]
					callback function
				@type void
				@description
					Updates widget content using the provided header arguments;
					calls callbackFn if present, after update is done. Only the
					widget "body" is updated...
			*/
			updateWidget : function(wcid, wiid, optionsIn, wvcid, wviid) {

				optionsIn = optionsIn || {};
				beng.WidgetManager.updateContent(
				    wcid, wiid,
				    {
    					metadata : (optionsIn.metadata || {}),
    					bodyonly : true,
    					onSuccess : optionsIn.callbackFn || Prototype.emptyFunction
    				},
    				wvcid, wviid
				);
			},

			/* ----------------------------------------------------------
				private
			   ---------------------------------------------------------- */

			__metadata2WidgetHeader : function(metadata) {
				/*
					container -> "x-widgetcontainer"
					alignment -> "x-widgetalignment"
					display   -> "x-widgetdisplay"
					width     -> "x-widgetwidth"
					height    -> "x-widgetheight"
					maxwidth  -> "x-widgetmaxwidth"
				*/
				if (Object.isHash(metadata)) {
					metadata = metadata.toObject();
				}

				var headerObj = new Hash();
				$w("container alignment display width height maxwidth").each(function(key) {
					if (metadata[key]) {
						headerObj.set("x-widget" + key, metadata[key]);
					}
				});
				return headerObj.toObject();
			},

			TERM_END : null
		}
	)
});


