/// <reference path="./definition.d.ts" />

import * as React from 'react';
import classNames from 'classnames';
import Hammer from 'react-hammerjs';
import Slider from '../slider';
import { setTranslate } from '../util/animation';
import {
  getDaysInMonth,
  zellerFormula,
  valueFormat
} from '../util/date';
import WebApi from '../ajax/webapi';

const today = new Date();
/**
 * 日历起始年份
 */
const startYear = 1950;
const prefix = 'tsp-component-calendar';
class Calendar extends React.Component<TspComponentCalendarProps, any> {
  constructor(props: TspComponentCalendarProps, state: any) {
    super(props, state);
    this.onPanStart = this.onPanStart.bind(this);
    this.onPan = this.onPan.bind(this);
    this.onPanEnd = this.onPanEnd.bind(this);
    this.onTransitionEnd = this.onTransitionEnd.bind(this);
    this.onClickWithDay = this.onClickWithDay.bind(this);
  }

  public static defaultProps: TspComponentCalendarProps = {
    width: document.body.clientWidth,
    height:  document.documentElement.clientHeight,
    year: today.getFullYear(),
    month: today.getMonth(),
    day: today.getDate()
  };

  /**
   * Slider的实例
   */
  private slider: Slider;
  /**
   * 滑动容器
   */
  private sliderElem: HTMLElement;
  /**
   * hammer Element
   */
  private containerElem: HTMLElement;
  /**
   * 当前年月显示
   */
  private dateElem: HTMLElement;
  /**
   * slider索引
   */
  private selectedIndex: number = this.getIndexByDate(this.props.year, this.props.month);
  /**
   * 选中的哪天
   */
  private selectedDay: number[] = [this.selectedIndex, undefined, this.props.day];
  /**
   * 获取上页的年份
   */
  public getPrevYear(selectedIndex: number): number {
    return this.getYearByIndex(selectedIndex - 1);
  }
  /**
   * 获取上一页的月份
   */
  public getPrevMonth(selectedIndex: number): number {
    return this.getMonthByIndex(selectedIndex - 1);
  }
  /**
   * 获取选中的年份
   */
  public getSelectedYear(selectedIndex: number): number {
    return this.getYearByIndex(selectedIndex);
  }
  /**
   * 获取选中的月份
   */
  public getSelectedMonth(selectedIndex: number): number {
    return this.getMonthByIndex(selectedIndex);
  }
  /**
   * 获取下一页年份
   */
  public getNextYear(selectedIndex: number): number {
    return this.getYearByIndex(selectedIndex + 1);
  }
  /**
   * 获取下一页月份
   */
  public getNextMonth(selectedIndex: number): number {
    return this.getMonthByIndex(selectedIndex + 1);
  }

  public componentDidMount(): void {
    this.sliderElem = this.refs['slider'] as HTMLElement;
    this.containerElem = this.refs['container'] as HTMLElement;
    this.dateElem = this.refs['date'] as HTMLElement;
    this.slider = new Slider({
      sliderElem: this.sliderElem,
      containerElem: this.containerElem,
      count: 1200,
      direction: 'horizontal',
      width: this.props.width,
      height: this.props.height,
      selectedIndex: this.selectedIndex
    });

    this.calendarInitial();
    this.setCurrentDayStyle();
    this.setDarkenByDay(this.props.darken);

    this.sliderElem.addEventListener('webkitTransitionEnd', this.onTransitionEnd);
  }

  public componentWillReceiveProps(nextProps: TspComponentCalendarProps): void {
    if (this.props.darken !== nextProps.darken) {
      this.setDarkenByDay(nextProps.darken);
    }
  }

  public shouldComponentUpdate(nextProps: TspComponentCalendarProps): boolean {
    return this.props.refreshId && (this.props.refreshId !== nextProps.refreshId);
  }

  public componentWillUnmount(): void {
    this.sliderElem.removeEventListener('webkitTransitionEnd', this.onTransitionEnd);
  }

