/*
 * $Id: StateHandling.js,v 1.63.2.3 2006/01/04 10:43:11 shanmugampl Exp $
 */

// -----------------------  Short Name Declarations ---------------------------
/**
 * Applications should not use the letters 'p', 'w' & 'd' as variable names as
 * they are used by the framework.
 */

var p = parent;
var w = window;
var d = document;

// -------------------  Browser Determination Code ----------------------------

var browser_opera = false;
var browser_ie = false;
var browser_nn6 = false;
var browser_nn4 = false;

if (d.all)
{
  if (w.navigator.appName.toUpperCase=="OPERA")
	browser_opera=true;
  else
	browser_ie=true;
}
else if (d.layers || (!d.all && d.getElementById))
  browser_nn6=true;
else if (d.layers)
  browser_nn4=true; 


// ------------ Cookie Related Functions ---------------------

function getCookie (name) 
{
  var arg = name + "=";  
  var alen = arg.length;  
  var clen = d.cookie.length;  
  var i = 0;  
  while (i < clen) {    
    var j = i + alen;    
    if (d.cookie.substring(i, j) == arg)      
      return getCookieVal (j);    
    i = d.cookie.indexOf(" ", i) + 1;    
    if (i == 0) break;   
  }  
  return null;
}

function getCookieVal(offset) 
{
  var endstr = d.cookie.indexOf (";", offset);
  if (endstr == -1)
    endstr = d.cookie.length;
  return unescape(d.cookie.substring(offset, endstr));
}

function setCookie (name, value) 
{  
  var argv = setCookie.arguments;  
  var argc = setCookie.arguments.length;  
  var expires = (argc > 2) ? argv[2] : null;  
  var path = (argc > 3) ? argv[3] : null;  
  var domain = (argc > 4) ? argv[4] : null;  
  var secure = (argc > 5) ? argv[5] : false;  
  d.cookie = name + "=" + escape (value) + 
    ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + 
    ((path == null) ? "" : ("; path=" + path)) +  
    ((domain == null) ? "" : ("; domain=" + domain)) +    
    ((secure == true) ? "; secure" : "");
}

function deleteCookie (name) 
{  
  var exp = new Date();  
  exp.setTime (exp.getTime() - 1);  
  var cval = getCookie(name);  
  d.cookie = name + "=" + cval + "; expires=" + exp.toGMTString();
}

//------------------------------- Cookie related functions end ---------------------

/**
 * Returns the uniqueId for the passed variable. The variable can sometimes be the
 * required uniqueId itself or the referenceId. 
 * Incase passed variable is null, null is returned back.
 * Note: There is an issue if the uniqueId itself is a number.
 */
function getUniqueId(variable){
       if(variable == null)
       {
          return null;
       }

	if(isNaN(variable)){
		return variable;
	}
	return referenceIds[variable]; 
}

/**
 * Returns the referenceId for the passed variable. The variable can sometimes be the
 * required referenceId itself or the uniqueId. 
 *
 * Note: There is an issue if the uniqueId itself is a number.
 */
function getReferenceId(variable){
	if(isNaN(variable)){
           if(stateData[variable] == null)
           {
                return null;
           }
           return stateData[variable]["ID"]; 
	}
	return variable;
}

/**
 * Initializes the main view by creating a stateData and a referenceIds object.
 * The stateData contains state for each subview and the referenceIds contains
 * each views referenceId. The values for a view can be retrieved at any point
 * of time by using the getUniqueId(id) and the getReferenceId(id) methods.
 */
function initializeMainView(win,rootViewId,context,themedir)
{
  win.postInvokeScripts = new Array();      
  if((parent.stateData) && (parent != win))
  {
    win.stateData = parent.stateData;   
    win.referenceIds = parent.referenceIds;        
    win.SUBREQUEST=true;  
  }
  else if(!win.stateData)
  {
    win.stateData = new Object();
    win.referenceIds = new Object();
    win.oldViews = new Object();
  }
  if(!win.ROOT_VIEW_ID)
  {
    win.ROOT_VIEW_ID= rootViewId;
    win.CONTEXT_PATH=context;
    win.THEME_DIR=themedir;   
  }
}

