import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/extends";
import _objectSpread from "@babel/runtime/helpers/objectSpread";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _defineProperty from "@babel/runtime/helpers/defineProperty";

/**
 * Web DatePicker Component
 *
 */
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { addDays, addMonths, addYears, setDate, setMonth, setYear, isAfter, format } from 'date-fns';
import classnames from 'classnames';
import MaskedInput from 'react-text-mask'; // https://github.com/text-mask/text-mask

import Input, { SubmitButton } from '../input/Input';
import keycode from 'keycode';
import { validateDOMAttributes } from '../../shared/component-helper';
import { isDisabled } from './DatePickerCalc';
export const propTypes = {
  id: PropTypes.string,
  maskOrder: PropTypes.string,
  maskPlaceholder: PropTypes.string,
  separatorRexExp: PropTypes.instanceOf(RegExp),
  minDate: PropTypes.instanceOf(Date),
  maxDate: PropTypes.instanceOf(Date),
  range: PropTypes.bool,
  status: PropTypes.string,
  status_state: PropTypes.string,
  status_animation: PropTypes.string,
  disabled: PropTypes.bool,
  opened: PropTypes.bool,
  showInput: PropTypes.bool,
  onChange: PropTypes.func,
  onSubmit: PropTypes.func,
  onSubmitButtonFocus: PropTypes.func,
  onFocus: PropTypes.func
};
export const defaultProps = {
  id: null,
  maskOrder: 'dd/mm/yyyy',
  maskPlaceholder: 'dd/mm/åååå',
  separatorRexExp: /[-/ ]/g,
  range: null,
  status: null,
  status_state: 'error',
  status_animation: null,
  minDate: null,
  maxDate: null,
  disabled: null,
  opened: false,
  showInput: null,
  onChange: null,
  onSubmit: null,
  onSubmitButtonFocus: null,
  onFocus: null
};

var _ref6 =
/*#__PURE__*/
React.createElement("span", {
  className: "dnb-date-picker--separator",
  "aria-hidden": true
}, ' – ');

