/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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";

define(function(require, exports, module) {

// Dependencies
const React = require("react");
const { createFactories } = require("./rep-utils");
const { ObjectBox } = createFactories(require("./object-box"));
const { Caption } = createFactories(require("./caption"));

// Shortcuts
const DOM = React.DOM;

/**
 * Renders an object. An object is represented by a list of its
 * properties enclosed in curly brackets.
 */
const Obj = React.createClass({
  displayName: "Obj",

  render: function() {
    var object = this.props.object;
    var props = this.shortPropIterator(object);

    return (
      ObjectBox({className: "object"},
        DOM.span({className: "objectTitle"}, this.getTitle(object)),
        DOM.span({className: "objectLeftBrace", role: "presentation"}, "{"),
        props,
        DOM.span({className: "objectRightBrace"}, "}")
      )
    )
  },

  getTitle: function() {
    return ""; // Could also be "Object";
  },

  longPropIterator: function (object) {
    try {
      return this.propIterator(object, 100);
    }
    catch (err) {
      console.error(err);
    }
  },

  shortPropIterator: function (object) {
    try {
      return this.propIterator(object, /*could be a pref*/ 3);
    }
    catch (err) {
      console.error(err);
    }
  },

  propIterator: function(object, max) {
    function isInterestingProp(t, value) {
      return (t == "boolean" || t == "number" || (t == "string" && value) ||
        (t == "object" && value && value.toString));
    }

    // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=945377
    if (Object.prototype.toString.call(object) === "[object Generator]") {
      object = Object.getPrototypeOf(object);
    }

    // Object members with non-empty values are preferred since it gives the
    // user a better overview of the object.
    var props = [];
    this.getProps(props, object, max, isInterestingProp);

    if (props.length <= max) {
      // There are not enough props yet (or at least, not enough props to
      // be able to know whether we should print "more..." or not).
      // Let's display also empty members and functions.
      this.getProps(props, object, max, function(t, value) {
        return !isInterestingProp(t, value);
      });
    }

    if (props.length > max) {
      props.pop();
      props.push(Caption({
        key: "more",
        object: Locale.$STR("jsonViewer.reps.more"),
      }));
    }
    else if (props.length > 0) {
      // Remove the last comma.
      props[props.length-1] = React.cloneElement(
        props[props.length-1], { delim: "" });
    }

    return props;
  },

  getProps: function (props, object, max, filter) {
    max = max || 3;
    if (!object) {
      return [];
    }

    var len = 0;
    var mode = this.props.mode;

    try {
      for (var name in object) {
        if (props.length > max) {
          return;
        }

        var value;
        try {
          value = object[name];
        }
        catch (exc) {
          continue;
        }

        var t = typeof(value);
        if (filter(t, value)) {
          props.push(PropRep({
            key: name,
            mode: "short",
            name: name,
            object: value,
            equal: ": ",
            delim: ", ",
            mode: mode,
          }));
        }
      }
    }
    catch (exc) {
    }
  },
});

/**
 * Renders object property, name-value pair.
 */
var PropRep = React.createFactory(React.createClass({
  displayName: "PropRep",

  render: function(){
    var { Rep } = createFactories(require("./rep"));
    var object = this.props.object;
    var mode = this.props.mode;
    return (
      DOM.span({},
        DOM.span({"className": "nodeName"}, this.props.name),
        DOM.span({"className": "objectEqual", role: "presentation"}, this.props.equal),
        Rep({object: object, mode: mode}),
        DOM.span({"className": "objectComma", role: "presentation"}, this.props.delim)
      )
    );
  }
}));

function supportsObject(object, type) {
  return true;
}

// Exports from this module

exports.Obj = {
  rep: Obj,
  supportsObject: supportsObject
};

});
