function controller()
{
	this.id = null;
	this.type = null;
	this.meta = null;
	this.graph = null;
	this.portlet = null;
	this.version = NEWVERSION;
	this.views = null;
	this.title="";
	this.formName = null
	this.url = null
	this.filterJson = {};

	// Mandatory implementation method for locating JSTL template for layout
	this.locateLayoutTemplate = function()
	{
		throw "Layout template not specified for "+this.type;
	}

	// Mandatory implementation method for locating JSTL template for views
	this.locateViewTemplates = function()
	{
		throw "Views and corresponding templates not specified for "+this.type;
	}

	this.resetControllerState = function(){}

	this.start = function(){}

	this.init = function()
	{
		this.views={};
		this.preinit();
		this.registerKeys();
		var template = getTemplate(this.locateLayoutTemplate());
		if(template)
		{
			this.portlet.html(template.process(this));
			var tds = this.portlet.find("td");
			var vtPath = this.locateViewTemplates();
			for(var i = 0; i < tds.length; i++)
			{
				var td = tds[i];
				if(vtPath[td.className])
				{
                    var templateInfo=vtPath[td.className];
                    var viewTemplate=null;
                    var renderer="html";
                    if(templateInfo instanceof Array)
                    {
                        viewTemplate = getTemplate(templateInfo[0]);
                        renderer=templateInfo[1]?templateInfo[1]:'html';
                    }
                    else{
                        viewTemplate = getTemplate(templateInfo);
                    }
                    td.templateInfo = templateInfo;
					td.template = viewTemplate;
					td.controller = this;
                    td.renderer=renderer;
					td.render = function()
					{
                        if(this.renderer=='svg'){
                            svgContainer=vtPath[this.className][2];
                            if(svgContainer)
                            {
                                svgObj=$(svgContainer).svg();
                                if(svgObj.length>0)
                                {
                                    svgGraph = this.template.process(this.controller);
                                    var svgObj = $(svgContainer).svg('get');
                                    svgObj.configure({'xmlns':"http://www.w3.org/2000/svg",'xmlns:xlink':"http://www.w3.org/1999/xlink",'width':'1000','height':'100%'},false);
                                    svgObj.clear();
                                    svgObj.add($(svgGraph));
                                }
                            }
                            else{
                                throw "SVG Layout not defined as part of view template definition for SVG view "+this.className[0];
                            }
                        }else{
							try{
				            	this.innerHTML = this.template.process(this.controller);
			                }catch(err){
			                	jAlert('Error while processing '+this.templateInfo, 'Error', null, err.name+': '+err.message);
			                }
						}
					}
                    if(td.template  != null){
                        td.render();
                        this.views[td.className] = td;
                    }else{
                        throw "view template for the "+templateInfo+" jstl is undefined";
                    }
				}
			}
			this.setTitle();
			this.callback();
		}
		else
		{
			this.portlet.error("Template "+this.locateLayoutTemplate()+" not found")
		}

		//$.manageAjax.create('reportQueue', { queue: 'clear' });
	}

	this.registerKeys=function()
	{
		registerKeys(this);
	}

	this.render = function()
	{
		for(var name in this.views)
		{
			this.views[name].render();
		}
		this.callback()
	}

	this.resize=function()
	{
	}

	this.callback=function()
	{
	}

	this.preinit = function()
	{
	}

	this.getView = function(name)
	{
		return this.views[name];
	}

	this.get = function(id)
	{
		var sheet = this.graph[id];
		if(!sheet){
			if(primarySession){
				primarySession.loadSheet(id)
				sheet = this.graph[id];
			}
			if(!sheet){
				this.info('data not found for the id '+id)
			}
		}
		if(!sheet.controller)
		{
			sheet.controller = this;
			sheet.getValue = function(name)
			{
				var value = this[name];
				if(value != null){
					return value
				}
				return null
			};

			sheet.getList = function(name)
			{
				var list = new Array();
				for (var i in this.getValue(name))
					list.push(this.controller.get(i));
				return list;
			};

			sheet.get = function(name)
			{
				for (var i in this.getValue(name))
					return this.controller.get(i);
				return null;
			};

			sheet.getDisplayValue = function(name)
			{
				var value = this[name];
				if(value != null){
					return value
				}
				return null
			};
		}
		return sheet;
	};

	this.getRoot = function()
	{
		return this.get(this.graph.root);
	};

	this.debug = function(str){this.portlet.debug(str);}

	this.info = function(str){this.portlet.info(str);}

	this.warn = function(str){this.portlet.warn(str);}

	this.error = function(str){this.portlet.error(str);}

	this.fnUpdateGraph=function(data)
	{
		this.graph = data;
		this.render();
	}
	/*
	 *********options*****************
	 * methodType = GET/PUT/POST
	 * metaType = metadatatype
	 * sheetMetadataId = parentsheetmetadataid
	 * taskId = taskid
	 * list = true/false
	 * next = key/value object for the next hierarchy
	 * data = key/value object which will be constructed in to a query string.
	 *
	 */
	this.communicate =  function(options, message, isSubmit)
	{
		var controller=this;
        var statusMsg=null;
        var hasError=false;
		var async=(options.async==false)?false:true;
		//temp workaround for Google appengine problem,need to look into it very important
		if(options.methodType == "PUT"){
			options.methodType = "GET"
			options['query']["__method"] = "PUT"
		}
		var ajaxUrl = this.constructUrl(options)
		$.manageAjax.add('requestQueue',{type:options.methodType, url:ajaxUrl, dataType: "json", async:async,data: options['data'],
		beforeSend:function()
		{
			if(isSubmit)
            {
                $.blockUI({ message: "Submitting the record. Please wait" });
            }
            else
            {
                $.blockUI({
                    message: 'Processing...',
                    fadeIn: 700,
                    fadeOut: 700,
                    showOverlay: false,
                    centerY: false,
                    css: {
                        width: '350px',
                        top: '10px',
                        left: '',
                        right: '10px',
                        border: 'none',
                        padding: '5px',
                        backgroundColor: '#000',
                        '-webkit-border-radius': '10px',
                        '-moz-border-radius': '10px',
                        opacity: .6,
                        color: '#fff'
                    }
                });
             }
		},
		success: function(data){
			if(!isSubmit || controller.meta.typeName == 'Inbox')
            	controller.fnUpdateGraph(data);
            else
            	controller.graph = data;
		},
		error: function(XMLHttpRequest, textStatus, errorThrown)
		{
		   hasError = true

		   exceptionMsg = XMLHttpRequest.getResponseHeader('ErrorMessage')
		   exceptionTrace = XMLHttpRequest.responseText
           if(exceptionMsg){
               jAlert('Error '+exceptionMsg,'Error',null,exceptionTrace);
           }
           else if(XMLHttpRequest && XMLHttpRequest.status==401){
                alert("User session timed out. Please login again to continue...");
           }else{
	           	exceptionMsg = "Server returned an error"
                jAlert('Error '+exceptionMsg,'Error',null,exceptionTrace);
           }
		},
		complete:function(XMLHttpRequest,textStatus){
            if(!hasError)
            {
                if(message){
                        statusMsg= controller.parseJSTLText(message);
						$.unblockUI();
                        jAlert(statusMsg,'Success',function(){
                                                    xmlReq=XMLHttpRequest
                                                    pageUrlPath = xmlReq.getResponseHeader("Location")
                                                    if(pageUrlPath)
                                                        location.href=controller.parseJSTLText(pageUrlPath);
                                                    },null);
                }
                else
                {
                	if(XMLHttpRequest.getResponseHeader){
	                    pageUrlPath = XMLHttpRequest.getResponseHeader("Location")
	                    if(pageUrlPath){
	                        location.href=controller.parseJSTLText(pageUrlPath);
	                    }
                    }
                }
            }
            //for inbox we need to unblock even if it submit,need to revisit
         	if(!isSubmit || controller.meta.typeName == 'Inbox')
            	$.unblockUI();
         	if(hasError && isSubmit)
                 $.unblockUI();
		}
	});
}

	this.parseJSTLText = function(message)
	{
		try
		{
	       	var template=TrimPath.parseTemplate(message);
	        parsedText = template.process({instance: this.getRoot()});
        }
        catch(err){
			//need to complete exception handling
        }
        finally{
        	return parsedText
        }
	}

	this.constructUrl = function(options)
	{
		var url = ""
		if (!options.url)
		{
			var list = options.list ? "/list" : "";
			var urlList = new Array()
			urlList.push("", options.metaType, options.version, options.sheetMetadataId, options.taskId)
			if (options['next'])
				urlList = this.appendUrlPart(urlList, options['next'])
			url = urlList.join("/")
		}
		else url=options.url;
		var queryString = options['query']
		if(this.editorGridViewId)
		{
			if (queryString)
				queryString['editorGridViewId'] = this.editorGridViewId
			else
				queryString = {'editorGridViewId':this.editorGridViewId}
		}
		var urlWithQs = url
		if(urlWithQs.lastIndexOf("/")==urlWithQs.length-1)
			urlWithQs=urlWithQs.substring(0,urlWithQs.length-1);
		if (queryString){
			urlWithQs += "?"
			urlWithQs += this.addQueryString(queryString);
		}
		//alert(urlWithQs)
		return urlWithQs
	}

	this.appendUrlPart = function(urlList,next)
	{
		if(next['metadataId'])
		{
			urlList.push(next['metadataId'])
			if(next['instanceId'])
			{
				urlList.push(next['instanceId'])
				if(next['referenceId'])
				{
					urlList.push(next['referenceId'])
					if(next['next'])
						urlList = this.appendUrlPart(urlList,next['next'])
				}
			}
		}
		return urlList
	}

	this.addQueryString = function(query)
	{
		querystring = "";
		if (query) {
			for (name in query) {
				if (querystring.length != 0)
				{
					querystring += '&';
				}
				querystring += (encodeURIComponent(name) + '=' + encodeURIComponent(query[name]));
			}
		}
		return querystring
	}

	 /*
	 * Queued Ajax requests.
	 * A new Ajax request won't be started until the previous queued
	 * request has finished.
	 */
	var currentAjaxRequest = null;
	jQuery.ajaxQueue = function(ajaxOptions)
	{
	    var success = ajaxOptions.success;
	    var error = ajaxOptions.error;
	    var complete = ajaxOptions.complete;

	    ajaxOptions.success = function()
	    {
	        if (success) { try { success.apply(this, arguments); } catch (e) { } }
	    };
	    ajaxOptions.error = function()
	    {
	        if (error) { try { error.apply(this, arguments); } catch (e) { } }
	    };
	    ajaxOptions.complete = function()
	    {
	        if (complete) { try { complete.apply(this, arguments); } catch (e) { } }
	        currentAjaxRequest = null;
	        $(window).dequeue("ajax");
	    };
	    $(window).queue("ajax", function()
	    {
	        currentAjaxRequest = jQuery.ajax(ajaxOptions);
	    });
	    if (currentAjaxRequest === null) { $(window).dequeue("ajax"); }
	};

	this.getEncodedValue = function(value)
	{
		if(value && typeof(value) == "string"){
			value = value.replace(/'/g, "&apos;")
			value = value.replace(/"/g, "&quot;")
			return value
		}
        else
            return value;
	}

}



