// index.js
// supposed to render plot in real-time using javascript... I wonder... 

var Xpoints = [];  // scaled X coordinates for plot
var Ypoints = [];  // scaled Y coordinates for plot
var Y2points = []; // scaled Y coordinates for plot
var objGraph;      // graphing object

var rawData = [];  // storage for full data set
var tUpdate;       // timeout object for updating

var GRAPH_WIDTH  = 500;
var GRAPH_HEIGHT = 200;

var MARGIN_TOP = 30;
var MARGIN_BOTTOM = 30;
var MARGIN_LEFT = 40;
var MARGIN_RIGHT = 40;

var SAMPLES_PER_QUERY = 2;
var SAMPLE_PERIOD = 10; // every 10 seconds
var MAX_POINTS = 360;   // About 1 hour

var vars = ["f0min","f0max","f0now", "f1min","f1max","f1now"];
var vals = [ "NaN",   "NaN",   "NaN",    "NaN",   "NaN",   "NaN" ];
var ts_min = "NaN";
var ts_max = "NaN";


Array.prototype.min = function() { 
  return Math.min.apply( Math, this );
}

Array.prototype.max = function() { 
  return Math.max.apply( Math, this );
}


function calculatePlotSize(objId) { 
  var obj = document.getElementById(objId);
  if( !obj ) { 
    return false;
  }
  /*
  GRAPH_WIDTH  = parseInt(obj.style.width);
  GRAPH_HEIGHT = parseInt(obj.style.height);
  */
}


function lookupTimeScale() { 
  var dt = ts_max-ts_min;
  var step = 60;  // min step size = 1 minute

  var dtFmt = ":%S";
  if( dt > 126144000 ) { // 4 years
    step = dt/8;
    dtFmt = "%Y";
  } else if( dt > 10368000 ) { // 120 days
    step = 2592000; // 30 days
    dtFmt = "%b";
  } else if( dt > 2419200 ) {  // 4 weeks
    step = 604800;  // 1 week 
    dtFmt = "%b %d";
  } else if( dt > 345600 ) { // 4 days
    step = 86400; // 1 day
    dtFmt = "%d";
  } else if( dt > 86400 ) { // 1 day 
    step = 21600; // 6 hours
    dtFmt = "%d %H:%M";
  } else if( dt > 14400 ) { // 4 hours
    step = 3600;  // 1 hour
    dtFmt = "%H:%M:";
  } else if( dt > 3600 ) { // 1 hour
    step = 900;  // 15 minutes
    dtFmt = "%H:%M:";
  } else if( dt > 1200 ) {  // 20 minutes
    step = 300;  // 5 minutes
    dtFmt = "%H:%M:";
  } else if( dt > 240 ) { // 4 minutes
    step = 60; // 1 minute
    dtFmt = "%H:%M:";
  } else if( dt > 120 ) { 
    step = 30;
    dtFmt = ":%M:%S";
  } else if( dt > 40 ) { 
    step = 10;
    dtFmt = ":%M:%S";
  } else if( dt > 20 ) { 
    step = 5;
    dtFmt = ":%M:%S";
  } else { 
    step = 1;  // 1 second
    dtFmt = ":%M:%S";
  }
  return [step,dtFmt];
}


