import moment from 'moment';
import { select } from 'd3';
import { isDef, StartEndDuration } from './util.js';
import './seismogram.js';
import { SeismographConfig } from './seismographconfig.js';
import { Seismograph } from './seismograph.js';
import { insertCSS } from './plotutil.js';

/*
 * Philip Crotwell
 * University of South Carolina, 2019
 * http://www.seis.sc.edu
 */
/**
 * A helicorder-like multi-line seismogram display usually covering 24 hours
 * @param inSvgParent the parent element, usually a div tag
 * @param heliConfig configuration object
 * @param seisData the data to display
 */

class Helicorder {
  constructor(inSvgParent, heliConfig, seisData) {
    this.seismographArray = [];
    this.seisData = seisData;
    this.svgParent = select(inSvgParent).append('div').classed('helicorder', true);
    this.heliConfig = heliConfig;
    this.maxVariation = 1;

    if (seisData.seismogram) {
      const seis = seisData.seismogram; // for flow

      const timeWindow = this.heliConfig.fixedTimeScale; // for flow

      if (!isDef(timeWindow)) {
        throw new Error("Helicorder config must have fixedTimeScale set");
      }

      let cutSeis = seis.cut(timeWindow);

      if (cutSeis) {
        let [min, max] = cutSeis.findMinMax();
        let mean = cutSeis.mean();
        let posOffset = max - mean;
        let negOffset = mean = min;
        this.maxVariation = Math.max(posOffset, negOffset);
      }
    }
  }
  /**
   * draws, or redraws, the helicorder.
   */


  draw() {
    this.drawSeismograms();
  }

  drawSeismograms() {
    if (!this.seisData) {
      // no data
      return;
    }

    const timeWindow = this.heliConfig.fixedTimeScale;

    if (!isDef(timeWindow)) {
      throw new Error("Helicorder config must have fixedTimeScale set");
    }

    let startTime = moment.utc(timeWindow.startTime);
    this.seismographArray = [];
    const secondsPerLine = timeWindow.duration.asSeconds() / this.heliConfig.numLines;
    let lineTimes = this.calcTimesForLines(startTime, secondsPerLine, this.heliConfig.numLines);

    for (let lineTime of lineTimes) {
      let startTime = lineTime.startTime;
      let endTime = lineTime.endTime;
      let nl = this.heliConfig.numLines;
      let height = (this.heliConfig.maxHeight - this.heliConfig.margin.bottom) / (nl - (nl - 1) * this.heliConfig.overlap);
      let marginTop = lineTime.lineNumber === 0 ? 0 : Math.round(-1.0 * height * this.heliConfig.overlap);
      let seisDiv = this.svgParent.append('div').classed('heliLine', true).style('height', height + 'px').style('margin-top', "".concat(marginTop, "px"));
      let lineSeisConfig = this.heliConfig.lineSeisConfig.clone();

      if (lineTime.lineNumber === 0) {
        lineSeisConfig.title = this.heliConfig.title;
        lineSeisConfig.isXAxisTop = this.heliConfig.isXAxisTop;
        lineSeisConfig.margin.top += this.heliConfig.margin.top;
        height += this.heliConfig.margin.top;
        seisDiv.style('height', height + 'px');
      } else if (lineTime.lineNumber === nl - 1) {
        lineSeisConfig.isXAxis = this.heliConfig.isXAxis;
        lineSeisConfig.margin.bottom += this.heliConfig.margin.bottom;
        height += this.heliConfig.margin.bottom;
        seisDiv.style('height', height + 'px');
      }

      lineSeisConfig.fixedTimeScale = lineTime;
      lineSeisConfig.yLabel = "".concat(startTime.format("HH:mm"));
      lineSeisConfig.yLabelRight = "".concat(endTime.format("HH:mm"));
      lineSeisConfig.lineColors = [seisDiv.style("color")];
      let trimSeisDataList = [];
      let lineCutSeis = null;
      let lineMean = 0;
      let lineSeisData;

      if (this.seisData.seismogram) {
        lineCutSeis = this.seisData.seismogram.cut(lineTime);
        lineSeisData = this.seisData.cloneWithNewSeismogram(lineCutSeis);

        if (lineCutSeis) {
          lineMean = lineCutSeis.mean();
        }
      } else {
        // no data in window, but keep seisData in case of markers, etc
        lineSeisData = this.seisData.clone();
      }

      lineSeisData.timeWindow = lineTime;
      trimSeisDataList.push(lineSeisData);

      if (this.heliConfig.fixedYScale) {
        lineSeisConfig.fixedYScale = this.heliConfig.fixedYScale;
      } else {
        lineSeisConfig.fixedYScale = [lineMean - this.maxVariation, lineMean + this.maxVariation];
      }

      let seismograph = new Seismograph(seisDiv, lineSeisConfig, trimSeisDataList);
      seismograph.draw();

      if (lineTime.lineNumber === 0) {
        // add UTC to top left
        seismograph.svg.append("g").classed("yLabel", true).classed("utcLabel", true).append("text").attr("x", 0).attr("y", 0).attr("text-anchor", "start").attr("dy", ".75em").text("UTC");
      }

      this.seismographArray.push(seismograph);
      startTime = endTime;
    }
  }
  /**
   * Calculates the time range covered by each line of the display
   * @param  {[type]} startTime      start of display
   * @param  {[type]} secondsPerLine seconds covered by each line
   * @param  {[type]} numberOfLines  number of lines
   * @return Array of HeliTimeRange, one per line
   */


