// 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

const DEFAULT_DUMP_FILENAME = "rdf-data-debug.rdf";
const DEFAULT_TEST_FILENAME = "rdf-test.txt";

const CC = Components.classes;
const CI = Components.interfaces;

/* from nspr's prio.h */
const PR_RDONLY      = 0x01;
const PR_WRONLY      = 0x02;
const PR_RDWR        = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND      = 0x10;
const PR_TRUNCATE    = 0x20;
const PR_SYNC        = 0x40;
const PR_EXCL        = 0x80;

const RDFS = CC["@mozilla.org/rdf/rdf-service;1"]
             .getService(CI.nsIRDFService);
const NUM_TESTS = 100;

var _coop = CC["@flock.com/singleton;1"]
            .getService(CI.flockISingleton)
            .getSingleton("chrome://flock/content/common/load-faves-coop.js")
            .wrappedJSObject;

var ios = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService);

var loaded = false;
var mode;

function doClick(func, aDS) {
  var browser = document.getElementById("rdfviewBrowser");
  browser.setAttribute("src", "about:blank");
  
  var profileDir = CC["@mozilla.org/file/directory_service;1"]
                   .getService(CI.nsIProperties)
                   .get("ProfD", CI.nsILocalFile);

  var outfile = profileDir.clone();
  if (func == rdf_test) {
    outfile.append(DEFAULT_TEST_FILENAME);
    // re-create the file the first time.
    if (mode != PR_APPEND) {
      mode = PR_APPEND;
      try {
        outfile.remove(false);
      } catch (ex) {
      }
    }
  }
  else {
    outfile.append(DEFAULT_DUMP_FILENAME);
  }
  if (!outfile.exists()) {
    outfile.create(CI.nsIFile.NORMAL_FILE_TYPE, 0600);
  }
  var outStream = CC["@mozilla.org/network/file-output-stream;1"]
                  .createInstance(CI.nsIFileOutputStream);
  // 0600 == -rw------- file permissions
  outStream.init(outfile, PR_WRONLY | PR_CREATE_FILE | mode, 0600, 0);

  if (!aDS) {
    aDS = _coop.datasource;
  }

  func(outStream, aDS);
  outStream.close();
  var ios = CC["@mozilla.org/network/io-service;1"]
                                  .getService(CI.nsIIOService);
  var fileUri = ios.newFileURI(outfile);
  browser.setAttribute("src", "view-source:" + fileUri.spec);
}

function onLoad() {
  if (!loaded ) {
    loaded = true;
    doClick(dump_ds);
  }
}

function unload() {
}

var testedType = _coop.mTypes["http://home.netscape.com/NC-rdf#MediaQuery"];


function rdf_test(outStream, aDS) {
  addLabel(outStream, "Starting...............\n");

  testGetResource(outStream);  
  testGetResource(outStream);  

  selectionSort(outStream)
  selectionSort(outStream)

  testAsserts(outStream, aDS);

  testCoopCreateDeletes(outStream);
  testCoopCreateDeletes(outStream);

  testUnobservedAsserts(outStream);
  testUnobservedAsserts(outStream);
  
  testCoopGetFromResource(outStream);
  testCoopGetFromResource(outStream);
  testCoopGetFromResource(outStream, true);
  testCoopGetFromResource(outStream, true);

  startTestBlock();
  aDS.QueryInterface(CI.nsIRDFRemoteDataSource);
  aDS.Flush();
  addTestLog(outStream, "Done RDF flush.");
}

function testGetResource(outStream) {
  var stubResource = "http://flock.com/rdf#MyLongResourceWhichWillMakeMeHappy";
  var bar = null;
  startTestBlock();
  for (var i = 0; i < NUM_TESTS * 10; i++) {
    bar = stubResource + i;
    var foo = RDFS.GetResource(bar);
  }
  addTestLog(outStream, "Finished looping GetResource test, " + i + " tests");
}