  /**
   * 开始滑动
   */
  public onPanStart(evt: TspComponentSliderEvt): void {
    evt.preventDefault();
    this.slider.panStart();
  }

  /**
   * 滑动中
   */
  public onPan(evt: any): void {
    evt.preventDefault();
    this.slider.panMove(evt);
  }

  /**
   * 滑动结束
   */
  public onPanEnd(evt: any): void {
    evt.preventDefault();
    if (this.slider.isPanEnd) {
      return;
    }

    this.containerElem.classList.add('tsp-compoent-slider-disabled');
    this.slider.addPanEndClass();
    this.slider.panEnd(evt);
  }

  /**
   * 监听translate完成事件
   */
  public onTransitionEnd(): void {
    const { afterSelectedIndex } = this.slider.afterChangeIndexs;
    this.selectedIndex = afterSelectedIndex;
    const year = this.getSelectedYear(this.selectedIndex);
    const month = this.getSelectedMonth(this.selectedIndex) + 1;

    this.calendarReplace();
    this.setDarkenByDay(this.props.darken);

    if (this.props.afterChange) {
      this.props.afterChange(this.slider.afterChangeIndexs, [year, month]);
    }

    this.slider.removePanEndClass();
    this.containerElem.classList.remove('tsp-compoent-slider-disabled');
  }

  /**
   * 日历点击事件
   */
  public onClickWithDay(e: any): void {
    if (e.target.classList.contains(`${prefix}-table-currentMonth`)) {
      const dayInMonthElem = e.target;
      const dayCurrentElem = this.sliderElem.querySelector(`.${prefix}-day-current`);
      const year = this.getSelectedYear(this.selectedIndex);
      const month = this.getSelectedMonth(this.selectedIndex) + 1;
      const index = parseInt(dayInMonthElem['dataset'].index);

      if (dayCurrentElem) {
        dayCurrentElem.classList.remove(`${prefix}-day-current`);
      }
      dayInMonthElem.classList.add(`${prefix}-day-current`);
      this.selectedDay = [this.selectedIndex, index, parseInt(dayInMonthElem.textContent)];
      if (this.props.onClick) {
        this.props.onClick([year, month, this.selectedDay[2]]);
      }
    }
  }

  /**
   * 根据索引获得年份
   */
  public getYearByIndex(index: number): number {
    return parseInt((index / 12).toString()) + startYear;
  }

  /**
   * 根据索引获得月数
   */
  public getMonthByIndex(index: number): number {
    return index % 12;
  }
  /**
   * 根据日期获得索引
   */
  public getIndexByDate(year: number, month: number): number {
    return (year - startYear) * 12 + month;
  }

  /**
   * 渲染日历表需要的数据
   */
  public getCalendarRenderData(index: number): TspComponentCalendarGetCalendarRenderDataReturn {
    const selectedYear = this.getSelectedYear(index);
    const selectedMonth = this.getSelectedMonth(index);
    const currentMonthDays = getDaysInMonth(selectedYear, selectedMonth, 'array');
    const firstDayInMonth = zellerFormula(selectedYear, selectedMonth, 1);
    const prevSliceIndex = firstDayInMonth === 0 ? 7 : firstDayInMonth;
    const prevMonthDays = getDaysInMonth(this.getPrevYear(index), this.getPrevMonth(index), 'array');
    const prevMonthDisplayDays = prevMonthDays.reverse().slice(0, prevSliceIndex).reverse();
    const nextMonthDaysSliceEnd = 42 - currentMonthDays.length - prevMonthDisplayDays.length;
    const nextMonthDays = getDaysInMonth(this.getNextYear(index), this.getNextMonth(index), 'array').slice(0, nextMonthDaysSliceEnd);

    return {
      prevMonthDisplayDays,
      currentMonthDays,
      nextMonthDays,
      table: prevMonthDisplayDays.concat(currentMonthDays, nextMonthDays)
    };
  }

