//SelectionControl.js
//
//Handles management of selections associated with a given map.
//Selections are displayed in a set of tabs at the moment
//
//Future implementation plan:
//   Establish an virtual callback function invoked when the xml response
//    is recieved.  This function will be responsible for establishing the
//    appropriate target element for the data.  This could be anything from
//    validating the existance of a DIV to creating a new tab in a tab panel.
//  Establish a virtual callback function invoked when a clear selection request
//    is recieved.  This function will be responsible for erasing and cleaning
//    up after the selection.
//
//
//Attributes:
//  map        = pointer to map object associated with the selection object.
//  layerIndex = index of the layer in the map's layer array.
//  handle     = selection handle in the session on the server.
//  data       = selection data returned from the server
//
//Methods:
//  init       average ordinary constructor.  takes map, layerIndex as parameters.
//  callback   xmlrpc callback function.  accepts the xml response as a parameter.
//             also invokes a function in the map object, providing the updated map
//             information

SelectionControl = function(newId, newMapObject, newMapId, selectionPanelTarget)
{
  if (arguments.length > 0)
    this.init(newId, newMapObject, newMapId, selectionPanelTarget);
};

SelectionControl.prototype.init = function(newId, newMapObject, newMapId, selectionPanelTarget)
{
  //Initialize selection array
  //Selections are implemented as a linked list
  //  one list per theme
  //  based on the number of selections allowed per theme, we
  //  can determine what to do when a new selection is added.
  this.id = newId;

  if (newMapId!=null)
  {
    this.mapObject = OBJECT_MANAGER.getControl(newMapId);
    this.mapId = newMapId;
  }
  else
  {
    this.mapObject = newMapObject;
    this.mapId = newMapObject.id;
  }
  this.mapObject.selectionControl = this;
  OBJECT_MANAGER.addControl(this,'selectionControl', newId);
  this.selectionPanel = selectionPanelTarget;
};

SelectionControl.prototype.initialize = function()
{
  this.selections = null;
  this.selections = new Object;
  for (var i in this.mapObject.config.themes)
  {
    if(i=='toJSONString')continue;
    if (this.mapObject.config.themes[i]["selectOptions"])
    {
      this.selections[i] = {
        list: null,
        length: 0,
        maxLength: this.mapObject.config.themes[i]["selectOptions"]["limit"],
        panel: document.createElement('DIV')
      }
      this.selections[i].panel.style.display='none';
      this.selectionPanel.appendChild(this.selections[i].panel);
    }
  }
};

SelectionControl.prototype.setSelectionListHead = function(themeIndex, newListHead)
{
   //serves three purposes
   //  1) if the first selection in a list has been killed, allows the second to take its place
   //  2) allows a list to be explicitally set to a particular selection pointer.
   //  3) if the only element in a list has been killed, clears the list completely
   this.selections[themeIndex].list = newListHead;
   if (newListHead == null)
     this.selections[themeIndex].length = 0;
   else
     this.selections[themeIndex].length = this.getNumSelections(themeIndex);
};

SelectionControl.prototype.getNumSelections = function(themeIndex)
{
  if (this.selections[themeIndex].list != null)
    return this.selections[themeIndex].list.length();
  else
    return 0;
};

SelectionControl.prototype.removeSelectionListHead = function(themeIndex)
{
  if (this.selections[themeIndex].list.next != null)
    this.selections[themeIndex].list = this.selections[themeIndex].list.next;
  else
    this.selections[themeIndex].list = null;
};

SelectionControl.prototype.clearAll = function()
{
  for (var i in this.selections){
    var sel=this.selections[i];
    if(sel['list']) sel['list'].clearAll();
    sel['length']=0;
    sel['panel'].innerHTML='';
    sel['panel'].style.display='none';
  }
};

SelectionControl.prototype.clearSelection = function(themeIndex, handle)
{
  this.mapObject.clearSelection(themeIndex, handle);
};

SelectionControl.prototype.clearTheme = function(themeIndex)
{
  this.mapObject.clearThemeSelections(themeIndex); 
};

SelectionControl.prototype.zoomTo = function(themeIndex, handle)
{
  var currentSelection = this.getSelection (themeIndex, handle);
  var useQuotes = true;
  switch (this.mapObject.config.themes[themeIndex]['selectOptions']['keyFieldType'])
  {
    case  -7:   //'Boolean';
    case  -5:   //'Big Int';
    case   4:   //Integer
    case   5:   //SmallInt
    case   6:   //Float
    case   8:   //Double
    case  91:   //Date
      useQuotes = false;
      break;
    default:
      break;
  }
  var query = currentSelection.keyField+' = '+((useQuotes)?('\''):(''))+currentSelection.keyValue+((useQuotes)?('\''):(''));
  this.mapObject.zoomToSelection(themeIndex, query, false);
};