function updateGraph() {
  var dx = GRAPH_WIDTH - MARGIN_LEFT - MARGIN_RIGHT;
  var dt = ts_max-ts_min;

  var x0 = MARGIN_LEFT;
  var x1 = GRAPH_WIDTH-MARGIN_RIGHT;

  // Had it not been for IE 6, this would work.
  // [step,dtFmt] = lookupTimeScale();

  var a = lookupTimeScale();
  step  = a[0];
  dtFmt = a[1];

  objGraph.clear();
  objGraph.setColor("white");
  objGraph.fillRect(0,0,GRAPH_WIDTH,GRAPH_HEIGHT);

  objGraph.setColor("#000000");
  objGraph.setFont("arial","10px",Font.PLAIN);
  objGraph.drawString("gal/min",GRAPH_WIDTH-50,5);

  objGraph.setFont("arial","20px",Font.ITALIC_BOLD);
  objGraph.drawStringRect("Flow Rates",0,5,GRAPH_WIDTH,"center");

  if( rawData.length < 1 ) { 
    objGraph.drawRect(MARGIN_LEFT,MARGIN_TOP,GRAPH_WIDTH-MARGIN_LEFT-MARGIN_RIGHT,GRAPH_HEIGHT-MARGIN_TOP-MARGIN_BOTTOM);
    objGraph.setFont("arial","30px",Font.BOLD);
    objGraph.drawStringRect("No Data",0,Math.round(GRAPH_HEIGHT/2.5),GRAPH_WIDTH,"center");
    objGraph.paint();
    return;
  }

  objGraph.setFont("arial","10px",Font.PLAIN);

  var objDate = new Date();
  objGraph.setColor("#000000");
  objDate.setTime(ts_min*1000);
  objGraph.drawString(objDate.strftime("%Y-%m-%d %H:%M:%S %Z"),2,GRAPH_HEIGHT-13);
  objDate.setTime(ts_max*1000);
  objGraph.drawStringRect(objDate.strftime("%Y-%m-%d %H:%M:%S %Z"),2,GRAPH_HEIGHT-13,GRAPH_WIDTH-4,"right");


  objGraph.setColor("#e9e9e9");
  objGraph.setStroke(Stroke.DOTTED);

  var t0 = ts_min - ts_min%step;
      t0 = (t0==ts_min) ? t0 : t0+step;

  var ws_step = Math.round( step*dx/dt );
  for(var t=t0;t<ts_max;t+=step) { 
    var x = MARGIN_LEFT + Math.round( (t-ts_min)*dx/dt );
    objGraph.setColor("#e9e9e9");
    objGraph.drawLine(x,GRAPH_HEIGHT-MARGIN_BOTTOM,x,MARGIN_TOP);
    objDate.setTime(t*1000);
    objGraph.setColor("#000000");
    objGraph.drawStringRect(objDate.strftime(dtFmt),x-ws_step/2,GRAPH_HEIGHT-MARGIN_BOTTOM+2, ws_step, "center");
  }

  objGraph.setStroke(Stroke.DOTTED);

  // var vars = ["f0min","f0max","f0now", "f1min","f1max","f1now"];
  var v_min = Math.min(vals[0],vals[3]);
  var v_max = Math.max(vals[1],vals[4]);

// alert(vals);
  var dy = GRAPH_HEIGHT - MARGIN_BOTTOM - MARGIN_TOP;
  var dv = v_max - v_min;
  for(var v=v_min;v<=v_max;v+=dv/5) { 
    var y = GRAPH_HEIGHT - MARGIN_BOTTOM - Math.round( (v-v_min)*dy/dv );
    objGraph.setColor("#e9e9e9");
    objGraph.drawLine(x0,y,x1,y);
    objGraph.setColor("navy");
    objGraph.drawStringRect(f2s(1,v),3,y-6,MARGIN_LEFT-6,"right");
    objGraph.setColor("red");
    objGraph.drawStringRect(f2s(1,v),GRAPH_WIDTH-MARGIN_RIGHT+3,y-6,MARGIN_RIGHT-6,"left");
  }

  objGraph.setStroke(1);
  objGraph.setColor("#000000");
  objGraph.drawRect(MARGIN_LEFT,MARGIN_TOP,GRAPH_WIDTH-MARGIN_LEFT-MARGIN_RIGHT,GRAPH_HEIGHT-MARGIN_TOP-MARGIN_BOTTOM);


  // Now plot
  objGraph.setColor("navy");
  objGraph.drawPolyline(Xpoints,Y2points);

  objGraph.setColor("red");
  objGraph.drawPolyline(Xpoints,Ypoints);

  objGraph.paint();
}



