import {Graph} from "./graph";
import {SingleGraphType} from "./singlegraphtype";

declare var require: any

/**
 * Graph with single sensors (linechart, barchart, windchart)
 */
export class SingleGraph implements Graph {
  isAnalytics: boolean;
  sensorId: number;
  interval: number;
  data: any [];
  legend: {};
  type: SingleGraphType;

  /**
   *
   * @param sensorId id of sensor
   * @param isAnalytics true for analytics, false for observations
   * @param data source of values for vega graph
   * @param legend source of legend info for graph (e.g. sensor phenomenon)
   * @param interval default graph interval used for different purposes (in milliseconds) (see timeWindow and maxTimeDifference in vega specification)
   */
  constructor(sensorId: number, isAnalytics: boolean, data: any[], legend: {}, interval: number) {
    this.sensorId = sensorId;
    this.isAnalytics = isAnalytics;
    this.interval = interval;
    this.data = data;
    this.legend = this.getLegend(legend);
    this.type = this.getGraphType();

  }

  /**
   * Returns vega configuration (see readme.txt in vega folder)
   */
  getConfig(): {} {
    const lodash = require('lodash/object');
    const rvalue: any = {};
    let config1 = require('/src/vega/config/config.json');
    lodash.merge(rvalue, config1);

    let config2;
    if (this.isAnalytics) {
      config2 = require('/src/vega/config/config-analytics.json');
    } else {
      config2 = require('/src/vega/config/config-observations.json');
    }

    let config3;
    switch (this.type) {
      case SingleGraphType.LINECHART:
      case SingleGraphType.BARCHART:
        config3 = require('/src/vega/config/config-barline.json');
        break;
      case SingleGraphType.WINDCHART:
        config3 = require('/src/vega/config/config-wind.json');
        break;
    }


    for (let key in config2.signals) {
      let obj = config2.signals[key];
      rvalue.signals.push(obj);
    }

    for (let key in config3.signals) {
      let obj = config3.signals[key];
      rvalue.signals.push(obj);
    }

    return rvalue;
  }

  /**
   * Gets vega specification (see readme.txt in vega folder)
   */
  getSpec(): {} {
    const lodash = require('lodash/object');
    const rvalue: any = {};
    const base = require('/src/vega/base/default.json');
    const body = this.getBodySpec();
    const legend = this.getLegendSpec();
    const tooltip = this.getTooltipSpec();
    lodash.merge(rvalue, base, body, legend, tooltip);

    //setting data to result
    this.setData(rvalue, this.data);

    //setting legend to result
    rvalue['data'][0].values = this.legend;

    //setting interval to result
    rvalue.signals[0].value = this.interval;

    return rvalue;
  }

  /**
   * Gets legend specification (windchart does not have a legend)
   */
  private getLegendSpec() {
    switch (this.type) {
      case SingleGraphType.BARCHART:
      case SingleGraphType.LINECHART:
        return require('/src/vega/miscs/legend.json');
      case SingleGraphType.WINDCHART:
        return {};
    }
  }

  /**
   * Gets body specification
   */
  private getBodySpec() {
    switch (this.type) {
      case SingleGraphType.BARCHART:
        return require('/src/vega/body/barchart.json');
      case SingleGraphType.WINDCHART:
        return require('/src/vega/body/windchart.json');
      case SingleGraphType.LINECHART:
        return require('/src/vega/body/linechart.json');
    }
  }


  /**
   * Get tooltip specification
   */
  private getTooltipSpec() {
    let lodash = require('lodash');
    let tooltipMessage;
    let rvalue: any = {};
    if (this.isAnalytics) {
      lodash.merge(rvalue, require('/src/vega/miscs/samm-tooltip.json'));
      tooltipMessage = this.getSammTooltip();
    } else {
      lodash.merge(rvalue, require('/src/vega/miscs/value-tooltip.json'));
      tooltipMessage = this.getValueTooltip();
    }

    //sets the tooltip message
    rvalue.marks[0].marks[0].marks[0].encode.enter.tooltip.signal = tooltipMessage;
    return rvalue;
  }

  /**
   *
   */
  private getValueTooltip() {
    return "{title: timeFormat(datum.dateTime, '%A, %B %e, %Y %X')  ,  'value': datum.value + ' " + this.legend['phenomenon']['unit'] + "'}"
  }

  private getSammTooltip() {
    return "{title: timeFormat(datum.dateTime, '%A, %B %e, %Y %X') " +
      " , 'avg': datum.avg + ' " + this.legend['phenomenon']['unit'] +
      "', 'sum': datum.sum + ' " + this.legend['phenomenon']['unit'] +
      "', 'max': datum.max + ' " + this.legend['phenomenon']['unit'] +
      "', 'min': datum.min + ' " + this.legend['phenomenon']['unit'] + "'}"
  }


  /**
   * Sets data to specification. If data is array, it selects only the first element
   * @param spec specification to insert into
   * @param data data to insert
   */
  private setData(spec: any, data: any) {
    if(data.length > 0 && 'data' in data[0]) {
      spec['data'][1].values = data[0].data;
    } else {
      spec['data'][1].values = data;
    }
  }

  /**
   * If legend is array of phenomenons, it selects the first element
   * @param legend info for displaying legend
   */
  private getLegend(legend: any) {
    if (Array.isArray(legend)) {
      return legend[0];
    } else {
      return legend;
    }
  }

  /**
   * Select the graph type from id
   */
  private getGraphType() {
    if (this.sensorId >= 480000000 && this.sensorId < 490000000) {
      return SingleGraphType.BARCHART;
    } else if ((this.sensorId >= 470020000 && this.sensorId < 470030000) || (this.sensorId >= 470060000 && this.sensorId < 470090000) ||
      (this.sensorId >= 470130000 && this.sensorId < 470140000) || (this.sensorId >= 470180000 && this.sensorId < 470190000)) {
      return SingleGraphType.WINDCHART;
    } else {
      return SingleGraphType.LINECHART;
    }
  }


}
