/** 
 * This is the Graph class.  It defines an object that manages the display of the graph,
 * which holds the actual plot of the 2D image.  All static data pertaining to the graph is 
 * managed by the Graph object.
 */
PrtGraph.prototype = new abstract_Widget();
function PrtGraph() {
  if (Graph.intNumCurvesPossible > 12) {
    alert("There are not enough bright web-safe colors to make more than 12 curves.");
    if (GloScope.CrossPlatformCode.canGetStackTrace()) { 
      alert( (new StackTrace()).getStackTrace());
    }
  }
  this.intOffsetLeft = 20;    // Units are in pixels.
  this.intOffsetTop  = 20;    // Units are in pixels.
  this.intHeight     = 500;   // Units are in pixels.
  this.intWidth      = 500;   // Units are in pixels.
  this.objInputForm  = null;  // A reference to the input form widget.
  // Methods:
  this.getHtml         = Graph_getHtml;
  this.plotOnGraph     = Graph_plotOnGraph;
  this.renderAxes      = Graph_renderAxes;
  this.setInFront      = Graph_setInFront;
  this.cleanReferences = Graph_cleanReferences;
  /*- 
   * This method returns the height of the graph.
   * @return integer  The height of the graph in pixels.
   */
  this.getHeight     = function () { return this.intHeight; };
  /*- 
   * This method returns the width of the graph.
   * @return integer  The width of the graph in pixels.
   */
  this.getWidth      = function () { return this.intWidth;  };
  // --> special:
  this.PrtGraph_GetObjects = PrtGraph_GetObjects; // For inheritance purposes only. 
}


/**
 * This method provides a convenient way to implement the inheritance of objects
 * from the superclass to the extending class. 
 */
function PrtGraph_GetObjects() {
  this.arrContainerDivs = new Array(Graph.intNumCurvesPossible);
  this.objDivInFront = new Object();
  this.objDivInFront.intFormerZindex = null;
  this.objDivInFront.intArrayIndex = "";
  this.objDivInFront.intTopZindex = 5000; 
}

// Static values:
Graph.prototype = new PrtGraph(); // Scope chain assignment.
Graph.intNumCurvesPossible = 12;
/*-
 *  Optimization dictionary for point plotting (so that the literals don't 
 * have to be wrapped as objects for concatenation):
 */
Graph.objPId0 = new String("Pt"); // 'Pt' for 'Point'.
Graph.objPId1 = new String("inCont"); // 'intCont' for 'inContainer'.
Graph.objP1   = new String(" <div id='"); 
/*-
 * Note: Below, we leave the height & width as HTML attributes because IE5.0 freaks out
 * if they are in the style attribute.
 */
Graph.strAppropriateDivHW = GloScope.CrossPlatformCode.getFancyDivHtml(1,1);
Graph.objP2 = new String("' " + Graph.strAppropriateDivHW + " background-color: ");
Graph.objP3 = new String(";  position: absolute; top: ");
Graph.objP4 = new String("px; left: ");
Graph.objP5 = new String("px; ' > "+(abstract_Widget.getFillerMarkup(1,1))+"  </div> ");


/**
 * This is the constructor for the Graph class. 
 * @param intWidth   The width of the graph on the screen.
 * @param intHeight  The height of the graph on the screen.
 */
function Graph(intWidth, intHeight, objInputForm) {
  this.abstract_Widget();     // Invoke the superclass constructor here in order to get any of its objects. 
  this.PrtGraph_GetObjects(); // Inherit unique copies of all objects from the prototype/abstract class.
  this.objInputForm = objInputForm;
  hshVisibleXYranges = this.hshVisibleXYranges = this.objInputForm.getData();
  if (arguments.length > 0 && (intWidth != null) ) {
    this.intWidth  = intWidth;
  }
  if (arguments.length > 1 && (intHeight != null)) {
    this.intHeight = intHeight;
  }
  for (var intI=0; intI < Graph.intNumCurvesPossible; intI++) {
    this.arrContainerDivs[intI] = new ContainerDiv( this.intOffsetLeft, this.intOffsetTop, 
                                                    this.intWidth, this.intHeight, intI );
  }
  this.objAxes 
    = new Axes(this.intOffsetLeft, this.intOffsetTop, this.intWidth, 
               this.intHeight, hshVisibleXYranges);
}


/**
 * This method returns the HTML markup needed to draw the entire graph
 * (div containers and axes) on the screen. 
 * @return strHtml  A string containing the above.
 */
function  Graph_getHtml() {
  var strHtml = "";
  var arrHtml = new Array();
  for (intI=0; intI < Graph.intNumCurvesPossible; intI++) {
    arrHtml[arrHtml.length] = this.arrContainerDivs[intI].getHtml();
  }
  arrHtml[arrHtml.length] = "\n\n"+this.objAxes.getHtml();
  strHtml = arrHtml.join("\n<!-- break -->\n");
  // alert(strHtml);
  return strHtml;
}


/**
 * This method puts the individual dots that comprise a plotted line on the 
 * screen, in one of the div containers specified.
 * @param intContainerToDrawIn  The div container that is to hold the plotted line.
 * @param arrPlotCoordinates    The actual plot coodinates (x,y) to put on the screen.
 */