  /**
   * 渲染日历表
   */
  public calendarRender(renderData: TspComponentCalendarGetCalendarRenderDataReturn): DocumentFragment {
    const fragment = document.createDocumentFragment();
    const prevMonthDisplayDaysLength = renderData.prevMonthDisplayDays.length;
    const currentMonthDaysLength = renderData.currentMonthDays.length;
    const nextMonthDisplayDaysStart = prevMonthDisplayDaysLength + currentMonthDaysLength;
    const divElem = document.createElement('div');
    const getSelectedYear = this.getSelectedYear(this.selectedIndex);
    const getSelectedMonth = this.getSelectedMonth(this.selectedIndex) + 1;
    const yearMonth = `${getSelectedYear}-${valueFormat(getSelectedMonth, 'string')}-`;
    let i = 0;

    divElem.classList.add(`${prefix}-table-day`);
    for (i = 0; i < 42; i++) {
      const tempElem = divElem.cloneNode(false) as HTMLElement;
      const day = renderData.table[i].toString();
      if (i < prevMonthDisplayDaysLength || i >= nextMonthDisplayDaysStart) {
        tempElem.classList.add(`${prefix}-table-noCurrentMonth`);
        tempElem.dataset.date = '';
      } else {
        tempElem.classList.add(`${prefix}-table-currentMonth`);
        tempElem.dataset.date = yearMonth + valueFormat(day, 'string');
      }

      tempElem.innerText = renderData.table[i].toString();
      tempElem.dataset.index = i.toString();
      fragment.appendChild(tempElem);
    }

    return fragment;
  }

  /**
   * 日历初始化
   */
  public calendarInitial(): void {
    const prevMonthCalendar = this.calendarRender(this.getCalendarRenderData(this.selectedIndex - 1));
    const currentMonthCalendar = this.calendarRender(this.getCalendarRenderData(this.selectedIndex));
    const nextMonthCalendar = this.calendarRender(this.getCalendarRenderData(this.selectedIndex + 1));
    const fragment = document.createDocumentFragment();
    const wrapElem = document.createElement('div');
    const calendarArray = [prevMonthCalendar, currentMonthCalendar, nextMonthCalendar];
    let i = 0;

    wrapElem.classList.add(`${prefix}-table`);
    wrapElem.style.width = this.props.width + 'px';
    for (i = 0; i < 3; i++) {
      const tempElem = wrapElem.cloneNode(false);
      tempElem.appendChild(calendarArray[i]);
      fragment.appendChild(tempElem);
    }

    this.sliderElem.appendChild(fragment);
    this.restPosition();
    this.renderYearMonth();
  }

  /**
   * 替换日历表
   */
  public calendarReplace(): void {
    const prevMonthRenderData = this.getCalendarRenderData(this.selectedIndex - 1);
    const currentMonthRenderData = this.getCalendarRenderData(this.selectedIndex);
    const nextMonthRenderData = this.getCalendarRenderData(this.selectedIndex + 1);
    const renderDataArray = [prevMonthRenderData, currentMonthRenderData, nextMonthRenderData];
    const children = this.sliderElem.children;
    const getSelectedYear = this.getSelectedYear(this.selectedIndex);
    const getSelectedMonth = this.getSelectedMonth(this.selectedIndex) + 1;
    const yearMonth = `${getSelectedYear}-${valueFormat(getSelectedMonth, 'string')}-`;
    let [i, j] = [0, 0];

    for (i = 0; i < 42; i++) {
      for (j = 0 ; j < 3; j++) {
        const prevMonthDisplayDaysLength = renderDataArray[j].prevMonthDisplayDays.length;
        const currentMonthDaysLength = renderDataArray[j].currentMonthDays.length;
        const nextMonthDisplayDaysStart = prevMonthDisplayDaysLength + currentMonthDaysLength;
        const day = renderDataArray[j].table[i].toString();

        if (i < prevMonthDisplayDaysLength || i >= nextMonthDisplayDaysStart) {
          children[j].children[i].classList.add(`${prefix}-table-noCurrentMonth`);
          children[j].children[i].classList.remove(`${prefix}-table-currentMonth`);
          children[j].children[i]['dataset'].date = '';
        } else {
          children[j].children[i].classList.add(`${prefix}-table-currentMonth`);
          children[j].children[i].classList.remove(`${prefix}-table-noCurrentMonth`);
          children[j].children[i]['dataset'].date = yearMonth + valueFormat(day, 'string');
        }

        if (this.selectedIndex !== this.selectedDay[0]) {
          if (children[j].children[i].classList.contains(`${prefix}-day-current`)) {
            children[j].children[i].classList.remove(`${prefix}-day-current`);
          }
        } else if (j === 1) {
          children[1].children[this.selectedDay[1]].classList.add(`${prefix}-day-current`);
        }

        children[j].children[i].textContent = day;
        children[j].children[i]['dataset'].index = i.toString();
      }
    }
    this.restPosition();
    this.renderYearMonth();
  }

