import getFloatedTargetPos from '../utils/getFloatedTargetPos' import isTouchDevice from '../utils/isTouchDevice' import dateLt from '../utils/dateLt' import dateEq from '../utils/dateEq' import supportDom from '../decorators/supportDom' import {

addDays,
addMonths,
compareAsc,
endOfMonth,
getDay,
getDaysInMonth,
getMonth,
getYear,
isFuture,
range,
set,
startOfDay,
startOfMonth,
subMonths,
throttle,
toPixel,
format

} from '../utils' import { DEFAULT_TIMEZONE, DEFAULT_LOCALE } from '../consts'

const DEFAULT_WEEK_HEADER_ITEMS = [

{ id: 'monday', text: '一' },
{ id: 'tuesday', text: '二' },
{ id: 'wednesday', text: '三' },
{ id: 'thursday', text: '四' },
{ id: 'friday', text: '五' },
{ id: 'saturday', text: '六' },
{ id: 'sunday', text: '日' }

]

const CELL_TYPE_EMPTY = Symbol('CELL_TYPE_EMPTY') const CELL_TYPE_DAY = Symbol('CELL_TYPE_DAY')

@supportDom export default class DateMenu {

constructor({ date, startDate, endDate, options = {} }) {
  this.date = date
  this.date2 = addMonths(date, 1)
  this.startDate = startDate
  this.endDate = endDate
  this.hoveredCellData = null
  this.options = options
  this.noFuture = options.noFuture || false
  this.tz = options.tz || DEFAULT_TIMEZONE
  this.locale = options.locale || DEFAULT_LOCALE
  this.captionPattern = options.captionPattern || 'yyyy MMMM'
  this.weekHeaderItems = options.weekHeaderItems || DEFAULT_WEEK_HEADER_ITEMS
  this.isVisible = false
  this.init()
}

init() {
  this.addMenu()
  this.addEvents()
}

useSingleMenu() {
  const { useSingleMenu, isStatic } = this.options
  return isTouchDevice() || isStatic || useSingleMenu
}

setHoveredCell(data) {
  const dataChanged = JSON.stringify(data) !== JSON.stringify(this.hoveredCellData)
  if (dataChanged && (! this.endDate)) {
    this.hoveredCellData = data
    this.drawTables()
  }
}

getTableRows(date) {

  const { noFuture, startDate, endDate } = this

  const daysInMonth = getDaysInMonth(date)
  const firstDateOfMonth = startOfMonth(date)
  const toEmptyCell = () => ({ type: CELL_TYPE_EMPTY })

  const firstWeekday = getDay(startOfMonth(date))
  const beforeWeekday = ((firstWeekday - 1) === -1) ? 6 : (firstWeekday - 1)
  const emptyHeadRows = range(1, beforeWeekday + 1).map(toEmptyCell)

  const initialStartDate = startOfDay(startDate)
  const initialEndDate = startOfDay(endDate)

  // 00:00:00
  // eslint-disable-next-line prefer-const
  let startOfStartDate = initialStartDate

  // eslint-disable-next-line prefer-const
  let startOfEndDate = initialEndDate

  if (startDate && (! endDate) && this.hoveredCellData) {
    const { year: y, month: m, date: d } = this.hoveredCellData
    startOfEndDate = set(startOfStartDate, { year: y, month: m, date: d })
    if (dateLt(startOfEndDate, startOfStartDate)) {
      [startOfStartDate, startOfEndDate] = [startOfEndDate, startOfStartDate]
    }
  }

  const formatDate = date => {
    return format(date, 'yyyy-MM-dd', { timezone: this.tz, locale: this.locale })
  }
  const today = formatDate(new Date())

  const rows = range(1, daysInMonth + 1).map(day => {

    const d = addDays(firstDateOfMonth, day - 1)
    const resCompareStart = compareAsc(startOfStartDate, d)
    const resCompareEnd = compareAsc(startOfEndDate, d)

    return {
      type: CELL_TYPE_DAY,
      isStartDate: dateEq(initialStartDate, d),
      isEndDate: dateEq(initialEndDate, d),
      isSelected: (resCompareStart <= 0) && (resCompareEnd >= 0),
      isDisabled: noFuture && isFuture(d),
      isToday: (today === formatDate(d)),
      day
    }
  })

  const lastWeekday = getDay(endOfMonth(date))
  const emptyDays = ((7 - lastWeekday) % 7)
  const emptyTailRows = range(1, emptyDays + 1).map(toEmptyCell)

  return emptyHeadRows.concat(rows).concat(emptyTailRows)
}

setCaption() {
  const { date, date2 } = this
  const options = { timezone: this.tz, locale: this.locale }
  if (this.useSingleMenu()) {
    this.caption.textContent = format(date, this.captionPattern, options)
  }
  else {
    this.caption1.textContent = format(date, this.captionPattern, options)
    this.caption2.textContent = format(date2, this.captionPattern, options)
  }
}

drawTables() {
  if (this.useSingleMenu()) {
    const rows = this.getTableRows(this.date)
    this.table.innerHTML = this.getTableHtml(rows)
  }
  else {
    this.table1.innerHTML = this.getTableHtml(this.getTableRows(this.date))
    this.table2.innerHTML = this.getTableHtml(this.getTableRows(this.date2))
  }
}

setDate({ date, startDate, endDate }) {
  if (date) {
    this.date = date
    this.date2 = addMonths(date, 1)
    this.setCaption()
  }
  if (typeof startDate !== 'undefined') {
    this.startDate = startDate
  }
  if (typeof endDate !== 'undefined') {
    this.endDate = endDate
  }
  this.drawTables()
}

getWeekHeaderItems() {
  return this.weekHeaderItems.map(item => `<li>${item.text}</li>`)
    .join('')
}

getTableHtml(rows) {
  return rows.map((row, i) => {
    const rowHtml = this.getTdHtml(row)
    const r = i % 7
    if (r === 0) {
      return `<tr>${rowHtml}`
    }
    if (r === 6) {
      return `${rowHtml}</tr>`
    }
    return rowHtml
  }).join('')
}

getTdHtml(row) {
  if (row.type === CELL_TYPE_EMPTY) {
    return '<td></td>'
  }
  if (row.isDisabled) {
    return `<td class="cell js-disabled">${row.day}</td>`
  }
  if (row.isStartDate || row.isEndDate) {
    return `<td class="cell selected-ex" data-date-table-cell>${row.day}</td>`
  }
  if (row.isSelected) {
    return `<td class="cell selected" data-date-table-cell>${row.day}</td>`
  }
  if (this.options.highlightToday && row.isToday) {
    return `<td class="cell today" data-date-table-cell>${row.day}</td>`
  }
  return `<td class="cell" data-date-table-cell>${row.day}</td>`
}

addMenu() {
  const dom = document.createElement('div')
  const className = this.options.isStatic ? 'date-menu static' : 'date-menu'
  dom.className = className

  if (this.useSingleMenu()) {
    dom.innerHTML = `
      <div class="date-menu-content">
        <div class="date-menu-caption" data-menu-caption></div>
        <ul class="date-menu-week-header">
          ${this.getWeekHeaderItems()}
        </ul>
        <table class="date-menu-date-table" data-date-table></table>
        <button class="date-menu-btn-prev" data-btn-prev>
          <i class="icon icon-chevron-left"></i>
        </button>
        <button class="date-menu-btn-next" data-btn-next>
          <i class="icon icon-chevron-right"></i>
        </button>
      </div>
    `
    this.caption = dom.querySelector('[data-menu-caption]')
    this.table = dom.querySelector('[data-date-table]')
    this.btnPrev = dom.querySelector('[data-btn-prev]')
    this.btnNext = dom.querySelector('[data-btn-next]')
  }
  else {
    dom.innerHTML = `
      <div class="date-menu-content">
        <div class="date-menu-caption" data-menu-caption1></div>
        <ul class="date-menu-week-header">
          ${this.getWeekHeaderItems()}
        </ul>
        <table class="date-menu-date-table" data-date-table1></table>
        <button class="date-menu-btn-prev" data-btn-prev>
          <i class="icon icon-chevron-left"></i>
        </button>
      </div>
      <div class="date-menu-content second-content">
        <div class="date-menu-caption" data-menu-caption2></div>
        <ul class="date-menu-week-header">
          ${this.getWeekHeaderItems()}
        </ul>
        <table class="date-menu-date-table" data-date-table2></table>
        <button class="date-menu-btn-next" data-btn-next>
          <i class="icon icon-chevron-right"></i>
        </button>
      </div>
    `
    this.caption1 = dom.querySelector('[data-menu-caption1]')
    this.caption2 = dom.querySelector('[data-menu-caption2]')
    this.table1 = dom.querySelector('[data-date-table1]')
    this.table2 = dom.querySelector('[data-date-table2]')
    this.btnPrev = dom.querySelector('[data-btn-prev]')
    this.btnNext = dom.querySelector('[data-btn-next]')
  }

  const container = this.options.dom

  if (container) {
    container.appendChild(dom)
  }
  else {
    document.body.appendChild(dom)
  }
  this.dom = dom
}

findTable(target) {
  let node = target
  while (node.parentNode) {
    node = node.parentNode
    if (node.tagName === 'TABLE') {
      return node
    }
  }
  return null
}

addEvents() {
  this.addEvent(this.btnPrev, 'click', event => {
    this.setDate({ date: subMonths(this.date, 1) })
  })

  this.addEvent(this.btnNext, 'click', () => {
    this.setDate({ date: addMonths(this.date, 1) })
  })

  if (this.useSingleMenu()) {
    this.addEvent(this.table, 'click', event => {
      if ('dateTableCell' in event.target.dataset) {
        const res = {
          year: getYear(this.date),
          month: getMonth(this.date),
          date: parseInt(event.target.textContent.trim(), 10)
        }
        this.fire('td-click', event, res)
      }
    })
  }
  else {
    this.addEvent(this.table1, 'click', event => {
      if ('dateTableCell' in event.target.dataset) {
        const res = {
          year: getYear(this.date),
          month: getMonth(this.date),
          date: parseInt(event.target.textContent.trim(), 10)
        }
        this.fire('td-click', event, res)
      }
    })
    this.addEvent(this.table2, 'click', event => {
      if ('dateTableCell' in event.target.dataset) {
        const res = {
          year: getYear(this.date2),
          month: getMonth(this.date2),
          date: parseInt(event.target.textContent.trim(), 10)
        }
        this.fire('td-click', event, res)
      }
    })

    if (this.options.useMouseOver) {
      this.addEvent(this.dom, 'mouseover', throttle(event => {
        if ('dateTableCell' in event.target.dataset) {
          const table = this.findTable(event.target)
          const date = ('dateTable1' in table.dataset) ? this.date : this.date2
          const res = {
            year: getYear(date),
            month: getMonth(date),
            date: parseInt(event.target.textContent.trim(), 10)
          }
          return this.fire('td-mouseover', event, res)
        }
        this.fire('td-mouseover', event, null)
      }, 300))
    }

  }
}

pos(src) {
  const { dom } = this
  const { pos } = getFloatedTargetPos({
    src,
    target: dom,
    place: 'bottom',
    align: 'left',
    offset: 4
  })
  dom.style.left = toPixel(pos.left)
  dom.style.top = toPixel(pos.top)
}

show(src) {
  const { dom } = this
  dom.style.opacity = 0

  if (this.options.isStatic) {
    dom.style.display = 'inline-block'
  }
  else {
    dom.style.display = 'block'
  }

  if (src) {
    this.pos(src)
  }
  dom.style.opacity = 1
  this.isVisible = true
}

hide() {
  this.dom.style.display = 'none'
  this.isVisible = false
}

destroy() {
  this.caption = null
  this.btnPrev = null
  this.btnNext = null
  this.table = null
  this.dom.remove()
  this.menu = null
}

}