/**
 * This file defines the Controller class for the Gigapan Explorer Application.  It is used 
 * to create a singleton object.  Note that if running on IE 6 or below,
 * this class cleans out any 'png' images from the application user interface, changing the 
 * image source urls to request 'gif' images.  Therefore, be sure to create 'gif' alternatives
 * for any 'png' images that are intended to be used in this application.
 * <br>
 * <ul>
 * <li>
 *   All JavaScript operations conform to the specifications of the European
 *   Computer Manufacturers Assocation, at 
 *   <a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMA-262</a>
 * </li>
 * <li>
 *   All Document Object Model operations conform to specifications of the 
 *   World Wide Web Consortium, at  
 *   <a href="http://w3c.org">w3c.org</a>, with the exception of the industry-standard and
 *   cross-browser use of the 'innerHTML' property.
 * </li>
 * </ul>
 * <br><br>
 * <a href="mailto:ehudsons@andrew.cmu.edu">Ellen Hudson-Snyder</a>, 
 * <a href="mailto:evedar@andrew.cmu.edu">Elvin Vedar</a>,
 * <a href="mailto:cbalz@andrew.cmu.edu">Christopher M. Balz</a>.
 * <br><br>CVS Version Info:<br>
 *  $Id: Gigapan.js,v 1.48 2005/12/04 18:38:22 evedar Exp $     
 * <br><br>
 * @class-prop <code>string</code> <code>Gigapan.STR_XML_DESCRIPTION_OF_LAYER_FILE_NAME</code> A constant
 *                                 value that is the standard file name for a 
 *                                 Gigapan xml description of a layer within a given image set.  It is 'layer.xml' 
 *                                 (no quotes).
 * @object-prop <code>string</code> <code>strId</code>  The i.d. of the object made from this class.
 * @object-prop <code>string</code> <code>strObjType</code>  The type of object that this class defines, in the context
 *                                                           of the application (i.e., the application type -- 'controller',
 *                                                           'widget', etc).
 * @object-prop <code>Configuration</code> <code>objConf</code>  The <code>Configuration</code> object for the application.
 * @object-prop <code>string</code> <code>strImageSetDescriptionUrl</code>  The location, in url terms, of the xml 
 *                                                                          description of the current image set.
 *                                                                          This property is configurable: see the 
 *                                                                          JavaScriptdoc for
 *                                                                          <code>Configuration.js</code>.
 * @object-prop <code>number</code> <code>intNumImagesPerSide</code>  The number of images to show per side for the
 *                                                                    <code>Plate</code> object.  
 *                                                                    This property is configurable: see the 
 *                                                                    JavaScriptdoc for
 *                                                                    <code>Configuration.js</code>.
 * @object-prop <code>Console</code> <code>wgtConsole</code>  The <code>Console</code> widget.  It holds the
 *                                                            screen display.
 * @object-prop <code>Plate</code> <code>wgtPlate</code>  The <code>Plate</code> widget.  Held by the <code>
 *                                                        Console</code> object, it holds the images of the objects 
 *                                                        objects that the user wishes to see.
 * @object-prop <code>Messenger</code> <code>objMessenger</code>  The <code>Messenger</code> object.  It handles
 *                                                                communications with the server, 
 *                                                                which are routed exclusively 
 *                                                                through the <code>Gigapan</code> controller 
 *                                                                object.
 * @object-prop <code>string</code> <code>strWindowId</code>  The i.d. of the <code>window</code> document 
 *                                                            object, cached for speed during event handling.
 * @object-prop <code>PanoramaProtocol</code> <code>objPanoramaProtocol</code>  An object used to hold the values passed
 *                                                                    in from the server via the xml 
 *                                                                    description of the current image set. This 
 *                                                                    object also serves to define the client's 
 *                                                                    understanding of this data.
 * @object-prop <code>PanoramaLayerProtocol</code> <code>objPanoramaLayerProtocol</code>  An object used to hold the values passed
 *                                                                    in from the server via the xml 
 *                                                                    description of the layer image set. This 
 *                                                                    object also serves to define the client's 
 *                                                                    understanding of this data.
 * @object-prop <code>ImageURLCalculator</code> <code>objImageURLCalculator</code>  This object performs the 
 *                                                                      calculations 
 *                                                                      needed to get the urls of the images needed
 *                                                                      for display on the <code>plate</code> dom
 *                                                                      object.
 * @object-prop <code>ServerImageSet</code> <code>objServerImageSet</code>  This object keeps track of which 
 *                                                                  part of the 
 *                                                                  image set on the server is currently in view.
 * @object-prop <code>boolean</code> <code>booTestMessaging</code> If <code>true</code>, run the messaging 
 *                                                                 test functionality.
 * @object-prop <code>string</code> <code>strTestUrl</code>  The url to use for testing messaging.  This is used
 *                                                           if <code>booTestMessaging</code> is 
 *                                                           <code>true</code>.
 * @object-prop <code>booTurnOnImageIds</code>  Whether or not to show an image i.d. tag, used for debugging
 *              purposes, for the image managed
 *              by the <code>ImageLayer</code> widget.  This value is controlled by a configuration 
 *              parameter.  For details, read the JavaScriptdoc on the <code>Configuration</code> class,
 *              located in the <code>Controller</code> package.
 * @object-prop <code>string</code> <code>strConfigErrorRemedy</code>  An error message to help the user properly 
 *                                  configure the application.
 * @object-prop <code>RegExp</code> <code>rxpIsLayerRequest</code> A regular expression matching urls ending in 
 *                                  'layer.xml'.  This property is used to distinguish the receipt of responses to 
 *                                  xml-http requests for an xml description of a Gigapan image layer within a 
 *                                  given image set.
 * @author Team GigaToasted (Fall-2005-CMU-NASA/Google-Practicum Subteam) 
 * @version 1.0
 * <br><br>
 */

