/* vim: ts=4 sw=4 expandtab cindent
 *
 * BEGIN FLOCK GPL
 *
 * Copyright Flock Inc. 2005-2009
 * http://flock.com
 *
 * This file may be used under the terms of the
 * GNU General Public License Version 2 or later (the "GPL"),
 * http://www.gnu.org/licenses/gpl.html
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * END FLOCK GPL
 */

// FIXME: Stevo - Remove the global flockProfilerDatasource, use the datasource from the tree.

// Quick function for getElementById
// example: $('flockProfilerTree').setAttribute('ref', null);
function $(x) { return document.getElementById(x); }

var DEBUG = true;

// Constants for services we need
const JSPROFILER_CONTRACTID = '@flock.com/js/profiler;1';

const PROFILER_ON = true;
const PROFILER_OFF = false;

var flockProfilerService = null;      // Global profiler service
var flockProfilerDatasource = null;   // Global datasource for when we save
var flockOriginalRefForTree = null;   // Global save for datasource ref for filtering.

/**
 * debug - Wrapper function to print debug statements.
 *
 * @param aString - Message to print to console.
 */
function debug (aString)
{
	if (DEBUG) { dump ("showProfile.js: " + aString + "\n"); }
}

/**
 * init - Sets up the profiler service (see flockJSProfiler.js)
 *
 */
function onload ()
{
  window.sizeToContent();
  flockProfilerService = Components.classes[JSPROFILER_CONTRACTID].getService(Components.interfaces.flockIJSProfiler);
  if (flockProfilerService.isRunning) {
    // Reset all the buttons
    $('profilerStart').setAttribute('disabled','true');
    $('profilerClear').setAttribute('disabled','true');
    $('profilerSave').setAttribute('disabled','true');
    $('profilerStop').removeAttribute('disabled');
  }    
}

/**
 * unload - called when the profile dialog is unloaded.
 *
 */
function onunload ()
{
  debug("Turning off profiler, stopping flockProfilerService.");
  flockProfilerService.stop();
  flockProfilerService = null;
}

/**
 * profilerStart - Start the profiler
 *
 */
function profilerStart ()
{
	if (flockProfilerService)
	{
	  // Reset all the buttons
 		$('profilerStart').setAttribute('disabled','true');
    $('profilerClear').setAttribute('disabled','true');
    $('profilerSave').setAttribute('disabled','true');
		$('profilerStop').removeAttribute('disabled');
	  debug("Turning on profiler.");
    flockProfilerService.start();
	} else {
	  debug("No profiler service available!");
  }
}

/**
 * profilerStop - Stops the profiler and saves the data to a RDFDatasource
 *
 */
function profilerStop ()
{
	if (flockProfilerService)
	{
		$('profilerStop').setAttribute('disabled','true');

    debug("Stopping profiler, and gathering data.");
    flockProfilerService.stop();
  
    flockProfilerDatasource = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
            .createInstance(Components.interfaces.nsIRDFDataSource);
    flockProfilerService.getProfileData(flockProfilerDatasource);
  
    debug("Adding datasource to tree.");
    $('flockProfilerTree').database.AddDataSource(flockProfilerDatasource);
    $('flockProfilerTree').builder.rebuild();

    // Reset all the buttons
		$('profilerStart').removeAttribute('disabled');
    $('profilerSave').removeAttribute('disabled');
		$('profilerClear').removeAttribute('disabled');
	} else {
    debug("No profiler service started!");
  }
}

/**
 * profilerClear - Clears the tree of the datasources
 *
 */
function profilerClear ()
{
  $('flockProfilerTree').setAttribute('ref', null);
  flockProfilerService.stop();
  flockProfilerService.clearProfileData();

  // Reset all the buttons
  $('profilerStop').setAttribute('disabled','true');
  $('profilerSave').setAttribute('disabled','true');
  $('profilerClear').setAttribute('disabled','true');
}

/**
 * profilerSearch - searches for text inside the tree (filters)
 *
 * @param aField - field to filter/search on
 * @param aInput - value to filer/search with
 */
function profilerSearch (aField, aInput) {
  if(!flockOriginalRefForTree) {
      flockOriginalRefForTree = $('flockProfilerTree').getAttribute("ref");
      debug("Old Ref for tree = " + flockOriginalRefForTree);
  }

  if (!aInput)
    $('flockProfilerTree').setAttribute("ref", flockOriginalRefForTree);
  else
    $('flockProfilerTree').setAttribute("ref", "find:datasource=" + flockOriginalRefForTree + "&match=http://flock.com/profiler#functionName&method=contains&text=" + encodeURIComponent(aInput));    
}