function testAsserts(outStream, aDS) {
  var myResources = [];

  startTestBlock();
  var beginTime4testType = beginTime;
  // make sure "CoopType" is first for the observers
  var stubCoopType = RDFS.GetResource("http://flock.com/rdf#CoopType");
  for (var i = NUM_TESTS; i > 0; i--) {
    var myResource = RDFS.GetResource(RDFS.GetAnonymousResource().ValueUTF8);
    myResources.push(myResource);
    aDS.Assert(myResource, stubCoopType, testedType.mTypeResource, true);
  }
  addTestLog(outStream, "Finished looping Assert test for CoopType=" 
                                                      + testedType.mType);

  for (fieldName in testedType.fields)
  {
    startTestBlock();
    var value = testedType.field_defaults[fieldName];
    if (value != null) {
      for (var i in myResources) {
        var stubPredicate = testedType.fields[fieldName]
        var type = testedType.field_types[fieldName];
        aDS.Assert(myResources[i], stubPredicate, 
                 type ? type.toRdf(value, aDS) : RDFS.GetLiteral(value), true);
      }
      addTestLog(outStream, "Finished looping Assert test for " + fieldName 
                                                      + "=" + value);
    }
  }

  startTestBlock();
  var midTime = beginTime;
  for (var i in myResources) {
    aDS.Unassert(myResources[i], stubCoopType, testedType.mTypeResource);
  }
  addTestLog(outStream, "Finished looping Unassert test for CoopType=" 
                                                      + testedType.mType);
  
  for (fieldName in testedType.fields)
  {
    startTestBlock();
    var target = null;
    for (var i in myResources) {
      var stubPredicate = testedType.fields[fieldName];
      var r = myResources[i];
      target = aDS.GetTarget(r, stubPredicate, true);
      if (target) {
          aDS.Unassert(r, stubPredicate, target);
      }
    }
    if (target) {
      addTestLog(outStream, "Finished looping Unassert test for " + fieldName 
                                                      + "=" + value);
    }
  }
  beginTime = beginTime4testType; 
  addTestLog(outStream, "Finished looping Assert/Unassert for type " + 
            testedType.mType + " , " + myResources.length + " tests", midTime);
  return aDS;
}


function testUnobservedAsserts(outStream) {
  var myResource = RDFS.GetResource("f:parent");
  var aDS = CC["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
            .createInstance(CI.nsIRDFDataSource);
  
  startTestBlock();
  var stubResource = "f:some";
  var stubPredicate = RDFS.GetResource("f:attitude");
  var bar = null;
  for (var i = 0; i < NUM_TESTS * 10; i++) {
    bar = stubResource + i;
    var foo = RDFS.GetResource(bar);
    aDS.Assert(myResource, stubPredicate, foo, true);
    aDS.Unassert(myResource, stubPredicate, foo);
  }
  addTestLog(outStream, "Finished looping Unobserved Assert/Unassert test, " 
      + i + " tests");
}

function testCoopCreateDeletes(outStream) {
  var aDS = _coop.datasource;
  startTestBlock();
  //aDS.beginUpdateBatch();
  var objs = [];
  for (var i = NUM_TESTS; i > 0; i--)
  {
    objs.push(new testedType.ctor());
  }
  //startTestBlock();
  //aDS.endUpdateBatch();
  //addTestLog(outStream, "Finished endUpdateBatch");

  var midTime = new Date().getTime();
  //aDS.beginUpdateBatch();
  for (var i in objs)
  {
    objs[i].destroy();
  }
  addTestLog(outStream, "Finished looping coop create/delete test, "
      + objs.length + " tests", midTime);
  //startTestBlock();
  //aDS.endUpdateBatch();
  //addTestLog(outStream, "Finished endUpdateBatch");
}

function testCoopGetFromResource(outStream, fast) {
  var aDS = _coop.datasource;
  startTestBlock();
  var obj = new testedType.ctor();

  for (var i = 0; i < NUM_TESTS; i++) {
    var test = _coop.get_from_resource(obj.resource(), obj.id(), fast);
  }
  addTestLog(outStream, "Finished looping coop get from resource test, "
      + i + " tests");
  obj.destroy();
}

var beginTime = new Date().getTime();
function startTestBlock() {
  beginTime = Date.now();
}

function addLabel(outStream, aStr) {
  outStream.write(aStr, aStr.length);
}

function addTestLog(outStream, aStr, midTime) {
  var endTime = new Date().getTime();
  var elapsedTime = "" + (endTime - beginTime);
  while (elapsedTime.length < 5){
    elapsedTime = " " + elapsedTime;
  }
  if (midTime) {
    elapsedTime+= "(" + (midTime-beginTime) + "/" + (endTime-midTime) + ")";
  }
  addLabel(outStream, "Elapsed time:"+ elapsedTime +": " + aStr +"\n");
}

// tests that do not involve XPCOM calls seem to have less 
// variation in elapsed time. Bruno
function selectionSort(outStream) {
  startTestBlock();
  var theArray = [];
  for(var i = 0; i< NUM_TESTS; i++){
    //var c = new Circle(i*NUM_TESTS);
    theArray.push(i*NUM_TESTS);
  }
  var i = 0;
  while (i < theArray.length){
    var m = i;
    var j = i + 1;
    while (j < theArray.length){
	    if (("" + theArray[j]*theArray[j]) > ("" + theArray[m] * theArray[m]))
		    m = j;
	    j = j + 1;
    }
    var t;
    t = theArray[i];
    theArray[i] = theArray[m];
    theArray[m] = t;
    i = i + 1;
  }
  addTestLog(outStream, "Finished selectionSort (no XPCOM calls), "
      + theArray.length + " elements");
}