Gigapan.STR_XML_DESCRIPTION_OF_LAYER_FILE_NAME = "layer.xml";

/**
 * Create a <code>Gigapan</code> controller object.
 */
function Gigapan() {
    this.strId = "gigapan";
    this.strObjType = "controller";
    this.objConf = new Configuration(this);
    this.booIsIE6OrLess = (self.gloScope.objIs.ie && self.gloScope.objIs.major < 7);
    this.strImageSetDescriptionUrl = this.objConf.getParameter(Configuration.STR_PARAM_NAME_IMAGE_SET_DESCRIPTION_URL);
    this.intNumImagesPerSide =  this.objConf.getParameter(Configuration.STR_PARAM_NAME_NUMBER_IMAGES_PER_SIDE);
    this.booTurnOnImageIds = this.objConf.getParameter(Configuration.STR_PARAM_NAME_DEBUGGING_FLAGS_FOR_IMAGE_TILES_ON);
    this.strWindowId = window.id = "gigapanWindow"; // Set here since cannot do so in html.
    this.wgtConsole = new Console(this);
    this.objPanoramaProtocol = new PanoramaProtocol(this);
    this.objPanoramaLayerProtocol = new PanoramaLayerProtocol(this);
    this.wgtPlate = new Plate(this, this.wgtConsole);
    this.hshGrabAndDragTargets = {};
    this.grabAndDragAspect = null;
    this.objImageURLCalculator = new ImageURLCalculator(this,
                                                        this.objConf.getParameter(
                                                            Configuration.STR_PARAM_NAME_IMAGE_SET_ROOT_URL));
    this.objServerImageSet = new ServerImageSet(this);
    this.objMessenger = new Messenger(this);
    this.rxpIsLayerRequest = new RegExp(Gigapan.STR_XML_DESCRIPTION_OF_LAYER_FILE_NAME + "$");
    this.booTestMessaging = false;
    this.strTestUrl = "http://localhost/gigapan/xml/panorama_protocol/descriptions/salttown.xml";
    this.objStringBuffer;

    // Methods:
    this.init = Gigapan_init;
    this.draw = Gigapan_draw;
    this.receiveEvent = Gigapan_receiveEvent;
    this.testPlateMove = Gigapan_testPlateMove;
    // this.getNewImageSet = Gigapan_getNewImageSet;
    this.exit = Gigapan_exit;
    this.getXmlDescriptionOfLayer = Gigapan_getXmlDescriptionOfLayer;
    this.setPassThroughEvents = Gigapan_setPassThroughEvents; 

    self.gloScope.registerEvent(this.strId, this, "mainBody", "ondblclick");
    self.gloScope.registerEvent(this.strId, this, "mainBody", "onmousedown");
    self.gloScope.registerEvent(this.strId, this, "mainBody", "onmouseup");
    self.gloScope.registerEvent(this.strId, this, "mainBody", "onmousemove");
    this.init();
}