/**
 * rdfNodeToString - Converts a RDF node to its string representation
 *
 * @param node The node to convert to string
 * @returns a string if successfull, or '' if unable to convert
 *
 * NOTE: Stevo - could we do this without try/catch blocks?
 */
function rdfNodeToString (node) {

        try { 
            var date = node.QueryInterface (Components.interfaces.nsIRDFDate);
            return date.Value;
        }
        catch (e) { }

        try { 
            var blob = node.QueryInterface (Components.interfaces.nsIRDFBlob);
            return blob.Length;
        }
        catch (e) { }

        try { 
            var int = node.QueryInterface (Components.interfaces.nsIRDFInt);
            return int.Value;
        }
        catch (e) { }

        try { 
            var literal = node.QueryInterface (Components.interfaces.nsIRDFLiteral);
            return '"'+literal.Value+'"';
        }
        catch (e) { }
      
        return '';
}

/**
 * exportDataCsv - This is suppose to export a the profiler RDFDataSource to a csv file
 *
 * @param ds - RDF Datasource to export
 * @param toFile - nsILocalFile to write csv data to
 */
function exportDataCsv (ds, toFile) {
    var outString = "";
    var outputFile = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
    outputFile.init(toFile, 0x04 | 0x08 | 0x10, 0644, 0 );
    var resources = ds.GetAllResources ();
    outString = "FileName, FunctionName, LineNumber, LineExtent, CallCount, MaxRecurseDepth, MinExecutionTime, MaxExecutionTime, TotalExecutionTime, MinOwnExecutionTime, MaxOwnExecutionTime, TotalOwnExecutionTime\n";
    outputFile.write(outString, outString.length);
    while (resources.hasMoreElements ()) {
      outString = "";
      var resource = resources.getNext ();
      var arcs = ds.ArcLabelsOut (resource);
      while (arcs.hasMoreElements ()) {
          var arc = arcs.getNext ().QueryInterface (Components.interfaces.nsIRDFResource);
          var targets1 = ds.GetTargets (resource, arc, true);
          while (targets1.hasMoreElements ()) {
              var target = targets1.getNext().QueryInterface (Components.interfaces.nsIRDFNode);
              outString += rdfNodeToString(target);
          }
          if (arcs.hasMoreElements ()) {
            outString += ",";
          }
      }
      outString += "\n";
      outputFile.write(outString, outString.length);
    }
    outputFile.close();
}

/**
 * exportDataXml - Exports a RDFDataSource to an xml file
 *
 * @param ds Datasource to export
 * @param outFile nsILocalFile to write xml to
 */
function exportDataXml (ds, outFile){
  var serializer=Components.classes["@mozilla.org/rdf/xml-serializer;1"]
    .createInstance(Components.interfaces.nsIRDFXMLSerializer);
  serializer.init(ds);
  
  var outputStream = { 
    data: "",
    close : function (){},
    flush : function (){},
    write : function (buffer,count) {
      data += buffer;
      return count;
    },
    writeFrom : function (stream,count) {
      
    },
    isNonBlocking: false
  };
  
  serializer.QueryInterface(Components.interfaces.nsIRDFXMLSource).Serialize(outputStream);
  outputStream.close();
  var summ=outputStream.data;

  var filoutputStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
    .createInstance( Components.interfaces.nsIFileOutputStream );
  filoutputStream.init( outFile, 0x04 | 0x08, 420, 0 );
  filoutputStream.write(summ, summ.length);
  filoutputStream.close();
}

/**
 * exportDataRdf - Exports a RDFDataSource to an rdf file
 *
 * @param ds Datasource to export
 * @param outFile File to export to
 */
function exportDataRdf (ds, outFile) {
  ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  ds.FlushTo("file://" + outFile.path);
}

/**
 * profilerSave - Saves the data collected from the profiler, called
 * from the Save button.
 *
 */
function profilerSave ()
{
  // Get filename from user
  const nsIFilePicker = Components.interfaces.nsIFilePicker;

  var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  fp.init(window, "Save As", nsIFilePicker.modeSave);
  fp.defaultString = "profile.csv";

  var rv = fp.show();
  if (rv == nsIFilePicker.returnOK)  {
    // Use extension to determin how to export
    // Supported extensions XML, CSV, RDF
    var fileName = fp.file.path;
  
    var fileExt = fileName.substring(fileName.lastIndexOf(".") + 1);
    if (fileExt.length > 0) {
      switch (fileExt.toLowerCase()) {
        case "xml":
          exportDataXml(flockProfilerDatasource, fp.file);
          break;
        case "csv":
          exportDataCsv(flockProfilerDatasource, fp.file);
          break;
        case "rdf":
        default:
          exportDataRdf(flockProfilerDatasource, fp.file);
          break;
      }
    } else {
      exportDataRdf(flockProfilerDatasource, fp.file);
    }
  }
}