function createView(win,uniqueId,viewName,requestParams,parentView,parentDCA,refId,viewGenTime)
{
  var newView = new Object();
  newView["_VN"] = viewName;
  newView.ID = refId;
  if(requestParams != null)
  {
      newView["_D_RP"] = requestParams;
  }
  newView.PDCA = parentDCA;
  if((win.SUBREQUEST) && (uniqueId == win.ROOT_VIEW_ID)
    && stateData[uniqueId])
  {
	 newView["_PV"] = stateData[uniqueId]["_PV"];
  }
  else
  {
      newView["_PV"] = getReferenceId(parentView);
  }
  stateData[uniqueId] = newView;
  referenceIds[refId] = uniqueId;
  newView["VGT"]=viewGenTime;
  return newView;
}
 
function updateExistingView(uniqueId,parentViewId,parentDCA)
{
   stateData[uniqueId]["_PV"]= parentViewId;
   stateData[uniqueId]["PDCA"] = parentDCA;
}


function updateState(id,stateName,stateValue,updateVMDFlag)
{
  var uniqueId = getUniqueId(id);
  if(stateValue)
  {
    encodeAndUpdateState(stateData[uniqueId],stateName,stateValue);
  }
  else
  {
    stateData[uniqueId][stateName]= null;
  }

  if(updateVMDFlag)
  {
    stateData[uniqueId]["_VMD"]= '1';
  }
}


function getState(id,stateName)
{
  var uniqueId = getUniqueId(id);
  return stateData[uniqueId][stateName];
}


function encodeAndUpdateState(stateObj,stateName,stateVal)
{
  var sTypeOf = typeof stateVal;
  if(sTypeOf == "string")
  { 
     stateObj[stateName] = stateVal;
     return;
  } 
  if(sTypeOf != "object")
  {
    alert("encodeAndUpdateState: " + sTypeOf + " Data Type cannot be stored as state");
      
  }
  var fConstructor = stateVal.constructor;
  if( fConstructor == Array)
  {
     var stateValArr = "";
     for( var i in stateVal)
     {
        stateValArr += stateVal[i] + ",";
     }
     stateObj[stateName + "_COLL_"] = stateValArr; 
  }
  else 
  {
     var stateValMap = "";
     for( var i in stateVal)
     {
        stateValMap += i + "," + stateVal[i] + ",";
     }
     stateObj[stateName + "_MAP_"] = stateValMap;      
  }
}

function handleStateForForm(frm,id,rootViewId,isSubReq,target)
{
  if(frm.action.indexOf("STATE_ID") > -1)
  {
    //TODO: User must have double clicked!!! But there is also another
    //problem where an form might have been be  reused, i.e re submitted!!!
    return;
  }
  var uniqueId = getUniqueId(id);

  if(isSubReq)
  {
    if(target == null)
    {
      target = parent.ROOT_VIEW_ID + "_RESPONSEFRAME"
    }  
     addHiddenInput(frm,"SUBREQUEST","true");
  }
  if(target != null)
  {
    setAttrOnFrm(frm,"target",target);
  }
  url = updateStateCookieAndAppendSid(frm.action,rootViewId);
  if(uniqueId != null)
  {
    url =  getURLSuffixed(url);
    url += "ACTION_SOURCE=" + uniqueId;
  }
  frm.action=url;
  return true;
}

