/*
 * Copyright 2005 - 2009  Zarafa B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3, 
 * as published by the Free Software Foundation with the following additional 
 * term according to sec. 7:
 *  
 * According to sec. 7 of the GNU Affero General Public License, version
 * 3, the terms of the AGPL are supplemented with the following terms:
 * 
 * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
 * the Program under the AGPL does not imply a trademark license.
 * Therefore any rights, title and interest in our trademarks remain
 * entirely with us.
 * 
 * However, if you propagate an unmodified version of the Program you are
 * allowed to use the term "Zarafa" to indicate that you distribute the
 * Program. Furthermore you may use our trademarks where it is necessary
 * to indicate the intended purpose of a product or service provided you
 * use it in accordance with honest practices in industrial or commercial
 * matters.  If you want to propagate modified versions of the Program
 * under the name "Zarafa" or "Zarafa Server", you may only do so if you
 * have a written permission by Zarafa B.V. (to acquire a permission
 * please contact Zarafa at trademark@zarafa.com).
 * 
 * The interactive user interface of the software displays an attribution
 * notice containing the term "Zarafa" and/or the logo of Zarafa.
 * Interactive user interfaces of unmodified and modified versions must
 * display Appropriate Legal Notices according to sec. 5 of the GNU
 * Affero General Public License, version 3, when you propagate
 * unmodified or modified versions of the Program. In accordance with
 * sec. 7 b) of the GNU Affero General Public License, version 3, these
 * Appropriate Legal Notices must retain the logo of Zarafa or display
 * the words "Initial Development by Zarafa" if the display of the logo
 * is not reasonably feasible for technical reasons."
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *  
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

/**
 * WebClient
 * Object which is available for every other object. It startups the client and
 * it is responsible for loading and deleting modules.
 */ 
function WebClient()
{
	this.modules = new Object();
}

/**
 * Function which intializes the WebClient
 * @param string base_url the base url of the webaccess
 * @param string modulePrefix the module prefix (used for create unique id in the XML)
 * @param object availableModules all the available modules
 * @param object settings all the settings
 * @param object pluginData all the plugin data
 */ 
WebClient.prototype.init = function(base_url, modulePrefix, availableModules, settings, pluginData)
{
	this.window = window;
	this.base_url = base_url;
	this.modulePrefix = modulePrefix;
	this.dialogs = new Object();

	this.xmlrequest = new XMLRequest(modulePrefix);
	this.dispatcher = new Dispatcher(availableModules);
	this.menu = new Menu();

	this.settings = this.dispatcher.loadModule("settingsmodule");
	if (this.settings) {
		var settingsID = this.addModule(this.settings);
		this.settings.init(settingsID);
		this.settings.settings = settings;
	}else if (parentWebclient && parentWebclient.settings){
		this.settings = parentWebclient.settings;
	}
	
	// Fire up the PluginManager, let is know about the plugins and initialize them.
	this.pluginManager = new PluginManager();
	this.pluginManager.setPluginData(pluginData);
	this.pluginManager.init();

	startKeepAlive();
}

WebClient.prototype.setUserInfo = function(username, fullname, entryid, emailaddress)
{
	this.username = username;
	this.fullname = fullname;
	this.userEntryid = entryid;
	this.emailaddress = emailaddress;
}


/**
 * Function which starts up the client. Set the right events and creates the
 * hierarchy list module. 
 */ 
