////////////////////
// Copyright (c) 2009, Perceptive Automation, LLC. All rights reserved.
// http://www.perceptiveautomation.com
//
// Redistribution of this source file, its binary forms, and images are not allowed.
// We are working on our open source license terms, but at this point the source files,
// its binary forms, and all images cannot be redistributed without written permission
// from both Perceptive Automation, LLC. and Andy Turner, HighEarthOrbit software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
////////////////////

// -------------------------------------------------------------------------------
// For our mod to handle auto-throttling the updates
var gCurUpdateInterval = null;
var gActiveTimer = null;

// -------------------------------------------------------------------------------
// Pulled from ricoAjaxEngine.js (from Rico 2.0)
PA_AjaxEngine = Class.create();

PA_AjaxEngine.prototype = {

   initialize: function() {
      this.ajaxElements = new Array();
      this.ajaxObjects  = new Array();
      this.requestURLS  = new Array();
      this.options = {};
   },

	_getContentAsString: function(parentNode) {
	  if (typeof parentNode.xml != 'undefined') return this._getContentAsStringIE(parentNode);
	  return this._getContentAsStringMozilla(parentNode);
	},

	_getContentAsStringIE: function(parentNode) {
	  var contentStr = "";
	  for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
		 var n = parentNode.childNodes[i];
		 contentStr += (n.nodeType == 4) ? n.nodeValue : n.xml;
	  }
	  return contentStr;
	},

	_getContentAsStringMozilla: function(parentNode) {
	   var xmlSerializer = new XMLSerializer();
	   var contentStr = "";
	   for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
			var n = parentNode.childNodes[i];
			if (n.nodeType == 4) { // CDATA node
				contentStr += n.nodeValue;
			}
			else {
			  contentStr += xmlSerializer.serializeToString(n);
		  }
	   }
	   return contentStr;
	},

   registerAjaxElement: function( anId, anElement ) {
      if ( !anElement )
         anElement = $(anId);
      this.ajaxElements[anId] = anElement;
   },

   registerAjaxObject: function( anId, anObject ) {
      this.ajaxObjects[anId] = anObject;
   },

   registerRequest: function (requestLogicalName, requestURL) {
      this.requestURLS[requestLogicalName] = requestURL;
   },

   sendRequest: function(requestName, options) {
      // Allow for backwards Compatibility
      if ( arguments.length >= 2 )
       if (typeof arguments[1] == 'string')
         options = {parameters: this._createQueryString(arguments, 1)};
      this.sendRequestWithData(requestName, null, options);
   },

   sendRequestWithData: function(requestName, xmlDocument, options) {
      var requestURL = this.requestURLS[requestName];
      if ( requestURL == null )
         return;

      // Allow for backwards Compatibility
      if ( arguments.length >= 3 )
        if (typeof arguments[2] == 'string')
          options.parameters = this._createQueryString(arguments, 2);

      new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument));
   },

   // Private -- not part of intended engine API --------------------------------------------------------------------

   _requestOptions: function(options,xmlDoc) {
      var requestHeaders = ['X-Indigo-Web-Server-Version', 1.0 ];
      var sendMethod = 'post';
      if ( xmlDoc == null )
        if (0/*Rico.prototypeVersion < 1.4*/)
        requestHeaders.push( 'Content-type', 'text/xml' );
      else
          sendMethod = 'get';
      (!options) ? options = {} : '';

      if (!options._RicoOptionsProcessed){
      // Check and keep any user onComplete functions
        if (options.onComplete)
             options.onRicoComplete = options.onComplete;
        options.onComplete = this._onRequestComplete.bind(this);
        options._RicoOptionsProcessed = true;
      }

     // Set the default options and extend with any user options
     this.options = {
                     requestHeaders: requestHeaders,
                     parameters:     options.parameters,
                     postBody:       xmlDoc,
                     method:         sendMethod,
                     onComplete:     options.onComplete
                    };
     // Set any user options:
     Object.extend(this.options, options);
     return this.options;
   },

   _createQueryString: function( theArgs, offset ) {
      var queryString = ""
      for ( var i = offset ; i < theArgs.length ; i++ ) {
          if ( i != offset )
            queryString += "&";

          var anArg = theArgs[i];

          if ( anArg.name != undefined && anArg.value != undefined ) {
            queryString += anArg.name +  "=" + escape(anArg.value);
          }
          else {
             var ePos  = anArg.indexOf('=');
			 // -----------------------------------------------
			 // <<==-- START PERCEPTIVE AUTOMATION CHANGE
			 // The server writes out URL arguments (like device or page names)
			 // as URL encoded into the HTML, because JavaScript's escape() function
			 // isn't UTF-8 smart. By having the server handle the encoding we are
			 // guaranteed the URL passed back to the server contains the encoding
			 // exactly as it expects (round tripping will work). We must avoid
			 // using JS escape() in this case or it will be double encoded. (mmb)
			 if (anArg.substring(ePos+1, ePos+11) == "<NOESCAPE>") {
			 	var argName  = anArg.substring( 0, ePos );
			 	var argValue = anArg.substring( ePos + 11 );
			 	queryString += argName + "=" + argValue;
			 	//alert("not escaping URL arg val: " + argValue);
			 	continue;
			 }
			 // END PERCEPTIVE AUTOMATION CHANGE --==>>
             var argName  = anArg.substring( 0, ePos );
             var argValue = anArg.substring( ePos + 1 );
             queryString += argName + "=" + escape(argValue);
          }
      }
      return queryString;
   },

   _onRequestComplete : function(request) {
      if(!request)
          return;
      // User can set an onFailure option - which will be called by prototype
      if (request.status != 200)
        return;

      var response = request.responseXML.getElementsByTagName("ajax-response");
      if (response == null || response.length != 1)
         return;
      this._processAjaxResponse( response[0].childNodes );
      
      // Check if user has set a onComplete function
      var onRicoComplete = this.options.onRicoComplete;
      if (onRicoComplete != null)
          onRicoComplete();
   },

   _processAjaxResponse: function( xmlResponseElements ) {
      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
         var responseElement = xmlResponseElements[i];

         // only process nodes of type element.....
         if ( responseElement.nodeType != 1 )
            continue;

         var responseType = responseElement.getAttribute("type");
         var responseId   = responseElement.getAttribute("id");

         if ( responseType == "element" )
            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
         // -----------------------------------------------
         // <<==-- START PERCEPTIVE AUTOMATION CHANGE
         else if ( responseType == "reregister" ) {
            if (responseId) {
				this.registerAjaxElement(responseId); 
				//alert('re-registered id ' + responseId + ' elem');
            }
            else {
				var className = responseElement.getAttribute("class");
				var elements = document.getElementsByClassName(className);
				for (var i2 = 0; i2 < elements.length; i2++) {
				   this.registerAjaxElement(elements[i2].id); 
				}
				//alert('re-registered all ' + className + ' elems');
            }
         }
         else if ( responseType == "dialogopen" ) {
            var dialogid = responseElement.getAttribute("id");
			showModalDialog(dialogid);
         }
         else if ( responseType == "dialogclose" ) {
            var dialogid = responseElement.getAttribute("id");
			closeModalDialog(dialogid);
		 }
         else if ( responseType == "setpollrate" ) {
            var interval = responseElement.getAttribute("interval");
            // evil reference to externally defined gCurUpdateInterval and updateControls() below
			if (interval != gCurUpdateInterval) {
				gCurUpdateInterval = interval;
				if (gActiveTimer) {
					clearTimeout(gActiveTimer);
					gActiveTimer = setTimeout("timerComplete()", gCurUpdateInterval);
		            //alert('changed timeout interval');
				}
	            //alert('new update timeout interval = ' + gCurUpdateInterval);
			}
         }
         // END PERCEPTIVE AUTOMATION CHANGE --==>>
         // -----------------------------------------------
         else
            alert('unrecognized AjaxResponse type : ' + responseType );
      }
   },

   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
	 // -----------------------------------------------
	 // <<==-- START PERCEPTIVE AUTOMATION CHANGE
	 // ajaxElement can legally be NULL. Example: server tells the client to swap in a
	 // new server status (serverStatus), but user does not have a server status control
	 // defined for that control page.
      if (ajaxElement) {
	 // END PERCEPTIVE AUTOMATION CHANGE --==>>
	 // -----------------------------------------------
	      ajaxElement.innerHTML = this._getContentAsString(responseElement);
	   }
   }

}
var ajaxEngine = new PA_AjaxEngine();