/**
 * This method draws performs any initialization tasks, including
 * commanding the drawing of the application on the screen.
 */
function Gigapan_init() {
    var domMainBody = null;

    self.gloScope.booDebugMode = true;
    this.hshGrabAndDragTargets[ this.wgtPlate.strId ] = this.wgtPlate; 
    self.gloScope.registerEvent(this.strId, this, this.objMessenger.strId, "onafterreceive");
    if (this.booTestMessaging) { 
        this.objMessenger.send(this.strTestUrl);
    }
    this.objMessenger.send(this.strImageSetDescriptionUrl);
    // this.testPlateMove();
}


/**
 * This method draws the application on the screen.
 */
function Gigapan_draw() {
    var wgtConsole = this.wgtConsole;
    this.objStringBuffer = new StringBuffer();
    this.wgtPlate.getHtml(this.objStringBuffer);

    gloScope.domCanvas.innerHTML = this.objStringBuffer.toString() + this.wgtConsole.getHtml();    
    wgtConsole.resize(); // Fit console to the window.
    if (this.booIsIE6OrLess) { 
        // IE under v. 7 does not support image transparency (in png's).
        self.gloScope.swapPngForGif( window, wgtConsole.wgtViewPortWindowSill.getDomRef() );
    }
    // Bind the listeners of any non-html-drawn dom objects (i.e., the window, etc): 
    wgtConsole.bindEvents();
}



function Gigapan_setPassThroughEvents() {
    var domMainBody; 

    if ( !this.booIsIE6OrLess ) {
        domMainBody = document.getElementById("mainBody");
        domMainBody.addEventListener('dblclick',function(objEvent){self.Environment.handleEvent("mainBody", "ondblclick", objEvent);},false);        
        domMainBody.onmousedown = function(pObjEvent) { 
            self.Environment.handleEvent("mainBody", "onmousedown", pObjEvent); 
            return false;
        };
        domMainBody.onmouseup   =  function(pObjEvent) { 
            self.Environment.handleEvent("mainBody", "onmouseup", pObjEvent); 
            return false;
        };
        domMainBody.onmousemove = function(pObjEvent) { 
            self.Environment.handleEvent("mainBody", "onmousemove", pObjEvent); 
            return false;
        };
    }
}


/**
 * This method receives any events that this object listens to and 
 * routes them to the proper methods.  It is highly optimized for speed, mainly through caching <code>this</code>
 * properties and sub-properties in local variables.  Also, it is optimized by avoiding excess method or function
 * calls and using in-line code (even repeated code) where necessary (this is important in interpreted languages).
 * @param pStrEventSourceElementId  <code>string</code> The i.d. of the document object model from which the event sprang.
 * @param pStrEventType   <code>string</code>  The event type of the object (e.g., 'onclick', etc).
 * @param pObjEvent   <code>Object</code>  MAY BE <code>null</code>  The event object itself.
 */