function updateStateCookieAndAppendSid(url,rootViewId)
{
  var type = getURLType(url);
  if(type ==  "OUT")
  {// An outside url. State need not be passed.
    return url;
  }

  var stateTime = new Date().getTime(); 
  var stateStr = "STATE_ID/" + stateTime;
  var newUrl = null;

  if(type == "ABS")
  {
    if(url.indexOf("STATE_ID") > -1)
    {
       newUrl = url.replace(/STATE_ID\/[^\/]*/g,stateStr);
    }
    else
    {
       var prefix = getCurProtoAndHostPrefix() + CONTEXT_PATH;        
       newUrl = prefix + "/" + stateStr + url.substring(prefix.length);
    }
  }
  else if(type == "HREL")
  {
     newUrl = CONTEXT_PATH + "/" + stateStr + url.substring(CONTEXT_PATH.length);
  }
  else if(type == "REL")
  {
    var newPath  = window.location.pathname;
    newPath = newPath.substring(0,newPath.lastIndexOf('/'));
    if(newPath.indexOf("STATE_ID/") > -1)
    {
      newPath =  newPath.replace(/STATE_ID\/[^\/]*/g,stateStr) + "/" + url;        
    }      
    else
    {
      var ctxIndex = newPath.indexOf(CONTEXT_PATH)+ CONTEXT_PATH.length +1;
      newPath = newPath.substring(0,ctxIndex) + "/" + stateStr + "/" + url;
    }
    newUrl = getCurProtoAndHostPrefix() + newPath;
  }
  else
  { //Not Possible
    return url;
  }
  updateStateCookie(CONTEXT_PATH + "/"  + stateStr,stateTime,rootViewId);
  return newUrl;
}


function getURLType(url)
{
   if(url.charAt(0) == '/')
   {
      if(url.indexOf(CONTEXT_PATH) == 0)
      {
         //Host relative url!!
         return "HREL";
      }
      else
      {//Different context!!
         return "OUT";
      }
   }

   if(url.indexOf(":") == -1)
   {//Relative URL
      return "REL"
   }  
   
   if(url.indexOf("?") > -1)
   {
      if(url.indexOf(":") > url.indexOf("?"))
      { // ':' is after '?'. So it cannot be a absolute url.
        return "REL";
      }
   }

   var prefix = getCurProtoAndHostPrefix();
   prefix += CONTEXT_PATH;

   if(url.indexOf(prefix) == 0)
   { //Absolute URL , but of same host and context.
     return "ABS";
   }         
   else
   { // An outside url. State need not be passed.
     return "OUT";
   }
}

function getCurProtoAndHostPrefix()
{
  var curLocation = window.location; 
  var curHost = curLocation.host;
  if((curLocation.port != null) && (curLocation.port != "") && (curHost.indexOf(":" + curLocation.port) < 1))
  {
    curHost += ":" + curLocation.port;
  } 
  return curLocation.protocol + "//" + curHost;
}

