import { cloneDate, isAfter, isBefore, isSameDay, iterateMonth, moveToDayOfWeek, toDayOfMonthString, toMonthAndYearString } from '@dvn/alaca/components/dvn-date-picker/date-utils';
import { Component, Event, h, Host, Prop, State, Watch } from '@stencil/core';
import moment from 'moment';
function getWeekStartDates(view) {
  view.setDate(1);
  view = moveToDayOfWeek(cloneDate(view), 0);
  const current = cloneDate(view);
  current.setDate(current.getDate() + 7);
  const starts = [view];
  const month = current.getMonth();
  while (current.getMonth() === month) {
    starts.push(cloneDate(current));
    current.setDate(current.getDate() + 7);
  }
  if (starts.length < 6) {
    // push the extra week even though it's the next month in order to fill the date picker height
    starts.push(cloneDate(current));
  }
  return starts;
}
function buildDays(start) {
  const days = [cloneDate(start)];
  let clone = cloneDate(start);
  for (let i = 1; i <= 6; i++) {
    clone = cloneDate(clone);
    clone.setDate(clone.getDate() + 1);
    days.push(clone);
  }
  return days;
}
function getDayClassName(month, selectedDay, highlightedDay, day, isDisabled) {
  let className = 'day';
  if (highlightedDay && isSameDay(day, highlightedDay))
    className += ' highlighted-day';
  if (isSameDay(day, new Date()))
    className += ' today';
  if (month !== day.getMonth())
    className += ' other-month';
  if (selectedDay) {
    let isSelected = false;
    if (Array.isArray(selectedDay)) {
      isSelected = !!selectedDay.find(d => day.toDateString() === d.toDateString());
    }
    else {
      isSelected = isSameDay(day, selectedDay);
    }
    if (isSelected)
      className += ' selected';
  }
  if (isDisabled)
    className += ' disabled';
  return className;
}
function isDisabled(minDate, maxDate, day) {
  return (minDate && isBefore(day, minDate)) || (maxDate && isAfter(day, maxDate));
}
function renderWeeks(currentDate, value, highlightedDay, minDate, maxDate, onSelect) {
  const starts = getWeekStartDates(currentDate);
  const month = starts[1].getMonth();
  return starts.map((s, i) => {
    const days = buildDays(s);
    return (h("div", { key: i, class: "week" }, days.map((day, j) => (h("div", { class: {
        day: true,
        [getDayClassName(month, value, highlightedDay, day, isDisabled(minDate, maxDate, day))]: true
      }, key: j, onClick: () => onSelect && onSelect(day) },
      h("div", { class: "real-day" }, toDayOfMonthString(day)))))));
  });
}
function valueLabel(value, dateFormat) {
  if (!value) {
    return '';
  }
  if (Array.isArray(value)) {
    return value.map(d => moment(d).format(dateFormat)).join(', ');
  }
  return moment(value).format(dateFormat);
}
function checkIfClickedDateIsOutsideVisibleRange(currentDate, shownMonthCount, day, isSlidingRight) {
  const firstVisibleMonth = isSlidingRight
    ? iterateMonth(currentDate, 1)
    : currentDate;
  const lastVisibleMonth = isSlidingRight
    ? iterateMonth(currentDate, shownMonthCount)
    : iterateMonth(currentDate, shownMonthCount - 1);
  const shouldMoveForward = (day.getMonth() > lastVisibleMonth.getMonth() && day.getFullYear() === lastVisibleMonth.getFullYear()) || day.getFullYear() > lastVisibleMonth.getFullYear();
  const shouldMoveBackward = (firstVisibleMonth.getMonth() > day.getMonth() && day.getFullYear() === firstVisibleMonth.getFullYear()) || day.getFullYear() < firstVisibleMonth.getFullYear();
  return { shouldMoveForward, shouldMoveBackward };
}
export class DvnDatePicker {
  constructor() {
    this.currentDate = new Date();
    this.isCalendarShown = false;
    this.isSliding = false;
    this.isSlidingRight = false;
    this.dateFormat = 'MM/DD/YYYY';
    this.leaned = false;
    this.disabled = false;
    this.embedded = false;
    this.multiple = false;
    this.shownMonthCount = 1;
    this.moveBackward = () => {
      if (!this.isSliding) {
        this.move(iterateMonth(this.currentDate, this.isSlidingRight ? 0 : -1), false);
      }
    };
    this.moveForward = () => {
      if (!this.isSliding) {
        this.move(iterateMonth(this.currentDate, 1), true);
      }
    };
    this.onTransitionEnd = () => {
      this.isSliding = false;
      if (!this.isSlidingRight) {
        this.currentDate = iterateMonth(this.currentDate, 1);
      }
      else {
        this.currentDate = iterateMonth(this.currentDate, -1);
      }
    };
    this.onSelectDay = (day) => {
      const { currentDate, shownMonthCount, isSlidingRight, valueInState } = this;
      const { shouldMoveForward, shouldMoveBackward } = checkIfClickedDateIsOutsideVisibleRange(currentDate, shownMonthCount, day, isSlidingRight);
      if (shouldMoveForward) {
        this.moveForward();
      }
      else if (shouldMoveBackward) {
        this.moveBackward();
      }
      if (this.isDayDisabled(day)) {
        return;
      }
      if (!this.multiple) {
        this.valueInState = day;
        this.isCalendarShown = false;
      }
      else {
        const dates = valueInState || [];
        const filtered = dates.filter(d => day.toDateString() !== d.toDateString());
        if (filtered.length === dates.length) {
          // if filtered is same as dates, then the item is not in the list
          this.valueInState = [...dates, day];
        }
        else {
          this.valueInState = filtered;
        }
      }
      this.change.emit(this.valueInState);
    };
  }
  move(date, isForward) {
    if (this.disabled) {
      return;
    }
    if (!isForward || this.isSlidingRight) {
      this.currentDate = cloneDate(date);
    }
    this.isSliding = true;
    this.isSlidingRight = !isForward;
  }
  componentDidLoad() {
    this.currentWeekContainer.addEventListener('transitionend', this.onTransitionEnd);
  }
  isDayDisabled(day) {
    const { minDate, maxDate } = this;
    return (minDate && isBefore(day, minDate)) || (maxDate && isAfter(day, maxDate));
  }
  updateInnerValue(newValue) {
    this.valueInState = newValue;
  }
  componentWillLoad() {
    this.valueInState = this.value;
  }
  render() {
    const { currentDate, embedded, isCalendarShown, isSliding, isSlidingRight, highlightedDay, placeholder, value, valueInState, minDate, maxDate, shownMonthCount, } = this;
    const valueToShow = value || valueInState;
    return (h(Host, { onBlur: () => this.isCalendarShown = false, onFocus: () => this.isCalendarShown = true, tabindex: this.disabled ? -1 : 0, style: {
        '--shown-month-count': `${shownMonthCount}`,
      } },
      !embedded &&
        h("dvn-input", { placeholder: placeholder, readonly: true, value: valueLabel(valueInState, this.dateFormat), leaned: this.leaned }),
      h("div", { class: {
          'calendar-container': true,
          shown: isCalendarShown,
          embedded,
        } },
        h("div", { class: "month-header" },
          h("i", { class: "navigation-icon", onClick: this.moveBackward }, String.fromCharCode(9664)),
          h("div", { class: "sliding-month-names-container" }, Array.apply(null, Array(Number(shownMonthCount) + 1)).map((_, i) => (h("div", { class: {
              'sliding-month-name': true,
              'sliding': isSliding,
              'to-right': isSlidingRight,
              'other': true,
            }, ref: el => this.currentWeekContainer = el, style: { '--month-order': `${isSlidingRight ? i - 1 : i}` } }, toMonthAndYearString(iterateMonth(currentDate, i)))))),
          h("i", { class: "navigation-icon", onClick: this.moveForward }, String.fromCharCode(9654))),
        h("div", { class: "week-headers-container" }, Array.apply(null, Array(shownMonthCount)).map((_, i) => (h("div", { class: "week-header", key: `week-${i}` },
          h("div", { class: "day-name" }, "Sun"),
          h("div", { class: "day-name" }, "Mon"),
          h("div", { class: "day-name" }, "Tue"),
          h("div", { class: "day-name" }, "Wed"),
          h("div", { class: "day-name" }, "Thu"),
          h("div", { class: "day-name" }, "Fri"),
          h("div", { class: "day-name" }, "Sat"))))),
        h("div", { class: "month-container" }, Array.apply(null, Array(shownMonthCount + 1)).map((_, i) => (h("div", { class: {
            'sliding-month': true,
            'other': true,
            'sliding': isSliding,
            'to-right': isSlidingRight,
          }, style: { '--month-order': `${isSlidingRight ? i - 1 : i}` }, ref: el => this.currentWeekContainer = el }, renderWeeks(iterateMonth(currentDate, i), valueToShow, highlightedDay, minDate, maxDate, this.onSelectDay))))))));
  }
  static get is() { return "dvn-date-picker"; }
  static get originalStyleUrls() { return {
    "$": ["./dvn-date-picker.scss"]
  }; }
  static get styleUrls() { return {
    "$": ["dvn-date-picker.css"]
  }; }
  static get properties() { return {
    "dateFormat": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "date-format",
      "reflect": false,
      "defaultValue": "'MM/DD/YYYY'"
    },
    "leaned": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "leaned",
      "reflect": false,
      "defaultValue": "false"
    },
    "disabled": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "disabled",
      "reflect": false,
      "defaultValue": "false"
    },
    "placeholder": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "placeholder",
      "reflect": false
    },
    "value": {
      "type": "unknown",
      "mutable": false,
      "complexType": {
        "original": "Date | Date[]",
        "resolved": "Date | Date[]",
        "references": {
          "Date": {
            "location": "global"
          }
        }
      },
      "required": false,
      "optional": true,
      "docs": {
        "tags": [],
        "text": ""
      }
    },
    "highlightedDay": {
      "type": "unknown",
      "mutable": false,
      "complexType": {
        "original": "Date",
        "resolved": "Date",
        "references": {
          "Date": {
            "location": "global"
          }
        }
      },
      "required": false,
      "optional": true,
      "docs": {
        "tags": [],
        "text": ""
      }
    },
    "minDate": {
      "type": "unknown",
      "mutable": false,
      "complexType": {
        "original": "Date",
        "resolved": "Date",
        "references": {
          "Date": {
            "location": "global"
          }
        }
      },
      "required": false,
      "optional": true,
      "docs": {
        "tags": [],
        "text": ""
      }
    },
    "maxDate": {
      "type": "unknown",
      "mutable": false,
      "complexType": {
        "original": "Date",
        "resolved": "Date",
        "references": {
          "Date": {
            "location": "global"
          }
        }
      },
      "required": false,
      "optional": true,
      "docs": {
        "tags": [],
        "text": ""
      }
    },
    "embedded": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "embedded",
      "reflect": false,
      "defaultValue": "false"
    },
    "multiple": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "multiple",
      "reflect": false,
      "defaultValue": "false"
    },
    "shownMonthCount": {
      "type": "number",
      "mutable": false,
      "complexType": {
        "original": "number",
        "resolved": "number",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "shown-month-count",
      "reflect": false,
      "defaultValue": "1"
    }
  }; }
  static get states() { return {
    "currentDate": {},
    "isCalendarShown": {},
    "isSliding": {},
    "isSlidingRight": {},
    "valueInState": {}
  }; }
  static get events() { return [{
      "method": "change",
      "name": "change",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": ""
      },
      "complexType": {
        "original": "any",
        "resolved": "any",
        "references": {}
      }
    }]; }
  static get watchers() { return [{
      "propName": "value",
      "methodName": "updateInnerValue"
    }]; }
}
