/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";

// React & Redux
const {
  createClass,
  createFactory,
  DOM: dom,
  PropTypes
} = require("devtools/client/shared/vendor/react");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button"));
const MessageIndent = createFactory(require("devtools/client/webconsole/new-console-output/components/message-indent").MessageIndent);
const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon"));
const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat"));
const FrameView = createFactory(require("devtools/client/shared/components/frame"));
const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));

const Message = createClass({
  displayName: "Message",

  propTypes: {
    open: PropTypes.bool,
    collapsible: PropTypes.bool,
    collapseTitle: PropTypes.string,
    source: PropTypes.string.isRequired,
    type: PropTypes.string.isRequired,
    level: PropTypes.string.isRequired,
    indent: PropTypes.number.isRequired,
    topLevelClasses: PropTypes.array.isRequired,
    messageBody: PropTypes.any.isRequired,
    repeat: PropTypes.any,
    frame: PropTypes.any,
    attachment: PropTypes.any,
    stacktrace: PropTypes.any,
    messageId: PropTypes.string,
    scrollToMessage: PropTypes.bool,
    exceptionDocURL: PropTypes.string,
    serviceContainer: PropTypes.shape({
      emitNewMessage: PropTypes.func.isRequired,
      onViewSourceInDebugger: PropTypes.func.isRequired,
      sourceMapService: PropTypes.any,
    }),
    notes: PropTypes.arrayOf(PropTypes.shape({
      messageBody: PropTypes.string.isRequired,
      frame: PropTypes.any,
    })),
  },

  getDefaultProps: function () {
    return {
      indent: 0
    };
  },

  componentDidMount() {
    if (this.messageNode) {
      if (this.props.scrollToMessage) {
        this.messageNode.scrollIntoView();
      }
      // Event used in tests. Some message types don't pass it in because existing tests
      // did not emit for them.
      if (this.props.serviceContainer) {
        this.props.serviceContainer.emitNewMessage(this.messageNode, this.props.messageId);
      }
    }
  },

  onLearnMoreClick: function () {
    let {exceptionDocURL} = this.props;
    this.props.serviceContainer.openLink(exceptionDocURL);
  },

  render() {
    const {
      messageId,
      open,
      collapsible,
      collapseTitle,
      source,
      type,
      level,
      indent,
      topLevelClasses,
      messageBody,
      frame,
      stacktrace,
      serviceContainer,
      dispatch,
      exceptionDocURL,
      notes,
    } = this.props;

    topLevelClasses.push("message", source, type, level);
    if (open) {
      topLevelClasses.push("open");
    }

    const icon = MessageIcon({level});

    // Figure out if there is an expandable part to the message.
    let attachment = null;
    if (this.props.attachment) {
      attachment = this.props.attachment;
    } else if (stacktrace) {
      const child = open ? StackTrace({
        stacktrace: stacktrace,
        onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger
      }) : null;
      attachment = dom.div({ className: "stacktrace devtools-monospace" }, child);
    }

    // If there is an expandable part, make it collapsible.
    let collapse = null;
    if (collapsible) {
      collapse = CollapseButton({
        open,
        title: collapseTitle,
        onClick: function () {
          if (open) {
            dispatch(actions.messageClose(messageId));
          } else {
            dispatch(actions.messageOpen(messageId));
          }
        },
      });
    }

    let notesNodes;
    if (notes) {
      notesNodes = notes.map(note => dom.span(
        { className: "message-flex-body error-note" },
        dom.span({ className: "message-body devtools-monospace" },
          "note: " + note.messageBody
        ),
        dom.span({ className: "message-location devtools-monospace" },
          note.frame ? FrameView({
            frame: note.frame,
            onClick: serviceContainer
              ? serviceContainer.onViewSourceInDebugger
              : undefined,
            showEmptyPathAsHost: true,
            sourceMapService: serviceContainer
              ? serviceContainer.sourceMapService
              : undefined
          }) : null
        )));
    } else {
      notesNodes = [];
    }

    const repeat = this.props.repeat ? MessageRepeat({repeat: this.props.repeat}) : null;

    // Configure the location.
    const location = dom.span({ className: "message-location devtools-monospace" },
      frame ? FrameView({
        frame,
        onClick: serviceContainer ? serviceContainer.onViewSourceInDebugger : undefined,
        showEmptyPathAsHost: true,
        sourceMapService: serviceContainer ? serviceContainer.sourceMapService : undefined
      }) : null
    );

    let learnMore;
    if (exceptionDocURL) {
      learnMore = dom.a({
        className: "learn-more-link webconsole-learn-more-link",
        title: exceptionDocURL.split("?")[0],
        onClick: this.onLearnMoreClick,
      }, `[${l10n.getStr("webConsoleMoreInfoLabel")}]`);
    }

    return dom.div({
      className: topLevelClasses.join(" "),
      ref: node => {
        this.messageNode = node;
      }
    },
      // @TODO add timestamp
      MessageIndent({indent}),
      icon,
      collapse,
      dom.span({ className: "message-body-wrapper" },
        dom.span({ className: "message-flex-body" },
          dom.span({ className: "message-body devtools-monospace" },
            messageBody,
            learnMore
          ),
          repeat,
          location
        ),
        attachment,
        ...notesNodes
      )
    );
  }
});

module.exports = Message;