function Gigapan_receiveEvent(pStrEventSourceElementId, pStrEventType, pObjEvent) { 
    var checkNum, intX, intY, wgtConsole = this.wgtConsole, wgtPlate = this.wgtPlate; // Cache in 'var's for speed.

    if (!this.booIsIE6OrLess && pStrEventType == "onmousemove") {
        intX = pObjEvent.clientX;
        intY = pObjEvent.clientY;
        if ((intX >= wgtPlate.intPresentationLeftPx) &&
            (intX <= (wgtPlate.intPresentationLeftPx +
                                  (wgtPlate.intNumStacksWide * wgtPlate.intStackWidthPx))) &&
            (intY >= wgtPlate.intPresentationTopPx) &&
            (intY <= (wgtPlate.intPresentationTopPx +
                                  (wgtPlate.intNumStacksHigh * wgtPlate.intStackHeightPx))) &&
            (intY <= (wgtConsole.getHeightWidth()[0] - wgtConsole.getWindowSillHeight() -
                                  (2*wgtConsole.getWindowSillBorderWidths()[0] -
                                  wgtConsole.getBorderWidths()[1] ))) ){
            wgtPlate.onmousemove(pObjEvent);
        }
    } else if (pStrEventSourceElementId == this.strWindowId && pStrEventType == "onresize") {
        this.resize();
    } else if (!this.booIsIE6OrLess && pStrEventType == "onmousedown") {
        intX = pObjEvent.clientX;
        intY = pObjEvent.clientY;
        if ((intX >= wgtPlate.intPresentationLeftPx) &&
            (intX <= (wgtPlate.intPresentationLeftPx +
                                  (wgtPlate.intNumStacksWide * wgtPlate.intStackWidthPx))) &&
            (intY >= wgtPlate.intPresentationTopPx) &&
            (intY <= (wgtPlate.intPresentationTopPx +
                                  (wgtPlate.intNumStacksHigh * wgtPlate.intStackHeightPx))) &&
            (intY <= (wgtConsole.getHeightWidth()[0] - wgtConsole.getWindowSillHeight() -
                                  (2*wgtConsole.getWindowSillBorderWidths()[0] -
                                  wgtConsole.getBorderWidths()[1] ))) ){
            wgtPlate.onmousedown(pStrEventSourceElementId, pStrEventType, pObjEvent);
        }
    } else if (!this.booIsIE6OrLess && pStrEventType == "onmouseup") {
        intX = pObjEvent.clientX;
        intY = pObjEvent.clientY;
        if ((intX >= wgtPlate.intPresentationLeftPx) &&
            (intX <= (wgtPlate.intPresentationLeftPx +
                                  (wgtPlate.intNumStacksWide * wgtPlate.intStackWidthPx))) &&
            (intY >= wgtPlate.intPresentationTopPx) &&
            (intY <= (wgtPlate.intPresentationTopPx +
                                  (wgtPlate.intNumStacksHigh * wgtPlate.intStackHeightPx))) &&
            (intY <= (wgtConsole.getHeightWidth()[0] - wgtConsole.getWindowSillHeight() -
                                  (2*wgtConsole.getWindowSillBorderWidths()[0] -
                                  wgtConsole.getBorderWidths()[1] ))) ){
            wgtPlate.onmouseup(pStrEventSourceElementId, pStrEventType, pObjEvent);
        }
    } else if (pStrEventSourceElementId == this.objMessenger.strId && pStrEventType == "onafterreceive") {
        if (this.booTestMessaging) {
            alert("Type of xml response (should be 'object'): " + (typeof this.objMessenger.getXmlResponse()) );
        } else if (!pObjEvent.strRequestUri.match(this.rxpIsLayerRequest)) { // Is a 'PanoramaProtocol' document, for initialization:
           this.objPanoramaProtocol.init(pObjEvent.objXmlHttpRequest.responseXML);
           this.objServerImageSet.init(this.objPanoramaProtocol);           
           wgtPlate.init(this.objPanoramaProtocol, 
                              this.objServerImageSet.objPlateUpdateResponse.intNumRowsInCompositeImage,
                              this.objServerImageSet.objPlateUpdateResponse.intNumColsInCompositeImage);
           this.draw();           
           this.grabAndDragAspect = new GrabAndDrag( this, this.hshGrabAndDragTargets );
           this.setPassThroughEvents(); // Work around unfortunate aspect in FF/Opera event model.
           wgtPlate.initImageLayers();
           wgtPlate.reloadZoomButtonState();
           wgtPlate.reloadBrowseLayerLabels();
        } else { // Layer information
           this.objPanoramaLayerProtocol.init(pObjEvent.objXmlHttpRequest.responseXML);
           this.objServerImageSet.initLayers(this.objPanoramaLayerProtocol);
           wgtPlate.reloadBrowseLayerLabels();
        }
    } else if (!this.booIsIE6OrLess && pStrEventType == "ondblclick") {
        intX = pObjEvent.clientX;
        intY = pObjEvent.clientY;
        /* Decide if over plate */
        if ((intX >= wgtPlate.intPresentationLeftPx) &&
            (intX <= (wgtPlate.intPresentationLeftPx +
                                  (wgtPlate.intNumStacksWide * wgtPlate.intStackWidthPx))) &&
            (intY >= wgtPlate.intPresentationTopPx) &&
            (intY <= (wgtPlate.intPresentationTopPx +
                                  (wgtPlate.intNumStacksHigh * wgtPlate.intStackHeightPx))) &&
            (intY <= (wgtConsole.getHeightWidth()[0] - wgtConsole.getWindowSillHeight() -
                                  (2*wgtConsole.getWindowSillBorderWidths()[0] -
                                  wgtConsole.getBorderWidths()[1] ))) ){
            wgtPlate.ondblclick(pObjEvent);
        }
    }  
}


