/* 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/. */
/* import-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals DetailsSubview */
"use strict";

/**
 * FlameGraph view containing a pyramid-like visualization of memory allocation
 * sites, controlled by DetailsView.
 */
var MemoryFlameGraphView = Heritage.extend(DetailsSubview, {

  shouldUpdateWhileMouseIsActive: true,

  rerenderPrefs: [
    "invert-flame-graph",
    "flatten-tree-recursion",
    "show-idle-blocks"
  ],

  /**
   * Sets up the view with event binding.
   */
  initialize: Task.async(function* () {
    DetailsSubview.initialize.call(this);

    this.graph = new FlameGraph($("#memory-flamegraph-view"));
    this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
    this.graph.setTheme(PerformanceController.getTheme());
    yield this.graph.ready();

    this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
    this._onThemeChanged = this._onThemeChanged.bind(this);

    PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
    this.graph.on("selecting", this._onRangeChangeInGraph);
  }),

  /**
   * Unbinds events.
   */
  destroy: Task.async(function* () {
    DetailsSubview.destroy.call(this);

    PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
    this.graph.off("selecting", this._onRangeChangeInGraph);

    yield this.graph.destroy();
  }),

  /**
   * Method for handling all the set up for rendering a new flamegraph.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let recording = PerformanceController.getCurrentRecording();
    let duration = recording.getDuration();
    let allocations = recording.getAllocations();

    let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
    let data = FlameGraphUtils.createFlameGraphDataFromThread(thread, {
      invertStack: PerformanceController.getOption("invert-flame-graph"),
      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
      showIdleBlocks: PerformanceController.getOption("show-idle-blocks")
                      && L10N.getStr("table.idle")
    });

    this.graph.setData({ data,
      bounds: {
        startTime: 0,
        endTime: duration
      },
      visible: {
        startTime: interval.startTime || 0,
        endTime: interval.endTime || duration
      }
    });

    this.emit(EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
  },

  /**
   * Fired when a range is selected or cleared in the FlameGraph.
   */
  _onRangeChangeInGraph: function () {
    let interval = this.graph.getViewRange();

    // Squelch rerendering this view when we update the range here
    // to avoid recursion, as our FlameGraph handles rerendering itself
    // when originating from within the graph.
    this.requiresUpdateOnRangeChange = false;
    OverviewView.setTimeInterval(interval);
    this.requiresUpdateOnRangeChange = true;
  },

  /**
   * Called whenever a pref is changed and this view needs to be rerendered.
   */
  _onRerenderPrefChanged: function () {
    let recording = PerformanceController.getCurrentRecording();
    let allocations = recording.getAllocations();
    let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
    FlameGraphUtils.removeFromCache(thread);
  },

  /**
   * Called when `devtools.theme` changes.
   */
  _onThemeChanged: function (_, theme) {
    this.graph.setTheme(theme);
    this.graph.refresh({ force: true });
  },

  toString: () => "[object MemoryFlameGraphView]"
});

EventEmitter.decorate(MemoryFlameGraphView);
