/* 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) {
  const {
    createFactory,
    Component,
  } = require("resource://devtools/client/shared/vendor/react.js");
  const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
  const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
  const {
    createFactories,
  } = require("resource://devtools/client/shared/react-utils.js");

  const TreeView = createFactory(
    require("resource://devtools/client/shared/components/tree/TreeView.js")
  );
  const { JsonToolbar } = createFactories(
    require("resource://devtools/client/jsonview/components/JsonToolbar.js")
  );

  const {
    MODE,
  } = require("resource://devtools/client/shared/components/reps/reps/constants.js");
  const {
    Rep,
  } = require("resource://devtools/client/shared/components/reps/reps/rep.js");

  const { div } = dom;

  function isObject(value) {
    return Object(value) === value;
  }

  /**
   * This template represents the 'JSON' panel. The panel is
   * responsible for rendering an expandable tree that allows simple
   * inspection of JSON structure.
   */
  class JsonPanel extends Component {
    static get propTypes() {
      return {
        data: PropTypes.oneOfType([
          PropTypes.string,
          PropTypes.array,
          PropTypes.object,
          PropTypes.bool,
          PropTypes.number,
        ]),
        dataSize: PropTypes.number,
        expandedNodes: PropTypes.instanceOf(Set),
        searchFilter: PropTypes.string,
        actions: PropTypes.object,
      };
    }

    constructor(props) {
      super(props);
      this.state = {};
      this.onKeyPress = this.onKeyPress.bind(this);
      this.onFilter = this.onFilter.bind(this);
      this.renderValue = this.renderValue.bind(this);
      this.renderTree = this.renderTree.bind(this);
    }

    componentDidMount() {
      document.addEventListener("keypress", this.onKeyPress, true);
      document.getElementById("json-scrolling-panel").focus();
    }

    componentWillUnmount() {
      document.removeEventListener("keypress", this.onKeyPress, true);
    }

    onKeyPress() {
      // XXX shortcut for focusing the Filter field (see Bug 1178771).
    }

    onFilter(object) {
      if (!this.props.searchFilter) {
        return true;
      }

      const json = object.name + JSON.stringify(object.value);
      return json.toLowerCase().includes(this.props.searchFilter.toLowerCase());
    }

    renderValue(props) {
      const member = props.member;

      // Hide object summary when non-empty object is expanded (bug 1244912).
      if (isObject(member.value) && member.hasChildren && member.open) {
        return null;
      }

      // Render the value (summary) using Reps library.
      return Rep(
        Object.assign({}, props, {
          cropLimit: 50,
          noGrip: true,
          isInContentPage: true,
        })
      );
    }

    renderTree() {
      // Append custom column for displaying values. This column
      // Take all available horizontal space.
      const columns = [
        {
          id: "value",
          width: "100%",
        },
      ];

      // Render tree component.
      return TreeView({
        object: this.props.data,
        mode: MODE.TINY,
        onFilter: this.onFilter,
        columns,
        renderValue: this.renderValue,
        expandedNodes: this.props.expandedNodes,
      });
    }

    render() {
      let content;
      const data = this.props.data;

      if (!isObject(data)) {
        content = div(
          { className: "jsonPrimitiveValue" },
          Rep({
            object: data,
          })
        );
      } else if (data instanceof Error) {
        content = div({ className: "jsonParseError" }, data + "");
      } else {
        content = this.renderTree();
      }

      return div(
        { className: "jsonPanelBox tab-panel-inner" },
        JsonToolbar({
          actions: this.props.actions,
          dataSize: this.props.dataSize,
        }),
        div(
          {
            className: "panelContent",
            id: "json-scrolling-panel",
            tabIndex: 0,
          },
          content
        )
      );
    }
  }

  // Exports from this module
  exports.JsonPanel = JsonPanel;
});