WebClient.prototype.startClient = function()
{
	// disable context menu
	dhtml.addEvent(-1, document.body, "mousedown", eventBodyMouseDown);
	dhtml.addEvent(-1, document.body, "mouseup", checkMenuState);
	dhtml.addEvent(-1, document.body, "contextmenu", eventBodyMouseDown);
	
	this.inputmanager = new InputManager();
	this.inputmanager.init();
	this.layoutmanager = new LayoutManager();
	dhtml.addEvent(-1, window, "resize", eventWebClientResize);	

	// TODO
	if (window.addEventListener){
        window.addEventListener("unload", eventWebClientUnload, false);
    } else if (window.attachEvent){
        window.attachEvent("onunload", eventWebClientUnload);
    }

    // Every "mouseup" on the body element, the menu's (context and default) should be checked 
	// if they should be removed or hidden from the page.
	this.inputmanager.bindEvent(webclient, "mouseup", checkMenuState);

	// Drag/Drop events
	dhtml.addEvent(-1, document.body, "mouseup", eventDragDropMouseUpDraggable);
	dhtml.addEvent(-1, document.body, "mousemove", eventDragDropMouseMoveDraggable);

	
	// Load hierarchy module
	this.hierarchy = this.dispatcher.loadModule("hierarchymodule");
	if(this.hierarchy) {
		var moduleID = this.addModule(this.hierarchy);
		// Add module to the "left" of the screen
		var element = this.layoutmanager.addModule(moduleID, "left");

		this.hierarchy.addEventHandler("changefolder", this, this.onHierarchyChangeFolder);

		this.hierarchy.init(moduleID, element);
		this.hierarchy.list(true);
	}
	
	if (webclient.settings.get("global/startup/folder","inbox")=="inbox"){	
	    // If the user has selected 'open inbox at startup', we know we want the inbox
	    // so we can post the request here already, instead of waiting for the hierarchy module
	    // to load.
	    
	    // This ignores the first event from the hierarchy module since we have already loaded that
	    // folder
    	this.folderOpened = true;
    	this.openFolder();
    }
	
	// start notification
	this.notification = this.dispatcher.loadModule("notificationmodule");
	if (this.notification) {
		var notificationID = this.addModule(this.notification);
		this.notification.init(notificationID);
	}

	// start reminders
	this.reminder = this.dispatcher.loadModule("reminderlistmodule");
	if (this.reminder) {
		var reminderID = this.addModule(this.reminder);
		this.reminder.init(reminderID);
	}

	// start resolvenames
	this.resolvenames = this.dispatcher.loadModule("resolvenamesmodule");
	if (this.resolvenames) {
		var resolvenamesID = this.addModule(this.resolvenames);
		this.resolvenames.init(resolvenamesID);
	}
	
	// Load the buttons module
	this.buttonsModule = this.loadModule("buttonsmodule", false, "left", null, BOX_LAYOUT, INSERT_ELEMENT_AT_BOTTOM);
	this.buttonsModule.hierarchy = this.hierarchy;

	// update layoutmanager
	this.layoutmanager.updateElements("left");
	
	eventWebClientResize();
	this.xmlrequest.sendRequest();
	
	// if action attributes exist then process according to action type
	var action_url = window.location.search;
	if(action_url.length > 0) {
		// remove first '?' from the URL
		action_url = action_url.replace("?", "");

		// remove '?' in URL that is appended before 'subject' tag
		action_url = action_url.replace("?", "&");

		this.handleURLAction(action_url);
	}
}

/** 
 * Function which executes proper action when action arguments are passed
 * in the URL of WA
 *
 * @param string action arguments that are passed in URL
 */