  calcTimesForLines(startTime, secondsPerLine, numberOfLines) {
    let out = [];
    let s = moment.utc(startTime);

    for (let lineNum = 0; lineNum < numberOfLines; lineNum++) {
      let startEnd = new HeliTimeRange(s, null, secondsPerLine);
      startEnd.lineNumber = lineNum;
      out.push(startEnd);
      s = moment.utc(startEnd.endTime);
    }

    return out;
  }

}
/**
 * Configuration of the helicorder
 * @param timeWindow the time range covered by the helicorder, required
 */

class HelicorderConfig extends SeismographConfig {
  constructor(timeWindow) {
    super();

    if (!isDef(timeWindow)) {
      throw new Error("Helicorder config must have fixedTimeScale set");
    }

    this.fixedTimeScale = timeWindow;
    this.maxHeight = 600;
    this.xLabel = '';
    this.yLabel = '';
    this.xSublabel = '';
    this.ySublabel = ' ';
    this.ySublabelIsUnits = false;
    this.isXAxis = true;
    this.isXAxisTop = true;
    this.isYAxis = false;
    this.overlap = 0.5;
    this.numLines = 12;
    this.margin.left = 20;
    this.margin.right = 20;
    this.margin.top = 40;
    this.lineSeisConfig = new SeismographConfig();
    this.lineSeisConfig.ySublabel = " ";
    this.lineSeisConfig.xLabel = ' ';
    this.lineSeisConfig.yLabel = ''; // replace later with `${startTime.format("HH:mm")}`;

    this.lineSeisConfig.yLabelOrientation = 'horizontal';
    this.lineSeisConfig.ySublabelIsUnits = false;
    this.lineSeisConfig.isXAxis = false;
    this.lineSeisConfig.isYAxis = false;
    this.lineSeisConfig.margin.top = 0;
    this.lineSeisConfig.margin.bottom = 0;
    this.lineSeisConfig.margin.left = 37;
    this.lineSeisConfig.margin.right = 37;
    this.lineSeisConfig.wheelZoom = false;
  }

}
/**
 * Time range for a single line of the helicorder, extends StartEndDuration
 * to add the line number
 */

class HeliTimeRange extends StartEndDuration {
  constructor(startTime, endTime, duration = null, clockOffset = 0) {
    super(startTime, endTime, duration, clockOffset);
  }

}
const helicorder_css = "\n\ndiv.heliLine {\n  height: 75px;\n  margin-top: -50px;\n  color: black;\n}\n\ndiv.heliLine:last-child {\n  height: 135px;\n  margin-bottom: 35px;\n}\n\ndiv.helicorder div.heliLine .yLabel text {\n  font-size: smaller;\n}\n\ndiv.helicorder div.heliLine:first-child {\n  margin-top: 0;\n}\n\ndiv.helicorder div.heliLine:nth-child(3n+1) {\n  color: skyblue;\n}\ndiv.helicorder div.heliLine:nth-child(3n+1) path.seispath {\n  stroke: skyblue;\n}\ndiv.helicorder div.heliLine:nth-child(3n+2) {\n  color: olivedrab;\n}\ndiv.helicorder div.heliLine:nth-child(3n+2) path.seispath {\n  stroke: olivedrab;\n}\ndiv.helicorder div.heliLine:nth-child(3n) {\n  color: goldenrod;\n}\ndiv.helicorder div.heliLine:nth-child(3n) path.seispath {\n  stroke: goldenrod;\n}\ndiv.helicorder div.heliLine:nth-child(3n+1) .yLabel text {\n  fill: skyblue;\n}\ndiv.helicorder div.heliLine:nth-child(3n+2) .yLabel text {\n  fill: olivedrab;\n}\ndiv.helicorder div.heliLine:nth-child(3n) .yLabel text {\n  fill: goldenrod;\n}\n\n";

if (document) {
  insertCSS(helicorder_css);
}

export { HeliTimeRange, Helicorder, HelicorderConfig, helicorder_css };
//# sourceMappingURL=helicorder.js.map
