/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

const { PropertyListUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/PropertyListUtils.sys.mjs"
);

function checkValue(aPropertyListObject, aType, aValue) {
  Assert.equal(PropertyListUtils.getObjectType(aPropertyListObject), aType);
  if (aValue !== undefined) {
    // Perform strict equality checks until Bug 714467 is fixed.
    let strictEqualityCheck = function (a, b) {
      Assert.equal(typeof a, typeof b);
      Assert.equal(a, b);
    };

    if (typeof aPropertyListObject == "object") {
      strictEqualityCheck(aPropertyListObject.valueOf(), aValue.valueOf());
    } else {
      strictEqualityCheck(aPropertyListObject, aValue);
    }
  }
}

function checkLazyGetterValue(aObject, aPropertyName, aType, aValue) {
  let descriptor = Object.getOwnPropertyDescriptor(aObject, aPropertyName);
  Assert.equal(typeof descriptor.get, "function");
  Assert.equal(typeof descriptor.value, "undefined");
  checkValue(aObject[aPropertyName], aType, aValue);
  descriptor = Object.getOwnPropertyDescriptor(aObject, aPropertyName);
  Assert.equal(typeof descriptor.get, "undefined");
  Assert.notEqual(typeof descriptor.value, "undefined");
}

function checkMainPropertyList(aPropertyListRoot) {
  const PRIMITIVE = PropertyListUtils.TYPE_PRIMITIVE;

  checkValue(aPropertyListRoot, PropertyListUtils.TYPE_DICTIONARY);

  // Check .has()
  Assert.ok(aPropertyListRoot.has("Boolean"));
  Assert.ok(!aPropertyListRoot.has("Nonexistent"));

  checkValue(aPropertyListRoot.get("Boolean"), PRIMITIVE, false);

  let array = aPropertyListRoot.get("Array");
  checkValue(array, PropertyListUtils.TYPE_ARRAY);
  Assert.equal(array.length, 8);

  // Test both long and short values, since binary property lists store
  // long values a little bit differently (see readDataLengthAndOffset).

  // Short ASCII string
  checkLazyGetterValue(array, 0, PRIMITIVE, "abc");
  // Long ASCII string
  checkLazyGetterValue(array, 1, PRIMITIVE, new Array(1001).join("a"));
  // Short unicode string
  checkLazyGetterValue(array, 2, PRIMITIVE, "\u05D0\u05D0\u05D0");
  // Long unicode string
  checkLazyGetterValue(array, 3, PRIMITIVE, new Array(1001).join("\u05D0"));
  // Unicode surrogate pair
  checkLazyGetterValue(
    array,
    4,
    PRIMITIVE,
    "\uD800\uDC00\uD800\uDC00\uD800\uDC00"
  );

  // Date
  checkLazyGetterValue(
    array,
    5,
    PropertyListUtils.TYPE_DATE,
    new Date("2011-12-31T11:15:23Z")
  );

  // Data
  checkLazyGetterValue(array, 6, PropertyListUtils.TYPE_UINT8_ARRAY);
  let dataAsString = Array.from(array[6])
    .map(b => String.fromCharCode(b))
    .join("");
  Assert.equal(dataAsString, "2011-12-31T11:15:33Z");

  // Dict
  let dict = array[7];
  checkValue(dict, PropertyListUtils.TYPE_DICTIONARY);
  checkValue(dict.get("Negative Number"), PRIMITIVE, -400);
  checkValue(dict.get("Real Number"), PRIMITIVE, 2.71828183);
  checkValue(
    dict.get("Big Int"),
    PropertyListUtils.TYPE_INT64,
    "9007199254740993"
  );
  checkValue(
    dict.get("Negative Big Int"),
    PropertyListUtils.TYPE_INT64,
    "-9007199254740993"
  );
}

function readPropertyList(aFile, aCallback) {
  PropertyListUtils.read(aFile, function (aPropertyListRoot) {
    // Null root indicates failure to read property list.
    // Note: It is important not to run do_check_n/eq directly on Dict and array
    // objects, because it cases their toString to get invoked, doing away with
    // all the lazy getter we'd like to test later.
    Assert.notStrictEqual(aPropertyListRoot, null);
    aCallback(aPropertyListRoot);
    run_next_test();
  });
}

function run_test() {
  add_test(
    readPropertyList.bind(
      this,
      do_get_file("propertyLists/bug710259_propertyListBinary.plist", false),
      checkMainPropertyList
    )
  );
  add_test(
    readPropertyList.bind(
      this,
      do_get_file("propertyLists/bug710259_propertyListXML.plist", false),
      checkMainPropertyList
    )
  );
  run_next_test();
}