WebClient.prototype.handleURLAction = function (action_url)
{
	var keyValuePairs = dhtml.URLToObject(action_url);

	// check action type
	if(typeof keyValuePairs == "object" && keyValuePairs["action"] == "mailto") {
		if(typeof keyValuePairs["to"] != "undefined") {
			// remove 'mailto:' tag from 'to' field
			keyValuePairs["to"] = keyValuePairs["to"].replace("mailto:", "");

			// if there are more then one email address in 'to' field then 
			// make it semicolon seperated instead of default comma seperated
			keyValuePairs["to"] = keyValuePairs["to"].replace(/[,]/g, "; ");
		}

		// didn't find on internet anything about multiple CC & BCC fields
		// in mailto links, although its handled here for precaution
		if(typeof keyValuePairs["cc"] != "undefined") {
			// if there are more then one email address in 'cc' field then 
			// make it semicolon seperated instead of default comma seperated
			keyValuePairs["cc"] = keyValuePairs["cc"].replace(/[,]/g, "; ");
		}

		if(typeof keyValuePairs["bcc"] != "undefined") {
			// if there are more then one email address in 'bcc' field then 
			// make it semicolon seperated instead of default comma seperated
			keyValuePairs["bcc"] = keyValuePairs["bcc"].replace(/[,]/g, "; ");
		}

		var createmail_url = "";
		for(var key in keyValuePairs) {
			if(key != "action") {
				// action attribute is not needed in creating createmail dialog URL
				createmail_url = createmail_url + "&" + key + "=" + keyValuePairs[key];
			}
		}

		// open createmail dialog
		webclient.openWindow(this, "createmail", DIALOG_URL + "task=createmail_standard" + createmail_url);
	}
}
 

/**
 * Function which loads a module and returns the module object.
 * @param string moduleName module name
 * @param string title title of the module
 * @param string position position of module in the webclient
 * @param object data data (storeid, entryid, etc.)
 * @param integer LAYOUT the LAYOUT constant
 * @param integer INSERT_ELEMENT_AT the INSERT_ELEMENT_AT constant
 * @return object module object 
 */ 
WebClient.prototype.loadModule = function(moduleName, title, position, data, LAYOUT, INSERT_ELEMENT_AT)
{
	// If LAYOUT is BORDER_LAYOUT, remove all elements in that position
	if(LAYOUT == BORDER_LAYOUT) {
		var elements = this.layoutmanager.getElements(position);
		
		for(var moduleID in elements)
		{
			var module = this.getModule(moduleID);
			
			if(module) {
				this.deleteModule(module);
			}
		}
		// Removes all resizebar when a new module is loading within given position.
		this.layoutmanager.removeAllresizebars(position);
	}

	// Load module
	var module = this.dispatcher.loadModule(moduleName);

	if(module) {
		// Add module
		var moduleID = this.addModule(module);
		// Add element to LayoutManager
		var element = this.layoutmanager.addModule(moduleID, position, BOX_LAYOUT, INSERT_ELEMENT_AT);

		// Initialize module
		module.init(moduleID, element, title, data);
		module.list();
		
		// Update elemens (resize)
		this.layoutmanager.updateElements(position);
	}
	
	// Resize after loading module.
	eventResizePanes(false, false, false);
	
	return module;
}

/**
 * Function which returns a module.
 * @param integer moduleID the module id
 * @return object module object  
 */ 
WebClient.prototype.getModule = function(moduleID)
{
	var module = false;
	
	if(this.modules[moduleID]) {
		module = this.modules[moduleID];
	} else if(this.modules[this.modulePrefix + "_" + moduleID]) {
		module = this.modules[this.modulePrefix + "_" + moduleID];
	}else {
		// Loop through dialogs
		for(var dialogname in this.dialogs){
			if(this.dialogs[dialogname].window){
				if(typeof this.dialogs[dialogname].window.webclient != "unknown" && typeof this.dialogs[dialogname].window.webclient == "object" && this.dialogs[dialogname].window.webclient != null){
					module = this.dialogs[dialogname].window.webclient.getModule(moduleID);
					if(module){
						break;
					}
				}
			}
		}
	}
	
	return module;
}

/**
* Function which returns an array of modules who are matching "moduleName"
*
*@param string moduleName the name of the modules we want to find
*@param array list of modules
*/
WebClient.prototype.getModulesByName = function(moduleName)
{
	var modules = new Array();
	for(var moduleID in this.modules){
		if (this.modules[moduleID] && this.modules[moduleID].getModuleName() == moduleName){
			modules.push(this.modules[moduleID]);
		}
	}
	return modules;
}

