import { Host, h, } from '@stencil/core';
import { daysInMonth, getBeginningOfDay, getStartEndDateForInterval, } from '../helpers';
import { differenceInHours, differenceInMinutes, differenceInMonths, differenceInSeconds, differenceInWeeks, } from 'date-fns';
/**
 * @slot (default) - The Track's content
 * @part track-header - The Track's header
 * @part container - The Track's container
 */
export class RuxTrack {
  constructor() {
    this.hasRuler = false;
    this.width = 0;
    this.columns = 0;
    this.interval = undefined;
    this.start = '';
    this.end = '';
    this.timezone = 'UTC';
    this.playhead = undefined;
  }
  handleUpdate(_newValue, old) {
    if (old) {
      this.initializeRows();
    }
  }
  handlePlayheadChange() {
    this.initializeRows();
  }
  handleTimezoneUpdate() {
    this.initializeRows();
  }
  handleTimeRegionChange(e) {
    this.initializeRows();
    e.stopPropagation();
  }
  connectedCallback() {
    this._handleSlotChange = this._handleSlotChange.bind(this);
  }
  /**
   * Tracks are displayed as a (CSS) grid of cells.
   * Each cell can represent a minute or hour depending on the interval.
   */
  _calculateGridColumnFromTime(time) {
    if (this.start) {
      let useStartEndDates = getStartEndDateForInterval(this.start, this.end, this.interval);
      if (this.interval === 'hour') {
        const difference = Math.abs(differenceInMinutes(useStartEndDates.timelineStart, new Date(time)));
        return difference + 2;
      }
      if (this.interval === 'minute') {
        const difference = Math.abs(differenceInSeconds(useStartEndDates.timelineStart, new Date(time)));
        return difference + 2;
      }
      if (this.interval === 'day') {
        return (Math.abs(differenceInHours(useStartEndDates.timelineStart, new Date(time))) + 2);
      }
      if (this.interval === 'week') {
        const timeAsDate = new Date(time);
        const numWeeks = Math.ceil(Math.abs(differenceInWeeks(useStartEndDates.timelineStart, getBeginningOfDay(timeAsDate, 0))));
        const weekStartsOnDay = useStartEndDates.timelineStart.getDay();
        let extraDays = timeAsDate.getDay();
        if (weekStartsOnDay > extraDays) {
          extraDays += 7;
        }
        return (Math.ceil((numWeeks + (extraDays - weekStartsOnDay) / 7) * 24) + 2);
      }
      if (this.interval === 'month') {
        // For a month, the timeline starts on the first of the month
        const timeAsDate = new Date(time);
        const numMonths = Math.abs(differenceInMonths(useStartEndDates.timelineStart, timeAsDate));
        const extraDays = timeAsDate.getDate() - 1;
        const daysInCurrentMonth = daysInMonth(timeAsDate);
        const extraHours = timeAsDate.getHours();
        return (Math.ceil((numMonths +
          (extraDays + extraHours / 24) /
            daysInCurrentMonth) *
          24) + 2);
      }
    }
    return 0;
  }
  _validateTimeRegion(start, end) {
    if (!start) {
      return {
        success: false,
        error: 'Time Region must have a start date provided',
      };
    }
    if (!end) {
      return {
        success: false,
        error: 'Time Region must have a end date provided',
      };
    }
    if (new Date(start) > new Date(end)) {
      return {
        success: false,
        error: `The Time Region start date must be before the end date: ${start} - ${end}`,
      };
    }
    if (new Date(start) > new Date(this.end)) {
      return {
        success: false,
        error: `The Time Region start date does not fall within the Timeline's range: ${start} - ${this.start}/${this.end}`,
      };
    }
    if (new Date(start) < new Date(this.start) &&
      new Date(end) < new Date(this.start)) {
      return {
        success: false,
        error: `The Time Region start and end dates do not fall within the Timeline's range: ${start} - ${end}`,
      };
    }
    return {
      success: true,
    };
  }
  /**
   * Time Regions are dumb and don't know anything about the grid.
   * The Track is responsible for lining up the Time Regions with the grid.
   */
  initializeRows() {
    if (this.playhead) {
      this.playedIndicator.style.gridColumnEnd = this._calculateGridColumnFromTime(this.playhead).toString();
    }
    const children = [...this.el.children].filter((el) => el.tagName.toLowerCase() === 'rux-time-region');
    let useStartEndDates = getStartEndDateForInterval(this.start, this.end, this.interval);
    children.forEach((el) => {
      const isHidden = el.style.visibility === 'hidden';
      const isValid = this._validateTimeRegion(el.start, el.end);
      /**
       * Store temp vars to use for calculating a Time Region's position in the grid
       * If a Time Region's range is outside the Timeline's range (a partial event),
       * visually it is treated as if its start/end dates = the timeline's.
       * */
      let start = el.start;
      let end = el.end;
      let startDate = new Date(el.start);
      let endDate = new Date(el.end);
      if (isValid.success) {
        if (startDate < useStartEndDates.timelineStart &&
          endDate > useStartEndDates.timelineEnd) {
          el.partial = 'ongoing';
          start = useStartEndDates.timelineStart.toISOString();
          end = useStartEndDates.timelineEnd.toISOString();
        }
        else if (startDate < useStartEndDates.timelineStart) {
          el.partial = 'start';
          start = this.start;
        }
        else if (endDate > useStartEndDates.timelineEnd) {
          el.partial = 'end';
          end = useStartEndDates.timelineEnd.toISOString();
        }
        else {
          el.partial = 'none';
        }
        el.timezone = this.timezone;
        el.style.gridRow = '1';
        el.style.display = 'block';
        const gridColumn = `${this._calculateGridColumnFromTime(start)} / ${this._calculateGridColumnFromTime(end)}`;
        el.style.gridColumn = gridColumn;
      }
      else {
        if (!isHidden) {
          el.style.display = 'none';
        }
      }
    });
  }
  _handleSlotChange() {
    this.initializeRows();
    const hasRuler = [...this.el.children].find((el) => el.tagName.toLowerCase() === 'rux-ruler');
    this.hasRuler = !!hasRuler;
  }
  renderCssGrid() {
    //we need the width of an hour so we need the interval * whatever the width of a grid col is
    const getCols = () => {
      if (['minute', 'hour'].includes(this.interval))
        return 60;
      if (['day', 'week', 'month'].includes(this.interval))
        return 24;
      return 60;
    };
    const span = getCols();
    const width = span * this.width;
    // we set this as --grid-gap and then use that variable
    // in rux-track.scss to set the gap between lines
    return `${width}px`;
  }
  render() {
    return (h(Host, null, h("div", { class: "rux-timeline rux-track", style: {
        gridTemplateColumns: `[header] 200px repeat(${this.columns}, ${this.width}px)`,
        '--grid-gap': this.renderCssGrid(),
      }, part: "container" }, h("div", { class: "rux-track__header", part: "track-header", style: {
        gridRow: '1 / -1',
      } }, h("slot", { name: "label" })), h("slot", { onSlotchange: this._handleSlotChange }), h("div", { class: {
        'rux-track__played': this.playhead,
        hidden: this.hasRuler,
      }, ref: (el) => (this.playedIndicator = el) }), h("div", { class: "grid" }))));
  }
  static get is() { return "rux-track"; }
  static get encapsulation() { return "shadow"; }
  static get originalStyleUrls() {
    return {
      "$": ["rux-track.scss"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["rux-track.css"]
    };
  }
  static get properties() {
    return {
      "width": {
        "type": "number",
        "mutable": false,
        "complexType": {
          "original": "number",
          "resolved": "number",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "internal",
              "text": "- The grid's width. Set automatically from the parent Timeline component."
            }],
          "text": ""
        },
        "attribute": "width",
        "reflect": true,
        "defaultValue": "0"
      },
      "columns": {
        "type": "number",
        "mutable": false,
        "complexType": {
          "original": "number",
          "resolved": "number",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "internal",
              "text": "- The number of grid columns to display. Set automatically from the parent Timeline component."
            }],
          "text": ""
        },
        "attribute": "columns",
        "reflect": true,
        "defaultValue": "0"
      },
      "interval": {
        "type": "any",
        "mutable": false,
        "complexType": {
          "original": "any",
          "resolved": "any",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "internal",
              "text": "- The Track's interval. Set automatically from the parent Timeline component."
            }],
          "text": ""
        },
        "attribute": "interval",
        "reflect": true
      },
      "start": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "internal",
              "text": "- The Track's start date. Set automatically from the parent Timeline component."
            }],
          "text": ""
        },
        "attribute": "start",
        "reflect": true,
        "defaultValue": "''"
      },
      "end": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "internal",
              "text": "- The Track's end date. Set automatically from the parent Timeline component."
            }],
          "text": ""
        },
        "attribute": "end",
        "reflect": true,
        "defaultValue": "''"
      },
      "timezone": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "internal",
              "text": "- The Track's time zone. Set automatically from the parent Timeline component."
            }],
          "text": ""
        },
        "attribute": "timezone",
        "reflect": true,
        "defaultValue": "'UTC'"
      },
      "playhead": {
        "type": "any",
        "mutable": false,
        "complexType": {
          "original": "any",
          "resolved": "any",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "The playhead of rux-track."
        },
        "attribute": "playhead",
        "reflect": true
      }
    };
  }
  static get states() {
    return {
      "hasRuler": {}
    };
  }
  static get elementRef() { return "el"; }
  static get watchers() {
    return [{
        "propName": "start",
        "methodName": "handleUpdate"
      }, {
        "propName": "end",
        "methodName": "handleUpdate"
      }, {
        "propName": "interval",
        "methodName": "handleUpdate"
      }, {
        "propName": "playhead",
        "methodName": "handlePlayheadChange"
      }, {
        "propName": "timezone",
        "methodName": "handleTimezoneUpdate"
      }];
  }
  static get listeners() {
    return [{
        "name": "ruxtimeregionchange",
        "method": "handleTimeRegionChange",
        "target": undefined,
        "capture": false,
        "passive": false
      }];
  }
}