function updateStateCookie(path,stateTime,rootViewId)
{
	var queryStr = "";
	if(rootViewId != null)
	{//TODO contentarea fix- To support partial refresh.
		stateData[rootViewId]["ORIGROOT"] = getReferenceId(ROOT_VIEW_ID);
	}
        checkForCacheSize();

	for(var name in stateData)
	{
		queryStr += "&" + name;
		for( var i in stateData[name])
		{
			var val = stateData[name][i];
			if(val && ((i != "_VN") || (val != name)))
			{ 
				if(i == "_D_RP"){
					val = getEscapedQueryString(val);
				}

				val = encodeURIComponent(val);
				val = val.replace(/\//g,"//");
				queryStr += "/"  + i + "/" + val;
			}
		}
	}
	if(!rootViewId)
	{
		rootViewId = ROOT_VIEW_ID;
	}

	queryStr += "&_REQS/_RVID/" + rootViewId;
	queryStr +="/_TIME/"+ stateTime;
        var len =0;
	for(var name in stateData)
	{
          if(stateData[name]["_D_RP"])
          {
            len += stateData[name]["_D_RP"].length;
          }
        }
        //alert("Length of Req " + len);
        setCookieBasedOnSize(queryStr,path);
}


function setCookieBasedOnSize(stateStr,path)
{
	if(stateStr.length > 3000){
		var number = Math.floor(stateStr.length / 3000);
		var startIdx = 0;
		var endIdx = 0;
		for(var count = 0; count <= number; count++){
			if(count == 0){
				startIdx = 0;
			}
			else{
				startIdx = count*3000 + 1;
			}
			if(count == number){
				endIdx = stateStr.length;
			}
			else {
				endIdx = (count+1)*3000 + 1;
			}
			var str = stateStr.substring(startIdx, endIdx);
			setCookie("STATE_COOKIE" + count,str,null,path);
		}
	}
	else {
	setCookie("STATE_COOKIE",stateStr,null,path);
	}

}

function removeStateForView(viewToRemove)
{
  var toProcessList = new Array();
  toProcessList.push(viewToRemove);
  var removedViewsNum = 0;
  while(toProcessList.length > 0)
  {
     var curUniqueId = toProcessList.pop();
     var curRefId = getReferenceId(curUniqueId);
     internalRemoveStateForView(curUniqueId); 
     //alert("Removing " + curUniqueId);
     removedViewsNum++;
     for(var name in stateData)
     {
        if(stateData[name] && (stateData[name]["_PV"] == curRefId))
        {
           toProcessList.push(name);
        } 
     } 
  }  
  defragState();
  return removedViewsNum;
}

function defragState()
{
  stateData = defragObject(stateData);
  oldViews = defragObject(oldViews);
  referenceIds = defragObject(referenceIds);
}

function internalRemoveStateForView(viewToRemove)
{
   stateData[viewToRemove] = null;
   oldViews[viewToRemove] = null;
   referenceIds[viewToRemove]= null;      
}

function defragObject(sparseObj)
{
  var newObj = new Object();
  for(var name in sparseObj)
  {
    if(sparseObj[name])
    {
      newObj[name] = sparseObj[name];
    }
  }
  return newObj;
}

function checkForCacheSize()
{
  var viewsToRemove = new Array();
  var cursor = 0;
  for(var name in stateData)
  {
    viewsToRemove[cursor++] = name;
  }
  var size = viewsToRemove.length;
  if(size < 50)
  {
     return;
  }

  var toRemove = removeCachedViewsState(viewsToRemove,true,size - 30);
  if(toRemove > 0)
  {
    removeCachedViewsState(viewsToRemove,false,toRemove);
  }
  //alert(getObjectSize(stateData));
}

function removeCachedViewsState(viewsToRemove,checkReqParam,toRemove)
{
  for(var i = 0; i < viewsToRemove.length; i++)
  {
    var name = viewsToRemove[i];
    if(!stateData[name])
    {
      continue;
    }
    if(!checkReqParam || (stateData[name]["_D_RP"] != null))
     {
        if(!isViewStateNeeded(name))
        {
           toRemove = toRemove - removeStateForView(name);
           //alert("To Remove is now " + toRemove);
           if(toRemove < 1)
           {
             break;
           }
        }
     }
  } 
  return toRemove;
}

function getObjectSize(obj)
{
  var i = 0;
  for(var name in obj)
  {
    i++;
  }
  return i;
}

/**
 * Escapes the values like " " and + and replaces them with the corresponding
 * hex values. This can be used to escape data that are to be passed as request
 * parameter.
 */
function getEscapedQueryString(str){
        var charCode,modValue,diff = null;
        var hexStr = "";
        for (var i=0; i < str.length; i++) {
                var chr = str.charAt(i);
                if(chr == " " || chr == "+"){
                        charCode = str.charCodeAt(i);
                        modValue = charCode % 16;
                        diff = (charCode - modValue)/16;
                        hexStr += '%' + "0123456789ABCDEF".charAt(diff) + "0123456789ABCDEF".charAt(modValue);
                }
                else{
                        hexStr = hexStr + chr;
                }
        }
        return hexStr;
}