/**
 * Function which adds a module to the modules list.
 * @param object module module object
 * @return integer module id
 * @todo
 * - Clean up the modules list, after a while, otherwise the modules list is long (could be memory leak)    
 */ 
WebClient.prototype.addModule = function(module)
{
	var moduleID = 0;

	while(typeof(this.modules[this.modulePrefix + "_" + moduleID]) != "undefined")
	{
		moduleID++;
	}
	
	this.modules[this.modulePrefix + "_" + moduleID] = module;

	return moduleID;
}

/**
 * Function which delets a module.
 * @param object module module object 
 */ 
WebClient.prototype.deleteModule = function(module)
{
	if(this.modules[this.modulePrefix + "_" + module.id]) {
		this.modules[this.modulePrefix + "_" + module.id] = false;
		// Delete module on server
		this.xmlrequest.deleteModule(this.modulePrefix + "_" + module.id);
		
		if(module.destructor) {
			// Destructor
			module.destructor();
		}
		
		// Delete element in layout manager
		this.layoutmanager.deleteElement(module.id);
	}
}

/**
 * Function which opens a new window.
 * @param object moduleObject module object
 * @param string name window name
 * @param string url url
 * @param integer width width of the window
 * @param integer height height of the window
 * @param resizable boolean true - window is resizable, false - window is not resizable     
 */ 
WebClient.prototype.openWindow = function(moduleObject, name, url, width, height, resizable) 
{
	this.clearDialogs();

	var windowwidth = 780;
	if(typeof(width)!="undefined") {
		windowwidth = width;
	}
	
	var windowheight = 560;
	if(typeof(height)!="undefined") {
		windowheight = height;
	}
	
	var windowresizable = 1;
	if(typeof(resizable)!="undefined") {
		windowresizable = resizable?1:0;
	}

	name = name + "_" + (new Date().getTime());
	while(this.dialogs[name]){
		name += "1";
	}
	this.dialogs[name] = new Object();
	this.dialogs[name]["module"] = moduleObject;
	this.dialogs[name]["window"] = dialog(url, name, "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=" + windowresizable + ",width=" + windowwidth + ",height=" + windowheight + ",top=" + ((screen.height / 2) - (windowheight / 2)) + ",left=" + ((screen.width / 2) - (windowwidth / 2)));
	return this.dialogs[name]["window"];
}

/**
 * Function which opens a modal dialog.
 * @param object moduleObject module object
 * @param string name window name
 * @param string url url
 * @param integer width width of the window
 * @param integer height height of the window
 */ 
WebClient.prototype.openModalDialog = function(moduleObject, name, url, width, height, callback, callbackdata, windowdata) 
{
	this.clearDialogs();

	name = name + "_" + (new Date().getTime());
	while(this.dialogs[name]){
		name += "1";
	}
	this.dialogs[name] = new Object();
	this.dialogs[name]["module"] = moduleObject;
	this.dialogs[name]["window"] = modal(url, name, "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=0,width=" + width + ",height=" + height + ",dialogWidth=" + width + " px,dialogHeight=" + (height + 50) + " px,top=" + ((screen.height / 2) - (height / 2)) + ",left=" + ((screen.width / 2) - (width / 2)), callback, callbackdata, windowdata);
	return this.dialogs[name]["window"];
}

WebClient.prototype.clearDialogs = function()
{
	for(name in webclient.dialogs){
		try {
			if (!webclient.dialogs[name].window || webclient.dialogs[name].window.name!=name){
				delete webclient.dialogs[name];
			}
		} catch(e) {
		//	alert(e.description);
			delete webclient.dialogs[name];
		}
	}
}

WebClient.prototype.requestReload = function()
{
	this.xmlrequest.xmlbuilder.addReloadRequest();
	this.xmlrequest.hasData = true;
}

WebClient.prototype.reload = function()
{
	this.window.location = this.base_url; 
}

/**
 * Called when hierarchy module has selected a folder
 */
