/* 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 { Cu } = require("chrome");
const { Promise: promise } =
  Cu.import("resource://gre/modules/Promise.jsm", {});

// Lazily require encoder and decoder in case only one is needed
Object.defineProperty(this, "Encoder", {
  get: () => require("./encoder/index").Encoder
});
Object.defineProperty(this, "QRRSBlock", {
  get: () => require("./encoder/index").QRRSBlock
});
Object.defineProperty(this, "QRErrorCorrectLevel", {
  get: () => require("./encoder/index").QRErrorCorrectLevel
});
Object.defineProperty(this, "decoder", {
  get: () => {
    // Some applications don't ship the decoder, see moz.build
    try {
      return require("./decoder/index");
    } catch(e) {
      return null;
    }
  }
});

/**
 * There are many "versions" of QR codes, which describes how many dots appear
 * in the resulting image, thus limiting the amount of data that can be
 * represented.
 *
 * The encoder used here allows for versions 1 - 10 (more dots for larger
 * versions).
 *
 * It expects you to pick a version large enough to contain your message.  Here
 * we search for the mimimum version based on the message length.
 * @param string message
 *        Text to encode
 * @param string quality
 *        Quality level: L, M, Q, H
 * @return integer
 */
exports.findMinimumVersion = function(message, quality) {
  let msgLength = message.length;
  let qualityLevel = QRErrorCorrectLevel[quality];
  for (let version = 1; version <= 10; version++) {
    let rsBlocks = QRRSBlock.getRSBlocks(version, qualityLevel);
    let maxLength = rsBlocks.reduce((prev, block) => {
      return prev + block.dataCount;
    }, 0);
    // Remove two bytes to fit header info
    maxLength -= 2;
    if (msgLength <= maxLength) {
      return version;
    }
  }
  throw new Error("Message too large");
};

/**
 * Simple wrapper around the underlying encoder's API.
 * @param string  message
 *        Text to encode
 * @param string  quality (optional)
          Quality level: L, M, Q, H
 * @param integer version (optional)
 *        QR code "version" large enough to contain the message
 * @return object with the following fields:
 *   * src:    an image encoded a data URI
 *   * height: image height
 *   * width:  image width
 */
exports.encodeToDataURI = function(message, quality, version) {
  quality = quality || "H";
  version = version || exports.findMinimumVersion(message, quality);
  let encoder = new Encoder(version, quality);
  encoder.addData(message);
  encoder.make();
  return encoder.createImgData();
};

/**
 * Simple wrapper around the underlying decoder's API.
 * @param string URI
 *        URI of an image of a QR code
 * @return Promise
 *         The promise will be resolved with a string, which is the data inside
 *         the QR code.
 */
exports.decodeFromURI = function(URI) {
  if (!decoder) {
    return promise.reject();
  }
  let deferred = promise.defer();
  decoder.decodeFromURI(URI, deferred.resolve, deferred.reject);
  return deferred.promise;
};

/**
 * Decode a QR code that has been drawn to a canvas element.
 * @param Canvas canvas
 *        <canvas> element to read from
 * @return string
 *         The data inside the QR code
 */
exports.decodeFromCanvas = function(canvas) {
  if (!decoder) {
    throw new Error("Decoder not available");
  }
  return decoder.decodeFromCanvas(canvas);
};