function updateTable() { 
  var el = document.getElementById("staid");
  el.innerHTML = "SIO01";

  // Reset the min/max/now values
  vals = [ "NaN",   "NaN",   "NaN",    "NaN",   "NaN",   "NaN" ];

  // Reset the min/max timestamps 
  ts_min = "NaN";
  ts_max = "NaN";

  // Reset the Graph points
  Xpoints = [];  // reset times
  Ypoints = [];  // reset flow rate
  Y2points = []; // reset storm flow


  if( rawData.length < 1 ) { 
    // no points? nothing to plot
    return;
  }


  // Scan from zero to max_points
  var p0 = Math.max(0, rawData.length-MAX_POINTS);
  for(var i=p0;i<rawData.length;i++) { 
    Xpoints.push( rawData[i]["timestamp"] );
    Ypoints.push( rawData[i]["flowrate"] );
    Y2points.push( rawData[i]["stormflow"] );

    var ts = rawData[i]["timestamp"];
    var f0 = Number(rawData[i]["flowrate"]);
    f1 = Number(rawData[i]["stormflow"]);

    // min flow rate
    if( f0 < vals[0] || vals[0] == "NaN" ) { 
      vals[0] = f0;
    }
    // max flow rate
    if( f0 > vals[1] || vals[1] == "NaN" ) { 
      vals[1] = f0;
    }

    // max timestamp, most recent values
    if( ts > ts_max || ts_max == "NaN" ) { 
      vals[2] = f0;
      vals[5] = f1;
      ts_max = ts;
    }
    // min timestamp
    if( ts < ts_min || ts_min == "NaN" ) { 
      ts_min = ts;
    }

    // min storm flow rate
    if( f1 < vals[3] || vals[3] == "NaN" ) { 
      vals[3] = f1;
    }
    // max storm flow rate
    if( f1 > vals[4] || vals[4] == "NaN" ) { 
      vals[4] = f1;
    }
  }

  // Write to table
  for(var i=0;i<vars.length;i++) { 
    el = document.getElementById(vars[i]);
    if( !el ) { 
      continue;
    }
    el.innerHTML = vals[i];
  }


  /*
  var x_min = Xpoints.min();
  var x_max = Xpoints.max();
  var y_min = Math.min( Ypoints.min(), Y2points.min() );
  var y_max = Math.max( Ypoints.max(), Y2points.max() );
  */
  var x_min = ts_min;
  var x_max = ts_max;
  var y_min = Math.min( vals[0], vals[3] );
  var y_max = Math.max( vals[1], vals[4] );

  var dx = x_max-x_min;
  var dy = y_max-y_min;

  var w = GRAPH_WIDTH - MARGIN_LEFT - MARGIN_RIGHT;
  var h = GRAPH_HEIGHT - MARGIN_TOP - MARGIN_BOTTOM;
  for(var i=0;i<Xpoints.length;i++) { 
    Xpoints[i]  = MARGIN_LEFT + Math.round( w*(Xpoints[i]-x_min)/dx );
    Ypoints[i]  = GRAPH_HEIGHT - ( MARGIN_TOP + Math.round( h*(Ypoints[i]-y_min)/dy ));
    Y2points[i] = GRAPH_HEIGHT - ( MARGIN_TOP + Math.round( h*(Y2points[i]-y_min)/dy ));
  }
}



function updateData() { 
  if( tUpdate ) { 
    window.clearTimeout(tUpdate);
  }
  
  // Do I need a lot of data, or just a little?
  var n = ( rawData.length < 1 ) ? MAX_POINTS : SAMPLES_PER_QUERY;

  // ajax request for all data since last update.
  // or more simply, just the last data point.
  ajaxRequest('query.php?sta=SIO01&ord=asc&n='+n,function (data,code) {
    var dom = parseXml(data);
    var recs = dom.getElementsByTagName("record");
    for(var i=0;i<recs.length;i++) { 
      var recObj = {};
      var children = recs[i].childNodes;
      for(var j=0;j<children.length;j++) { 
        if( typeof(children[j].tagName) == "undefined" ) { 
          continue;
        }
        var tn = String(children[j].tagName).toLowerCase();
        recObj[tn] = children[j].childNodes[0].nodeValue;
      }
      rawData.push(recObj);
    }
    updateTable();
    updateGraph();
  });

  tUpdate = window.setTimeout("updateData()",SAMPLES_PER_QUERY*SAMPLE_PERIOD*1000);
}


function img_customUrl() { 
  var frm = document.forms[0];
  var t0 = frm["t0"].value;
  var t1 = frm["t1"].value;
  var tz = frm["tz"].value;
  var type = "day";
  for(var i=0,n=frm["type"].length;i<n;i++) { 
    if( frm["type"][i].checked ) { 
      type = frm["type"][i].value;
      break;
    }
  }
  return "/projects/outfalls/sio/tsplot.php?t0="+
    encodeURI(t0)+"&t1="+encodeURI(t1)+"&tz="+
    encodeURI(tz)+"&type="+encodeURI(type)+"&fmt=xml";
}
  
function frm_blur(srcEl) { 
  img_showUrl(img_customUrl());
}

function img_defaultUrl() { 
  var d = new Date();
  var t1 = Math.round(d.getTime()/1000);
  var t0 = t1-(86400*16);
  return "/projects/outfalls/sio/tsplot.php?t0="+
    encodeURI(t0)+"&t1="+encodeURI(t1)+
    "&tz=UTC&type=hour&fmt=xml";
}

function img_showUrl(url) { 
  var ic = document.getElementById("imgContainer");
  if( !ic ) { 
    return;
  }
  var ims = ic.getElementsByTagName("img");
  if(ims.length < 1 ) { 
    ic.style.display = "none";
    return;
  }
  ims[0].src = url;
  ims[0].onload = function() { 
    ic.style.display = "block";
  }
}

function initialize() { 
  // calculatePlotSize("updatingPlot");
  objGraph = new jsGraphics("updatingPlot");
  updateData();
  img_showUrl(img_defaultUrl());
}

function doUnload() { 
  delete(Xpoints);
  delete(Ypoints);
  delete(objGraphics);
  delete(rawData);
  if( tUpdate ) { 
    window.clearTimeout(tUpdate);
  }
}

window.onload = initialize;
window.onunload = doUnload;