// -------------------------------------------------------------------------------
var gModalErrorDialog = null;
var gModalErrorDialogActive = false;

// ---------------------------------------
// Rico ajax 
// Register all of the page elements as ajax elements
function controlPageOnload(updateInterval)
{
	ajaxEngine.registerAjaxElement("serverStateVar"); 
	ajaxEngine.registerAjaxElement("pageRevisionVar");  

	ajaxEngine.registerAjaxElement("serverStatusText");
	ajaxEngine.registerAjaxElement("serverStatusGraphic"); 
	ajaxEngine.registerAjaxElement("modalErrorText"); 

	ajaxEngine.registerAjaxElement("pageElementList_section"); 
	registerAllPageElements();

	ajaxEngine.registerRequest('updateControls','_getupdatedcontrols');
	ajaxEngine.registerRequest('controlElement','_docontrol');
	ajaxEngine.registerRequest('controlDevice','_docontroldevice');
	
	gCurUpdateInterval = updateInterval
	gActiveTimer = setTimeout("timerComplete()", gCurUpdateInterval);

	// I don't like the way this flickers... maybe we should have it re-size from
	// the anchor when we open the window after all...
	// resizeWinToDiv('controlPage');
}

// Registers all pageElements as ajax-updatable
function registerAllPageElements()
{
	var elements = document.getElementsByClassName('pageElement');
	for (var i = 0; i < elements.length; i++) {
		ajaxEngine.registerAjaxElement(elements[i].id); 
	}
}