SelectionControl.prototype.getSelection = function(themeId, handle)
{
  return this.selections[themeId].list.getSelectionByHandle(handle); 
};

SelectionControl.prototype.printSelection = function(themeId, handle)
{
  this.selections[themeId].list.getSelectionByHandle(handle).printData();
};

SelectionControl.prototype.getAttachmentElem = function(themeId, handle)
{
  return(this.selections[themeId].list.getSelectionByHandle(handle).getAttachmentElem());
};

SelectionControl.prototype.displaySelection = function(newThemeIndex, newSelectionHandle)
{
  var selection = this.getSelection(newThemeIndex, newSelectionHandle);
  var currentSelectionTheme = newThemeIndex;
  var currentSelectionHandle = newSelectionHandle;
  if (selection == null)
    alert('Application Error in\n'+this.id+'.displaySelection('+newThemeIndex+','+newSelectionHandle+'):\nSelection '+newSelectionHandle+' on theme '+newThemeIndex+' not found\nUnable to display selection');
  else
  {
    selection.renderTarget = this.selections[newThemeIndex].panel;
    selection.renderTarget.style.display='block';
    selection.render();
    if (this.onSelectionDisplay)
      this.onSelectionDisplay();
  }
};

SelectionControl.prototype.callback = function (serverReplyDoc, requestDetails)
{
  //data returned from server:
  //  [0] = Map data array - pass directly to mapObject.
  //  [1..n] = selections
  //    [0] = selection handle (integer)
  //    [1] = data from theme
  //    [2] = data from cascaded SQL queries

  //Prepare response data
  //alert(this.id+'.callback');
  var xmlstr = '';
  if (serverReplyDoc != null)
      xmlstr = (typeof(serverReplyDoc.xml)=='function')?serverReplyDoc.xml():serverReplyDoc.xml;
  if((serverReplyDoc == null) || (xmlstr == '') || (serverReplyDoc.documentElement.nodeName == 'parsererror'))
  {
    return(this.mapObject.selectionCallback(null));
  }
  else
  {
    var clientReply = new XMLRPCResponse();
    //clientReply.setResponseByStr(serverReplyDoc.xml);
    clientReply.setResponseByDoc(serverReplyDoc);
  }

  //Verify data
  if(clientReply.isFault())
  {
    var data = null;
    
    if (clientReply.getFaultCode() != 1013)  //ignore case where no selections found
      alert('Error in Selection operation\nFault Code:'+clientReply.getFaultCode()+'\n"'+clientReply.getFaultString()+'"');
    return(this.mapObject.selectionCallback(null));
  }
  else  //data is good, process
  {
    var data = clientReply.getObject();
    switch (requestDetails.request)
    {
      case 'newSelection':                        //new selection(s) added
        var themeId = requestDetails.themeId;
        var existingElement = null;
        /** TODO:  Handle multiple selections differently from single selections.  if more than one, show index instead of the selection itself. **/
        for (var currentSelection=1; currentSelection<data.length; currentSelection++)
        {
          //check to see if this feature is already selected
          if (existingElement == null)
          {
            newSelection = new Selection(this.id+'.selection', themeId,this, this.mapObject, data[currentSelection], null, null);
            if (this.selections[themeId].list == null)
            {
              this.selections[themeId].list = newSelection;
            }
            else
            {
              if (this.selections[themeId].list.length() == 1)
              {
                newSelection.next = this.selections[themeId].list.next;
                if (newSelection.next != null)
                {
                  newSelection.next.prev = newSelection;
                }
                this.selections[themeId].list.clear();
                this.selections[themeId].list = newSelection;
              }
              else
              {
                newSelection.prev = this.selections[themeId].list.lastElement();
                this.selections[themeId].list.lastElement().next = newSelection;
              }
            }
            this.displaySelection(themeId,newSelection.handle);
          }
        }
        //scrollToUpperMapRange();  // out for now as it shuns the banner
        this.mapObject.selectionCallback(data[0]);   //pass map data to mapObject
        break;
      case 'clearSelection':
        var currentSelection = this.getSelection(requestDetails.themeIndex, requestDetails.handle);
        currentSelection.clear();  //unlinks selection from list
        this.selections[themeId].panel.innerHTML = '';
        this.mapObject.redraw();
        this.mapObject.setWaiting(false);  //call after requesting redraw so that the waiting image doesn't go out briefly
        break;
      case 'clearTheme':
        var themeId = requestDetails.themeIndex;
        if (this.selections[themeId].list != null)
          this.selections[themeId].list.clearAll();
        this.selections[themeId].panel.innerHTML = '';
        this.mapObject.redraw();
        this.mapObject.setWaiting(false);  //call after requesting redraw so that the waiting image doesn't go out briefly
        break;
      default:
        break;
    }
  }
  return;
};

