/**
 * Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

var testGenerator = testSteps();

function testSteps()
{
  const name = this.window ? window.location.pathname : "Splendid Test";
  let openRequest = mozIndexedDB.open(name, 1);
  openRequest.onerror = errorHandler;
  openRequest.onupgradeneeded = grabEventAndContinueHandler;
  openRequest.onsuccess = unexpectedSuccessHandler;
  let event = yield;
  let db = event.target.result;
  let trans = event.target.transaction;

  for each (let autoincrement in [true, false]) {
    for each (let keypath in [false, true, "missing", "invalid"]) {
      for each (let method in ["put", "add"]) {
        for each (let explicit in [true, false, undefined, "invalid"]) {
          for each (let existing in [true, false]) {
            let speccedNoKey = (keypath == false || keypath == "missing") &&
                               !explicit;

            // We can't do 'existing' checks if we use autogenerated key
            if (speccedNoKey && autoincrement && existing) {
              continue;
            }

            // Create store
            if (db.objectStoreNames.contains("mystore"))
              db.deleteObjectStore("mystore");
            let store = db.createObjectStore("mystore",
                                             { autoIncrement: autoincrement,
                                               keyPath: (keypath ? "id" : null) });

            test = " for test " + JSON.stringify({ autoincrement: autoincrement,
                                                   keypath: keypath,
                                                   method: method,
                                                   explicit: explicit === undefined ? "undefined" : explicit,
                                                   existing: existing });

            // Insert "existing" data if needed
            if (existing) {
              if (keypath)
                store.add({ existing: "data", id: 5 }).onsuccess = grabEventAndContinueHandler;
              else
                store.add({ existing: "data" }, 5).onsuccess = grabEventAndContinueHandler;

              let e = yield;
              is(e.type, "success", "success inserting existing" + test);
              is(e.target.result, 5, "inserted correct key" + test);
            }

            // Set up value to be inserted
            let value = { theObj: true };
            if (keypath === true) {
              value.id = 5;
            }
            else if (keypath === "invalid") {
              value.id = /x/;
            }

            // Which arguments are passed to function
            args = [value];
            if (explicit === true) {
              args.push(5);
            }
            else if (explicit === undefined) {
              args.push(undefined);
            }
            else if (explicit === "invalid") {
              args.push(/x/);
            }

            let expected = expectedResult(method, keypath, explicit, autoincrement, existing);

            let valueJSON = JSON.stringify(value);

            ok(true, "making call" + test);

            // Make function call for throwing functions
            if (expected === "throw") {
              try {
                store[method].apply(store, args);
                ok(false, "should have thrown" + test);
              }
              catch (ex) {
                ok(true, "did throw" + test);
                ok(ex instanceof IDBDatabaseException, "Got a IDBDatabaseException" + test);
                is(ex.code, IDBDatabaseException.DATA_ERR, "expect a DATA_ERR" + test);
                is(JSON.stringify(value), valueJSON, "call didn't modify value" + test);
              }
              continue;
            }

            // Make non-throwing function call
            let req = store[method].apply(store, args);
            is(JSON.stringify(value), valueJSON, "call didn't modify value" + test);

            req.onsuccess = req.onerror = grabEventAndContinueHandler;
            let e = yield;

            // Figure out what key we used
            let key = 5;
            if (autoincrement && speccedNoKey) {
              key = 1;
            }

            // Adjust value if expected
            if (autoincrement && keypath && speccedNoKey) {
              value.id = key;
            }

            // Check result
            if (expected === "error") {
              is(e.type, "error", "write should fail" + test);
              e.preventDefault();
              e.stopPropagation();
              continue;
            }

            is(e.type, "success", "write should succeed" + test);
            is(e.target.result, key, "write should return correct key" + test);

            store.get(key).onsuccess = grabEventAndContinueHandler;
            e = yield;
            is(e.type, "success", "read back should succeed" + test);
            is(JSON.stringify(e.target.result),
               JSON.stringify(value),
               "read back should return correct value" + test);
          }
        }
      }
    }
  }

  
  function expectedResult(method, keypath, explicit, autoincrement, existing) {
    if (keypath && explicit)
      return "throw";
    if (!keypath && !explicit && !autoincrement)
      return "throw";
    if (keypath == "invalid")
      return "throw";
    if (keypath == "missing" && !autoincrement)
      return "throw";
    if (explicit == "invalid")
      return "throw";

    if (method == "add" && existing)
      return "error";

    return "success";
  }

  openRequest.onsuccess = grabEventAndContinueHandler;
  yield;

  finishTest();
  yield;
}