/**
 * This method manages sending a request to the <code>Messenger</code> object for 
 * an xml description of an image layer within a Gigapan image set.
 * @param pStrRootUrl <code>string</code> The root url (url minus the file name) of the
 *                    file containing the xml description to be loaded.
 */
function Gigapan_getXmlDescriptionOfLayer(pStrLayerRootUrl) {
    var strRootUrl = this.objConf.getParameter(Configuration.STR_PARAM_NAME_IMAGE_SET_ROOT_URL);
    this.objMessenger.send(strRootUrl + pStrLayerRootUrl + "/" + Gigapan.STR_XML_DESCRIPTION_OF_LAYER_FILE_NAME);
}


/**
 * This method serves as a test stub that may be commented out when desired. 
 */
function Gigapan_testPlateMove() {
    // for ( var i = 0; i < 500; i++){
    //  this.wgtPlate.receiveEvent("navRightBtn", "onclick", {});
    // }
}


/**
 * This method issues the commands necessary to free memory allocated to 
 * this application for garbage collection.
 */
function Gigapan_exit() {
    self.GloScope.unload();
    this.wgtConsole.unload();
}


// --- Code Junkyard:
///
// This method fetches a new image set from the server.
// @param booExceededRightwardTravel  <code>true</code> if the <code>Plate</code> has exceeded its 
//                                    rightward travel.
// @param booExceededLeftwardTravel   <code>true</code> if the <code>Plate</code> has exceeded its 
//                                    leftward travel.
// @param booExceededUpwardTravel     <code>true</code> if the <code>Plate</code> has exceeded its 
//                                    upward travel.
// @param booExceededDownwardTravel   <code>true</code> if the <code>Plate</code> has exceeded its 
//                                    downward travel.
//
//function Gigapan_getNewImageSet( booExceededRightwardTravel, booExceededLeftwardTravel, 
//                                 booExceededUpwardTravel, booExceededDownwardTravel ) {
//}