export default class DatePickerInput extends PureComponent {
  constructor(props) {
    var _this;

    super(props);
    _this = this;

    _defineProperty(this, "state", {
      _listenForPropChanges: true,
      focusState: 'virgin',
      startDate: null,
      endDate: null
    });

    _defineProperty(this, "onKeyUpHandler", () => {
      if (this.props.showInput) {
        return;
      }

      if (this._startDayRef.current) {
        setTimeout(() => {
          try {
            const elem = this._startDayRef.current.inputElement;
            elem.focus();
            elem.select();
          } catch (e) {
            console.log(e);
          }
        }, 100);
      }

      if (typeof this.props.onSubmitButtonFocus === 'function') {
        this.props.onSubmitButtonFocus();
      }

      this.onKeyUpHandler = null;
    });

    _defineProperty(this, "callOnChange", function (_ref) {
      let {
        startDate,
        endDate
      } = _ref;
      let onState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

      if (typeof startDate !== 'undefined' && DatePickerInput.isValidDate(startDate)) {
        _this.setState({
          startDate,
          _listenForPropChanges: false
        }, onState);

        if (typeof _this.props.onChange === 'function') {
          _this.props.onChange({
            startDate
          });
        }
      }

      if (typeof endDate !== 'undefined' && DatePickerInput.isValidDate(endDate)) {
        _this.setState({
          endDate,
          _listenForPropChanges: false
        }, onState);

        if (typeof _this.props.onChange === 'function') {
          _this.props.onChange({
            endDate
          });
        }
      }
    });

    _defineProperty(this, "prepareCounting",
    /*#__PURE__*/
    function () {
      var _ref3 = _asyncToGenerator(function* (_ref2) {
        let {
          keyCode,
          target
        } = _ref2;
        const isDate = target.getAttribute('class').match(/__input--([day|month|year]+)($|\s)/)[1];
        const isInRange = target.getAttribute('id').match(/[0-9]-([start|end]+)-/)[1];
        let date = isInRange === 'start' ? _this.state.startDate : _this.state.endDate; // do nowting if date is not set yet

        if (!date) {
          return;
        }

        const count = keyCode === 'up' ? 1 : -1;

        if (keyCode === 'up' || keyCode === 'down') {
          switch (isDate) {
            case 'day':
              date = addDays(date, count);
              break;

            case 'month':
              date = addMonths(date, count);
              break;

            case 'year':
              date = addYears(date, count);
              break;
          }
        }

        _this.callOnChange({
          [isInRange === 'start' ? 'startDate' : 'endDate']: date
        });

        yield wait(1); // to get the correct position afterwards

        const endPos = target.value.length;
        target.focus();
        target.setSelectionRange(0, endPos);
      });

      return function (_x) {
        return _ref3.apply(this, arguments);
      };
    }());

    _defineProperty(this, "onKeyDownHandler",
    /*#__PURE__*/
    function () {
      var _ref4 = _asyncToGenerator(function* (e) {
        const keyCode = keycode(e);
        const target = e.target; // only to process key up and down press

        switch (keyCode) {
          case 'up':
          case 'down':
            _this.prepareCounting({
              keyCode,
              target
            });

            e.preventDefault();
            return false;

          case 'tab':
            return false;
        } // the rest is for value entry


        const size = parseFloat(target.getAttribute('size'));
        const firstSelectionStart = target.selectionStart;
        yield wait(1); // to get the correct position afterwards

        const secondSelectionStart = target.selectionStart;
        const isValid = /[0-9]/.test(keyCode);

        const index = _this.refList.findIndex((_ref5) => {
          let {
            current: {
              inputElement
            }
          } = _ref5;
          return inputElement === target;
        });

        if (index < _this.refList.length - 1 && (secondSelectionStart === size && isValid && keyCode !== 'left' && keyCode !== 'backspace' || firstSelectionStart === size && keyCode === 'right')) {
          try {
            // stop in case there is no next input element
            if (!_this.refList[index + 1].current) {
              return;
            }

            const nextSibling = _this.refList[index + 1].current.inputElement;

            if (nextSibling) {
              nextSibling.focus();
              nextSibling.setSelectionRange(0, 0);
            }
          } catch (e) {
            console.log(e);
          }
        } else if (firstSelectionStart === 0 && index > 0) {
          switch (keyCode) {
            case 'left':
            case 'backspace':
              try {
                const prevSibling = _this.refList[index - 1].current.inputElement;

                if (prevSibling) {
                  const endPos = prevSibling.value.length;
                  prevSibling.focus();
                  prevSibling.setSelectionRange(endPos, endPos);
                }
              } catch (e) {
                console.log(e);
              }

              break;
          }
        }
      });

      return function (_x2) {
        return _ref4.apply(this, arguments);
      };
    }());

    _defineProperty(this, "set_startDay", event => {
      this.setDate(event, 2, 'start', 'Day', setDate);
    });

    _defineProperty(this, "set_startMonth", event => {
      this.setDate(event, 2, 'start', 'Month', setMonth);
    });

    _defineProperty(this, "set_startYear", event => {
      this.setDate(event, 4, 'start', 'Year', setYear);
    });

    _defineProperty(this, "set_endDay", event => {
      this.setDate(event, 2, 'end', 'Day', setDate);
    });

    _defineProperty(this, "set_endMonth", event => {
      this.setDate(event, 2, 'end', 'Month', setMonth);
    });

    _defineProperty(this, "set_endYear", event => {
      this.setDate(event, 4, 'end', 'Year', setYear);
    });

    _defineProperty(this, "setDate", (event, count, mode, type, fn) => {
      try {
        let value = event.target.value;

        if (parseFloat(value) > 0 && new RegExp("[0-9]{".concat(count, "}")).test(value)) {
          //  define a reset date
          if (!this["_".concat(mode, "Date")]) {
            this["_".concat(mode, "Date")] = new Date(1111, 1, 1);
          }

          value = parseFloat(value); // months have to be decented

          if (type === 'Month') {
            value--;
          } // calculate new date


          const date = this["_".concat(mode, "Date")] = fn(this.state["".concat(mode, "Date")] || this["_".concat(mode, "Date")], value); // update the date

          this.callOnChange({
            ["".concat(mode, "Date")]: date
          });
        }
      } catch (e) {
        console.log(e);
      }
    });

    _defineProperty(this, "renderInputElement", params => {
      const {
        range
      } = this.props;
      this.refList = [];
      const startDateList = this.generateDateList(params, 'start');
      const endDateList = this.generateDateList(params, 'end');
      return React.createElement("span", {
        className: "dnb-date-picker__input__wrapper"
      }, startDateList, range && _ref6, range && endDateList);
    });

    const separators = props.maskOrder.match(props.separatorRexExp);
    this.maskList = props.maskOrder.split(props.separatorRexExp).reduce((acc, cur) => {
      acc.push(cur);

      if (separators.length > 0) {
        acc.push(separators.shift());
      }

      return acc;
    }, []);
    this._startDayRef = React.createRef();
    this._startMonthRef = React.createRef();
    this._startYearRef = React.createRef();
    this._endDayRef = React.createRef();
    this._endMonthRef = React.createRef();
    this._endYearRef = React.createRef();
  }