// ---------------------------------------
function showModalDialog(dialogid)
{
	if (dialogid == 'modalErrorDialog') {
		if (!gModalErrorDialog)		// important: only create first time
			gModalErrorDialog = new DialogBox('modalErrorDialog');
		gModalErrorDialog.Open();
    	gModalErrorDialogActive = true;
	}
	return false;
}

function closeModalDialog(dialogid)
{
	if (dialogid == 'modalErrorDialog' && gModalErrorDialogActive) {
		gModalErrorDialog.Close();
		gModalErrorDialogActive = false;
	}
	return false;
}

// ---------------------------------------
// Periodic (setTimeout based) updating of images that we know will be changing
// at regular intervals (video streams)
function updateImagePoller(imageSrcUrl, imageSrcName, refreshInterval)
{
	var cacheKiller = new Date();
	cacheKiller = "&nocache=" + cacheKiller.getTime();
	document.images[imageSrcName].src = imageSrcUrl + cacheKiller;
	setTimeout(function(){updateImagePoller(imageSrcUrl,imageSrcName,refreshInterval);}, refreshInterval * 1000);
}

// ---------------------------------------
// Ajax-update any controls that changes from the current revision
function updateControls()
{
	var pageNameURL = $('controlPageNameURLVar').innerHTML;
	var asList = $('asListVar').innerHTML;
	var folderFilter = $('folderFilterVar').innerHTML;
	var revision = $('pageRevisionVar').innerHTML;
	var serverState = $('serverStateVar').innerHTML;
	// Unbelievably, MSIE 6 will use its cache for 2 seconds regardless
	// of the fact that the HTTP header told it not to cache at all.
	// Adding a unique var to the URL will prevent this.
	ajaxEngine.sendRequest(
		  'updateControls'
		, "name=<NOESCAPE>" + pageNameURL
		, "asList=" + asList
		, "folderFilter=" + folderFilter
		, "changeCount=" + revision
		, "serverState=" + serverState
	);
}

function timerComplete()
{
	gActiveTimer = null;	// timer is done; clear our reference to it
	updateControls();		// and request a server update
}

// ---------------------------------------
// Send a 'do control' to a single element
function controlElement(elementIndex)
{
	var pageNameURL = $('controlPageNameURLVar').innerHTML;
	ajaxEngine.sendRequest(
		  'controlElement' 
		, "page=<NOESCAPE>" + pageNameURL 
		, "id=" + elementIndex
	);
}

function commandDevice(deviceName, deviceCommand, commandParameter)
{
	//alert(deviceName + ": " + deviceCommand + " - " + commandParameter);
	if (deviceName != '') {
		ajaxEngine.sendRequest(
			  'controlDevice' 
			, "device=<NOESCAPE>" + deviceName 
			, "command=" + deviceCommand
			, "value=" + commandParameter
		);
	}
}

// ---------------------------------------
function restUpdateRequest(resourceUrl, parms)
{
	if (parms && typeof(parms) != "string")
		alert("restUpdateRequest currently only supports string parms arg");
	if (!parms)
		parms = ""
	if (parms.indexOf('_blockUntilIdle') === -1) {
		// If caller didn't specify _blockUntilIdle, then use default of not blocking.
		if (parms.length > 0)
			parms += "&"
		parms += "_blockUntilIdle=0"
	}
	//alert("restExecuteRequest parms: " + parms);
	new Ajax.Request(resourceUrl, {
			method:'put',
			parameters:parms,
			onComplete:updateControls
		}
    );
}

function restExecuteRequest(resourceUrl, parms)
{
	if (parms && typeof(parms) != "string")
		alert("restUpdateRequest currently only supports string parms arg");
	if (!parms)
		parms = ""
	if (parms.indexOf('_blockUntilIdle') === -1) {
		// If caller didn't specify _blockUntilIdle, then use default of not blocking.
		if (parms.length > 0)
			parms += "&"
		parms += "_blockUntilIdle=0"
	}
	//alert("restExecuteRequest parms: " + parms);
	new Ajax.Request(resourceUrl, {
			method:'execute',
			parameters:parms,
			onComplete:updateControls
		}
    );
}

// ---------------------------------------
function popupControlPage(rootPath, controlPath, elemName, width, height)
{
	var url = rootPath + "/" + controlPath + "?name=" + elemName + "&useJS=True";
	var args = "width=" + width + ",height=" + height;
	args = args + ",resizable=yes,scrollbars=no,toolbar=no,location=no,directories=no,status=no,menubar=no,copyhistory=no";
	window.open(url, controlPath, args).focus();
}

// ---------------------------------------
myGlobalHandlers =
{
	onCreate: function() {
	},
	onComplete: function() {
		if (Ajax && Ajax.activeRequestCount == 0) {
			if (!gActiveTimer) {
				gActiveTimer = setTimeout("timerComplete()", gCurUpdateInterval);
			}
		}
	}
};
if (Ajax)	// may be NULL... (caller should really be including prototype.js)
	Ajax.Responders.register(myGlobalHandlers);
