text: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  import Ember from 'ember';
  import Converter from 'yarn-ui/utils/converter';
  import ColumnDef from 'em-table/utils/column-definition';
  import TableDefinition from 'em-table/utils/table-definition';
  
  export default Ember.Component.extend({
    tableDefinition: TableDefinition.create({
      searchType: 'manual',
    }),
    graphDrawn: false,
    userInfo: null,
  
    actions: {
      changeViewType(param) {
        this.sendAction("changeViewType", param);
        if (this.get('attemptModel')) {
          this.setAttemptsGridColumnsAndRows();
        }
      }
    },
  
    canvas: {
      svg: undefined,
      h: 0,
      w: 0,
      tooltip: undefined
    },
  
    clusterMetrics: undefined,
    modelArr: [],
    containerIdArr: [],
    colors: d3.scale.category10().range(),
    _selected: undefined,
    gridColumns: [],
    gridRows: [],
    serviceName: undefined,
  
    selected: function() {
      return this._selected;
    }.property(),
  
    tableComponentName: function() {
      return "app-attempt-table";
    }.property(),
  
    setSelected: function(d) {
      var dom;
  
      if (this._selected === d) {
        return;
      }
  
      // restore color
      if (this._selected) {
        dom = d3.select("#timeline-bar-" + this._selected.get("id"));
        dom.attr("fill", this.colors[0]);
      }
  
      this._selected = d;
      this.set("selected", d);
      dom = d3.select("#timeline-bar-" + d.get("id"));
      dom.attr("fill", this.colors[1]);
    },
  
    getPerItemHeight: function() {
      var arrSize = this.modelArr.length;
  
      if (arrSize < 20) {
        return 30;
      } else if (arrSize < 100) {
        return 10;
      } else {
        return 2;
      }
    },
  
    getPerItemGap: function() {
      var arrSize = this.modelArr.length;
  
      if (arrSize < 20) {
        return 5;
      } else if (arrSize < 100) {
        return 1;
      } else {
        return 1;
      }
    },
  
    getCanvasHeight: function() {
      return (this.getPerItemHeight() + this.getPerItemGap()) * this.modelArr.length + 200;
    },
  
    draw: function(start, end) {
      // get w/h of the svg
      var bbox = d3.select("#" + this.get("parent-id"))
        .node()
        .getBoundingClientRect();
      this.canvas.w = bbox.width;
      this.canvas.h = this.getCanvasHeight();
  
      this.canvas.svg = d3.select("#" + this.get("parent-id"))
        .append("svg")
        .attr("width", this.canvas.w)
        .attr("height", this.canvas.h)
        .attr("id", this.get("my-id"));
      this.renderTimeline(start, end);
    },
  
    renderTimeline: function(start, end) {
      var border = 30;
      var singleBarHeight = this.getPerItemHeight();
      var gap = this.getPerItemGap();
      var textWidth = 200;
      /*
       start-time                              end-time
        |--------------------------------------|
           ==============
                  ==============
                          ==============
                                ===============
       */
      var xScaler = d3.scale.linear()
        .domain([start, end])
        .range([0, this.canvas.w - 2 * border - textWidth]);
  
      /*
       * Render frame of timeline view
       */
      this.canvas.svg.append("line")
        .attr("x1", border + textWidth)
        .attr("y1", border - 5)
        .attr("x2", this.canvas.w - border)
        .attr("y2", border - 5)
        .attr("class", "chart");
  
      this.canvas.svg.append("line")
        .attr("x1", border + textWidth)
        .attr("y1", border - 10)
        .attr("x2", border + textWidth)
        .attr("y2", border - 5)
        .attr("class", "chart");
  
      this.canvas.svg.append("line")
        .attr("x1", this.canvas.w - border)
        .attr("y1", border - 10)
        .attr("x2", this.canvas.w - border)
        .attr("y2", border - 5)
        .attr("class", "chart");
  
      this.canvas.svg.append("text")
          .text(Converter.timeStampToDate(start))
          .attr("y", border - 15)
          .attr("x", border + textWidth)
          .attr("class", "bar-chart-text")
          .attr("text-anchor", "left");
  
      this.canvas.svg.append("text")
          .text(Converter.timeStampToDate(end))
          .attr("y", border - 15)
          .attr("x", this.canvas.w - border)
          .attr("class", "bar-chart-text")
          .attr("text-anchor", "end");
  
      // show bar
      var bar = this.canvas.svg.selectAll("bars")
        .data(this.modelArr)
        .enter()
        .append("rect")
        .attr("y", function(d, i) {
          return border + (gap + singleBarHeight) * i;
        })
        .attr("x", function(d) {
          return border + textWidth + xScaler(d.get("startTs"));
        })
        .attr("height", singleBarHeight)
        .attr("fill", function() {
          return this.colors[0];
        }.bind(this))
        .attr("width", function(d) {
          var finishedTs = xScaler(d.get("finishedTs"));
          finishedTs = finishedTs > 0 ? finishedTs : xScaler(end);
          return finishedTs - xScaler(d.get("startTs"));
        })
        .attr("id", function(d) {
          return "timeline-bar-" + d.get("id");
        });
      bar.on("click", function(d) {
        this.setSelected(d);
      }.bind(this));
  
      this.bindTooltip(bar);
  
      if (this.modelArr.length <= 20) {
        // show bar texts
        for (var i = 0; i < this.modelArr.length; i++) {
          this.canvas.svg.append("text")
            .text(this.modelArr[i].get(this.get("label")))
            .attr("y", border + (gap + singleBarHeight) * i + singleBarHeight / 2)
            .attr("x", border)
            .attr("class", "bar-chart-text");
        }
      }
    },
  
    bindTooltip: function(d) {
      d.on("mouseover", function() {
          this.tooltip
            .style("left", (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
        }.bind(this))
        .on("mousemove", function(d) {
          this.tooltip.style("opacity", 0.9);
          this.tooltip.html(d.get("tooltipLabel"))
            .style("left", (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
        }.bind(this))
        .on("mouseout", function() {
          this.tooltip.style("opacity", 0);
        }.bind(this));
    },
  
    initTooltip: function() {
      this.tooltip = d3.select("body")
        .append("div")
        .attr("class", "tooltip")
        .attr("id", "chart-tooltip")
        .style("opacity", 0);
    },
  
    didInsertElement: function() {
      // init model
      this.modelArr = [];
      this.containerIdArr = [];
  
      if (this.get("rmModel")) {
        this.get("rmModel").forEach(function(o) {
          if(!this.modelArr.contains(o)) {
            this.modelArr.push(o);
            this.containerIdArr.push(o.id);
          }
        }.bind(this));
      }
  
      if (this.get("tsModel")) {
        this.get("tsModel").forEach(function(o) {
          if(!this.containerIdArr.contains(o.id)) {
            this.modelArr.push(o);
          }
        }.bind(this));
      }
  
      if (this.modelArr.length === 0) {
        return;
      }
  
      this.modelArr.sort(function(a, b) {
        var tsA = a.get("startTs");
        var tsB = b.get("startTs");
        return tsA - tsB;
      });
  
      if (this.get('attemptModel')) {
        this.setAttemptsGridColumnsAndRows();
      } else {
        this.setContainersGridColumnsAndRows();
      }
    },
  
    didUpdate: function() {
      if (this.get("viewType") === "grid" || this.graphDrawn) {
        return;
      }
  
      this.initTooltip();
  
      var begin = 0;
      if (this.modelArr.length > 0) {
        begin = this.modelArr[0].get("startTs");
      }
      var end = 0;
      for (var i = 0; i < this.modelArr.length; i++) {
        var ts = this.modelArr[i].get("finishedTs");
        if (ts > end) {
          end = ts;
        }
      }
      if (end < begin) {
        end = Date.now();
      }
  
      this.draw(begin, end);
  
      if (this.modelArr.length > 0) {
        this.setSelected(this.modelArr[0]);
      }
  
      this.graphDrawn = true;
    },
  
    setAttemptsGridColumnsAndRows: function() {
      var self = this;
      var columns = [];
      var serviceName = this.get('serviceName');
  
      columns.push({
        id: 'id',
        headerTitle: 'Attempt ID',
        contentPath: 'id',
        cellComponentName: 'em-table-linked-cell',
        minWidth: '300px',
        getCellContent: function(row) {
          var attemptId = row.get('id');
          var query = 'viewType=' + self.get("viewType");
          if (serviceName) {
            query += '&service=' + serviceName;
          }
          return {
            displayText: attemptId,
            href: `#/yarn-app-attempt/${attemptId}?${query}`
          };
        }
      }, {
        id: 'attemptStartedTime',
        headerTitle: 'Started Time',
        contentPath: 'attemptStartedTime'
      }, {
        id: 'finishedTime',
        headerTitle: 'Finished Time',
        contentPath: 'finishedTime',
        getCellContent: function(row) {
          if (row.get('finishedTs')) {
            return row.get('finishedTime');
          }
          return 'N/A';
        }
      }, {
        id: 'elapsedTime',
        headerTitle: 'Elapsed Time',
        contentPath: 'elapsedTime'
      }, {
        id: 'appMasterContainerId',
        headerTitle: 'AM Container ID',
        contentPath: 'appMasterContainerId',
        minWidth: '350px'
      }, {
        id: 'amNodeId',
        headerTitle: 'AM Node ID',
        contentPath: 'amNodeId'
      }, {
        id: 'attemptState',
        headerTitle: 'State',
        contentPath: 'attemptState',
        getCellContent: function(row) {
          var state = row.get('attemptState');
          if (state) {
            return state;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'exposedPorts',
        headerTitle: 'Exposed Ports',
        contentPath: 'exposedPorts',
        getCellContent: function(row) {
          var ports = row.get('exposedPorts');
          if (ports) {
            return ports;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'nodeHttpAddress',
        headerTitle: 'NodeManager Web UI',
        contentPath: 'nodeHttpAddress',
        cellComponentName: 'em-table-html-cell',
        getCellContent: function(row) {
          var address = self.checkHttpProtocol(row.get('nodeHttpAddress'));
          var link = row.get('masterNodeURL');
          if (address) {
            return `<a href="${link}">${address}</a>`;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'logsLink',
        headerTitle: 'Logs',
        contentPath: 'logsLink',
        cellComponentName: 'em-table-html-cell',
        getCellContent: function(row) {
          var containerLogUrl = row.get('appAttemptContainerLogsURL');
          if (containerLogUrl) {
            return `<a href="${containerLogUrl}">Link</a>`;
          } else {
            return 'N/A';
          }
        }
      });
  
      var gridCols = ColumnDef.make(columns);
      this.set('gridColumns', gridCols);
      this.set('gridRows', this.modelArr);
    },
  
    setContainersGridColumnsAndRows: function() {
      var self = this;
      var columns = [];
  
      columns.push({
        id: 'id',
        headerTitle: 'Container ID',
        contentPath: 'id',
        cellComponentName: 'em-table-html-cell',
        minWidth: '350px',
        getCellContent: function(row) {
          var termLink = self.checkHttpProtocol(row.get('nodeHttpAddress'));
          var containerId = row.get('id');
          var requestedUser = self.get('requestedUser');
          return `<a href="${termLink}/terminal/terminal.template?container=${containerId}&user.name=${requestedUser}" target="_blank">${containerId}</a>`;
        }
      }, {
        id: 'startedTime',
        headerTitle: 'Started Time',
        contentPath: 'startedTime'
      }, {
        id: 'finishedTime',
        headerTitle: 'Finished Time',
        contentPath: 'finishedTime',
        getCellContent: function(row) {
          if (row.get('finishedTs')) {
            return row.get('finishedTime');
          }
          return 'N/A';
        }
      }, {
        id: 'elapsedTime',
        headerTitle: 'Elapsed Time',
        contentPath: 'elapsedTime'
      }, {
        id: 'priority',
        headerTitle: 'Priority',
        contentPath: 'priority'
      }, {
        id: 'containerExitStatus',
        headerTitle: 'Exit Status',
        contentPath: 'containerExitStatus',
        getCellContent: function(row) {
          var status = row.get('containerExitStatus');
          if (status) {
            return status;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'containerState',
        headerTitle: 'State',
        contentPath: 'containerState',
        getCellContent: function(row) {
          var state = row.get('containerState');
          if (state) {
            return state;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'logUrl',
        headerTitle: 'Logs',
        contentPath: 'logUrl',
        cellComponentName: 'em-table-html-cell',
        getCellContent: function(row) {
          var containerLogUrl = row.get('appAttemptContainerLogsURL');
          if (containerLogUrl) {
            return `<a href="${containerLogUrl}">Link</a>`;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'exposedPorts',
        headerTitle: 'Exposed Ports',
        contentPath: 'exposedPorts',
        getCellContent: function(row) {
          var ports = row.get('exposedPorts');
          if (ports) {
            return ports;
          } else {
            return 'N/A';
          }
        }
      }, {
        id: 'nodeHttpAddress',
        headerTitle: 'Node Manager UI',
        contentPath: 'nodeHttpAddress',
        cellComponentName: 'em-table-html-cell',
        getCellContent: function(row) {
          var address = self.checkHttpProtocol(row.get('nodeHttpAddress'));
          var link = row.get('masterNodeURL');
          if (address) {
            return `<a href="${link}">${address}</a>`;
          } else {
            return 'N/A';
          }
        }
      });
  
      var gridCols = ColumnDef.make(columns);
      this.set('gridColumns', gridCols);
      this.set('gridRows', this.modelArr);
    },
  
    checkHttpProtocol: function(prop) {
      if (prop && prop.indexOf('://') < 0) {
        prop = 'http://' + prop;
      }
      return prop;
    },
  
    requestedUser: function() {
      if (this.get('userInfo')) {
        return this.get('userInfo.requestedUser');
      }
      return '';
    }.property('userInfo'),
  });
  