  static isValidDate(date) {
    return date && isAfter(date, new Date(1971, 1, 1));
  }

  static getDerivedStateFromProps(props, state) {
    if (state._listenForPropChanges) {
      // watch for updates from the range calendar
      if (typeof props.startDate !== 'undefined') {
        state.startDate = props.startDate;
      }

      if (typeof props.endDate !== 'undefined') {
        state.endDate = props.endDate;
      }

      if (isDisabled(state.startDate, props.minDate, props.maxDate)) {
        state.startDate = props.minDate;
      }

      if (isDisabled(state.endDate, props.minDate, props.maxDate)) {
        state.endDate = props.maxDate;
      }
    }

    state._listenForPropChanges = true; // set the input values

    if (DatePickerInput.isValidDate(state.startDate)) {
      state._startDay = pad(format(state.startDate, 'D'), 2);
      state._startMonth = pad(format(state.startDate, 'M'), 2);
      state._startYear = format(state.startDate, 'YYYY');
    }

    if (DatePickerInput.isValidDate(state.endDate)) {
      state._endDay = pad(format(state.endDate, 'D'), 2);
      state._endMonth = pad(format(state.endDate, 'M'), 2);
      state._endYear = format(state.endDate, 'YYYY');
    }

    return state;
  }

  generateDateList(params, mode) {
    return this.maskList.map((value, i) => {
      const state = value.slice(0, 1);
      const index = this.props.maskOrder.indexOf(value);
      const placeholderChar = this.props.maskPlaceholder[index];

      if (!this.props.separatorRexExp.test(value)) {
        params = _objectSpread({}, params, {
          'aria-labelledby': this.props.id,
          onKeyDown: this.onKeyDownHandler,
          onMouseUp: selectInput,
          onFocus: () => {
            this.setState({
              focusState: 'focus',
              _listenForPropChanges: false
            });
          },
          onBlur: () => {
            this.setState({
              focusState: 'blur',
              _listenForPropChanges: false
            });
          },
          placeholderChar
        });

        switch (state) {
          case 'd':
            this.refList.push(this["_".concat(mode, "DayRef")]);
            return React.createElement(InputElement, _extends({}, params, {
              id: "".concat(this.props.id, "-").concat(mode, "-day"),
              key: 'd' + i,
              className: classnames(params.className, 'dnb-date-picker__input', 'dnb-date-picker__input--day'),
              size: "2",
              mask: [/[0-3]/, /[0-9]/],
              ref: this["_".concat(mode, "DayRef")],
              onChange: this["set_".concat(mode, "Day")],
              value: this.state["_".concat(mode, "Day")]
            }));

          case 'm':
            this.refList.push(this["_".concat(mode, "MonthRef")]);
            return React.createElement(InputElement, _extends({}, params, {
              id: "".concat(this.props.id, "-").concat(mode, "-month"),
              key: 'm' + i,
              className: classnames(params.className, 'dnb-date-picker__input', 'dnb-date-picker__input--month'),
              size: "2",
              mask: [/[0-1]/, /[0-9]/],
              ref: this["_".concat(mode, "MonthRef")],
              onChange: this["set_".concat(mode, "Month")],
              value: this.state["_".concat(mode, "Month")]
            }));

          case 'y':
            this.refList.push(this["_".concat(mode, "YearRef")]);
            return React.createElement(InputElement, _extends({}, params, {
              id: "".concat(this.props.id, "-").concat(mode, "-year"),
              key: 'y' + i,
              className: classnames(params.className, 'dnb-date-picker__input', 'dnb-date-picker__input--year'),
              size: "4",
              mask: [/[1-2]/, /[0-9]/, /[0-9]/, /[0-9]/],
              ref: this["_".concat(mode, "YearRef")],
              onChange: this["set_".concat(mode, "Year")],
              value: this.state["_".concat(mode, "Year")]
            }));
        }
      }

      return React.createElement("span", {
        key: 's' + i,
        className: "dnb-date-picker--separator",
        "aria-hidden": true
      }, placeholderChar);
    });
  }