function Graph_plotOnGraph(intContainerToDrawIn, arrPlotCoordinates) {
  var strMessage = null;
  var divPlotContainer = null;
  var strId = null;
  var intXrange = null;
  var intYrange = null;
  var hshVisibleXYranges = null;
  var intBorderAdj = null;

  if (intContainerToDrawIn < 0 || intContainerToDrawIn > (this.arrContainerDivs.length-1) ) {
    strMessage = "DevTime Alert: Can't draw on the container div layer indexed by #"
      +intContainerToDrawIn+" because it doesn't exist.";
    alert(strMessage);
  } else {
    // Convert coordinates from equation to coordinates for the screen:
    hshVisibleXYranges = this.hshVisibleXYranges = this.objInputForm.getData();
    intXrange = Math.abs(hshVisibleXYranges[Grapher.MAXX] - hshVisibleXYranges[Grapher.MINX]);
    intYrange = Math.abs(hshVisibleXYranges[Grapher.MAXY] - hshVisibleXYranges[Grapher.MINY]);
    intBorderAdj = GloScope.CrossPlatformCode.getBorderAdjustment()/2;

    for (var intI=0; intI<arrPlotCoordinates.length; intI++) {
      arrPlotCoordinates[intI].intX 
        = this.intWidth - (Math.round(
                                      (Math.abs(arrPlotCoordinates[intI].intX - hshVisibleXYranges[Grapher.MAXX])
                                       / intXrange)
                                      * this.intWidth) 
                           ) - intBorderAdj; 
      arrPlotCoordinates[intI].intY 
        = (Math.round(
                      ((hshVisibleXYranges[Grapher.MAXY] - arrPlotCoordinates[intI].intY)
                       / intYrange)
                      * this.intHeight)
           ) - intBorderAdj; 
      if (arrPlotCoordinates[intI].intY < 0 ) {
        arrPlotCoordinates[intI] = "dontPlot";
        // arrPlotCoordinates[intI].intY = -1;
      }
      if (arrPlotCoordinates[intI].intY > (this.intHeight-(intBorderAdj*2) ) ) {
        arrPlotCoordinates[intI] = "dontPlot";
        // arrPlotCoordinates[intI].intY = this.intHeight+1;
      }
    }

    // Create HTML (see above for details on pre-assigned static objects for optimization):
    var arrHtml = new Array();
    var strHtml = "";
    for (var intI=0; intI < arrPlotCoordinates.length; intI++) {
      if (arrPlotCoordinates[intI] != "dontPlot") {
      strId = Graph.objPId0 + intI + Graph.objPId1 + intContainerToDrawIn;
      arrHtml[arrHtml.length] = 
          Graph.objP1 + strId + Graph.objP2 + this.GRAPH_COLORS[intContainerToDrawIn]
        + Graph.objP3 + arrPlotCoordinates[intI].intY + Graph.objP4 
        + arrPlotCoordinates[intI].intX + Graph.objP5;
      }
    }
    divPlotContainer = this.arrContainerDivs[intContainerToDrawIn].getDomObj();
    // alert("divPlotContainer.id = "+divPlotContainer.id);
    var strContents =  arrHtml.join(" ");
    GloScope.CrossPlatformCode.writeToDiv(divPlotContainer, strContents);
  }
}


/** 
 * This method accomplishes the task of putting the specified div layer
 * in front of all of the other graphs.
 * @param intIndex  The selector for the div layer to put in front.
 */
function Graph_setInFront(intIndex) {
  var divCont = this.arrContainerDivs[intIndex].getDomObj();
  if (this.objDivInFront.intFormerZindex != null) { // Return the current front div to its old position.
    var divFormerFront = this.arrContainerDivs[this.objDivInFront.intArrayIndex].getDomObj();
    divFormerFront.style.zIndex = this.objDivInFront.intFormerZindex;
  }
  this.objDivInFront.intArrayIndex = intIndex;
  this.objDivInFront.intFormerZindex = divCont.style.zIndex;
  divCont.style.zIndex = this.objDivInFront.intTopZindex; 
}


/**
 * This method draws the axes in their container. 
 * @param hshXYRanges  The numeric range of each axis.  
 */
function Graph_renderAxes(hshXYranges) {
  var booStrAxesHtml;
  this.hshVisibleXYRanges = hshXYranges;
  booStrAxesHtml = this.objAxes.getChangeAxesHtml(hshXYranges);
  if (booStrAxesHtml) {
    for (var intI=0; intI<this.arrContainerDivs.length; intI++) {
      this.arrContainerDivs[intI].clear();
    }
    GloScope.CrossPlatformCode.writeToDiv(this.objAxes.getDomObj(),
                                          booStrAxesHtml);
  }
}


/**
 * This method removes all Graph references from the JS to the DOM. 
 * Because there are event handlers in the input_form widget that refer to
 * JS objects, references to the DOM elements of those event handlers must 
 * be removed.  For safety in case event handlers are added to the ContainerDivs
 * later, references to those DOM elements are removed as well.
 */
function Graph_cleanReferences() {
    for (var intI=0; intI < this.arrContainerDivs.length; intI++) {
      this.arrContainerDivs[intI].cleanReference();
    }
    this.objAxes.cleanReference();
}