WebClient.prototype.onHierarchyChangeFolder = function(storeid, folder)
{
	if (webclient.settings.get("global/startup/folder","inbox")=="last"){	
		webclient.settings.set("global/startup/folder_lastopened",folder["entryid"]);
	}

	if(!this.folderOpened) {
    	this.openFolder(storeid, folder);
    }

    // Any next request should trigger openFolder
    this.folderOpened = false;
}

/**
 * Open a folder by showing it in a listmodule
 */
WebClient.prototype.openFolder = function(storeid, folder)
{
	var data = new Object;
	var name;
	
	if(storeid)
    	data["storeid"] = storeid;
    if(folder) {
    	data["entryid"] = folder["entryid"];

        var moduleName = folder["container_class"].substring(folder["container_class"].indexOf(".") + 1).toLowerCase();
        if(moduleName.indexOf(".") != -1) {
            // if IPF.Note.OutlookHomepage
            moduleName = moduleName.substring(0, moduleName.lastIndexOf("."));
        }

        moduleName = moduleName + "list";
        name = folder["display_name"];
    } else {
        moduleName = "maillist";
        name = _("Inbox");
		//Here we get the inbox folder data from settings to get its display settings.
		data["storeid"] = webclient.settings.get("folders/entryid_inbox/storeid","");
 		data["entryid"] = webclient.settings.get("folders/entryid_inbox/entryid","");
    }

	// if folder is an appointmentlist, load the folder via the datepicker
	if (moduleName == "appointmentlist") {
		if (!webclient.datepicker){
			webclient.datepicker = webclient.loadModule("datepickerlist", _("Date picker"), "left", data, BOX_LAYOUT, INSERT_ELEMENT_AT_TOP);
		}
		webclient.datepicker.loadFolder(folder, data);
	} else {
		// this is not an appointment list, so remove (if exists) the datepicker
		if (webclient.datepicker){
			webclient.deleteModule(webclient.datepicker);
			webclient.datepicker = null;
			dragdrop.updateTargets("folder");
		}
		webclient.loadModule(moduleName, name, "main", data, BORDER_LAYOUT);
	}
}

/**
 * Event: Function which resizes the webclient. 
 */ 
function eventWebClientResize(moduleObject, element, event)
{
	var windowWidth = document.documentElement.clientWidth;
	var windowHeight = document.documentElement.clientHeight;
	
	var top = dhtml.getElementById("top");
	var footer = dhtml.getElementById("footer");

	// Setup left pane (height only)
	var left = dhtml.getElementById("left");
	if(left)
		left.style.height = (windowHeight - top.clientHeight - footer.clientHeight) - 10 + "px";
	
	var main = dhtml.getElementById("main");
	
	// Setup main pane
	if(main) {
		main.style.height = (windowHeight - top.clientHeight - footer.clientHeight) - 10 + "px";
	}
	
	if(webclient.layoutmanager)
		webclient.layoutmanager.updateAllElements();
		
	/**
	 * This function retrieves settings and positions the panes, 
	 * this function is called because updateElements() 
	 * overrides the default sizes.
	 */
	eventResizePanes(false, false, false);
}

/**
 * Event: Function which fires on unload. It sends the last XML requets to the server. 
 */
function eventWebClientUnload()
{
	// try to close any dialog that is still open
	for(name in webclient.dialogs){
		try{
			webclient.dialogs[name].window.close();
		}catch(e){
		}
	}

	webclient.xmlrequest.sendRequest();
	dhtml.removeEvents(document.body);
}

function eventBodyMouseDown(moduleObject, element, event)
{
    // For FF only
    if(event.forceDefaultAction)
        return true;
	return false;
}

// Set the handler of an element to this to force the default browser action
// instead of the body default handler
function forceDefaultActionEvent(moduleObject, element, event)
{
    // IE6/7 code
    if(window.BROWSER_IE) {    
        event.cancelBubble = true;
        return true;
    } else {
        // FF code
        event.forceDefaultAction = true;
    }
}