  render() {
    const _this$props = this.props,
          {
      id,
      range
      /* eslint-disable-line */
      ,
      maskOrder
      /* eslint-disable-line */
      ,
      maskPlaceholder
      /* eslint-disable-line */
      ,
      separatorRexExp
      /* eslint-disable-line */
      ,
      date
      /* eslint-disable-line */
      ,
      endDate
      /* eslint-disable-line */
      ,
      startDate
      /* eslint-disable-line */
      ,
      minDate
      /* eslint-disable-line */
      ,
      maxDate
      /* eslint-disable-line */
      ,
      onChange
      /* eslint-disable-line */
      ,
      onFocus
      /* eslint-disable-line */
      ,
      onSubmit
      /* eslint-disable-line */
      ,
      onSubmitButtonFocus
      /* eslint-disable-line */
      ,
      showInput
      /* eslint-disable-line */
      ,
      disabled,
      opened,
      status,
      status_state,
      status_animation
    } = _this$props,
          rest = _objectWithoutProperties(_this$props, ["id", "range", "maskOrder", "maskPlaceholder", "separatorRexExp", "date", "endDate", "startDate", "minDate", "maxDate", "onChange", "onFocus", "onSubmit", "onSubmitButtonFocus", "showInput", "disabled", "opened", "status", "status_state", "status_animation"]);

    const {
      focusState
    } = this.state;
    validateDOMAttributes(this.props, rest);
    return React.createElement(Input, {
      id: "".concat(id, "__input"),
      input_state: disabled ? 'disabled' : focusState,
      inputElement: this.renderInputElement,
      disabled: disabled,
      status: !opened ? status : null,
      status_state: status_state,
      status_animation: status_animation,
      submitButton: React.createElement(SubmitButton, _extends({
        id: id,
        disabled: disabled // title={submit_button_title} // Not implemented yet
        ,
        type: "button",
        icon: "calendar",
        variant: "secondary",
        on_submit: onSubmit,
        onKeyUp: this.onKeyUpHandler
      }, rest))
    });
  }

}

_defineProperty(DatePickerInput, "defaultProps", defaultProps);

DatePickerInput.propTypes = process.env.NODE_ENV !== "production" ? propTypes : {};

const selectInput = e => {
  e.target.focus();
  e.target.select();
};

const InputElement = React.forwardRef((props, innerRef) => React.createElement(MaskedInput, _extends({
  guide: true,
  showMask: true,
  keepCharPositions: false // so we can overwrite next value, if it already exists
  ,
  autoComplete: "off",
  ref: innerRef
}, props)));

const pad = (num, size) => ('000000000' + num).substr(-size);

const wait = t => new Promise(r => setTimeout(r, t));