  /**
   * 设置选中日期的样式
   */
  public setCurrentDayStyle(): void {
    const currentSliderElem = this.sliderElem.children[1];
    for (let i = 0; i < 42; i++) {
      const children = currentSliderElem.children[i];
      if (children.classList.contains(`${prefix}-table-currentMonth`)) {
        if (children.textContent === this.props.day.toString()) {
          children.classList.add(`${prefix}-day-current`);
          this.selectedDay = [this.selectedIndex, i, this.props.day];
          break;
        }
      }
    }
  }

  /**
   * 重置滑块的位置
   */
  public restPosition(): void {
    const position = this.slider.getMoveBeforePosition();
    setTranslate(this.sliderElem, position.x, position.y);
  }

  /**
   * 渲染年月
   */
  public renderYearMonth(): void {
    this.dateElem.innerText = `${this.getSelectedYear(this.selectedIndex)}年${this.getSelectedMonth(this.selectedIndex) + 1}月`;
  }

  /**
   * 根据日期设置加深样式
   */
  public setDarkenByDay(days: string[]): void {
    const elem = this.sliderElem.children[1];
    if (days.length) {
      const length = days.length;
      let count = 0;

      for (let j = 0; j < length; j++) {
        for (let i = count; i < 42; i++) {
          const dayElem = elem.children[i] as HTMLElement;
          if (dayElem.classList.contains('tsp-component-calendar-table-currentMonth')) {
            if (dayElem.dataset.date === days[j]) {
              dayElem.dataset.darken = 'true';
              count = i + 1;
              break;
            } else {
              dayElem.dataset.darken = 'false';
            }
          }
        }
      }
    } else {
      for (let i = 0; i < 42; i++) {
        const dayElem = elem.children[i] as HTMLElement;
        dayElem.dataset.darken = 'false';
      }
    }
  }

  public render(): JSX.Element {
    const contentStyle = { width: this.props.width };
    const flexStyle = { width: this.props.width * 3 };

    return (
      <div
        className={classNames({
          [prefix]: true,
          [this.props.className]: this.props.className
        })}
        ref="container"
      >
        <div className={`${prefix}-date`} ref="date" />
        <Hammer
          className={`${prefix}-hammer`}
          onPanStart={this.onPanStart}
          onPan={this.onPan}
          onPanEnd={this.onPanEnd}
        >
          <div style={contentStyle}>
            <Hammer onTap={this.onClickWithDay}>
              <div
                className={`${prefix}-flex`}
                style={flexStyle}
                ref="slider"
              />
            </Hammer>
          </div>
        </Hammer>

        {this.props.leftArrow ? (
          <div className={`${prefix}-left-arrow`}>{this.props.leftArrow}</div>
        ) : null}
        {this.props.rightArrow ? (
          <div className={`${prefix}-right-arrow`}>{this.props.rightArrow}</div>
        ) : null}
      </div>
    );
  }
}

export default Calendar;