/* 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 { Cc, Ci, Cu, Cr } = require("chrome");

loader.lazyRequireGetter(this, "Services");
loader.lazyRequireGetter(this, "promise");

loader.lazyImporter(this, "FileUtils",
  "resource://gre/modules/FileUtils.jsm");
loader.lazyImporter(this, "NetUtil",
  "resource://gre/modules/NetUtil.jsm");

// This identifier string is used to tentatively ascertain whether or not
// a JSON loaded from disk is actually something generated by this tool.
// It isn't, of course, a definitive verification, but a Good Enough™
// approximation before continuing the import. Don't localize this.
const PERF_TOOL_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
const PERF_TOOL_SERIALIZER_VERSION = 1;

/**
 * Helpers for importing/exporting JSON.
 */
let PerformanceIO = {
  /**
   * Gets a nsIScriptableUnicodeConverter instance with a default UTF-8 charset.
   * @return object
   */
  getUnicodeConverter: function() {
    let className = "@mozilla.org/intl/scriptableunicodeconverter";
    let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
    converter.charset = "UTF-8";
    return converter;
  },

  /**
   * Saves a recording as JSON to a file. The provided data is assumed to be
   * acyclical, so that it can be properly serialized.
   *
   * @param object recordingData
   *        The recording data to stream as JSON.
   * @param nsILocalFile file
   *        The file to stream the data into.
   * @return object
   *         A promise that is resolved once streaming finishes, or rejected
   *         if there was an error.
   */
  saveRecordingToFile: function(recordingData, file) {
    let deferred = promise.defer();

    recordingData.fileType = PERF_TOOL_SERIALIZER_IDENTIFIER;
    recordingData.version = PERF_TOOL_SERIALIZER_VERSION;

    let string = JSON.stringify(recordingData);
    let inputStream = this.getUnicodeConverter().convertToInputStream(string);
    let outputStream = FileUtils.openSafeFileOutputStream(file);

    NetUtil.asyncCopy(inputStream, outputStream, deferred.resolve);
    return deferred.promise;
  },

  /**
   * Loads a recording stored as JSON from a file.
   *
   * @param nsILocalFile file
   *        The file to import the data from.
   * @return object
   *         A promise that is resolved once importing finishes, or rejected
   *         if there was an error.
   */
  loadRecordingFromFile: function(file) {
    let deferred = promise.defer();

    let channel = NetUtil.newChannel(file);
    channel.contentType = "text/plain";

    NetUtil.asyncFetch(channel, (inputStream, status) => {
      try {
        let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
        var recordingData = JSON.parse(string);
      } catch (e) {
        deferred.reject(new Error("Could not read recording data file."));
        return;
      }
      if (recordingData.fileType != PERF_TOOL_SERIALIZER_IDENTIFIER) {
        deferred.reject(new Error("Unrecognized recording data file."));
        return;
      }
      if (recordingData.version != PERF_TOOL_SERIALIZER_VERSION) {
        deferred.reject(new Error("Unsupported recording data file version."));
        return;
      }
      deferred.resolve(recordingData);
    });

    return deferred.promise;
  }
};

exports.PerformanceIO = PerformanceIO;
