import './style.less'

import classnames from 'classnames'
import * as React from 'react'

import {
  LONG_TOUCH_DURATION,
  MAX_MOVE,
  PAD_WIDTH_LIST,
  PHONE_WIDTH_LIST
} from '../common'

interface ImgItem {
  [propName: string]: any
}

export interface Props {
  className?: string
  widthList?: Array<number>
  isPad?: boolean
  imgList: Array<ImgItem>
  useDivInner?: boolean
  onClick?: (e: any, imgItem: ImgItem, index: number) => void
  onSuccess: () => void
  onFailure: () => void
  onLongTouch?: () => void
}

function getDPR() {
  let dpr = Math.floor(window.devicePixelRatio) || 1
  if (dpr > 3) dpr = 3
  if (dpr < 1) dpr = 1
  return dpr
}

export default class Imager extends React.Component<Props, object> {
  public static defaultProps = {
    className: '',
    onSuccess() {},
    onFailure() {}
  }
  public dpr = getDPR()
  public container: HTMLDivElement | null = null
  private touchTimer: Number | null = null
  private touchX = 0
  private touchY = 0
  public componentDidMount() {
    this.initImage()
  }
  public initImage() {
    const { imgList, onSuccess, onFailure } = this.props
    let sequence = Promise.resolve()
    imgList.forEach((listItem, index) => {
      sequence = sequence.then(() => {
        return this.createImage(listItem, index)
      })
    })
    sequence.then(onSuccess, onFailure)
  }
  public createImage(listItem: ImgItem, index: number): Promise<any> {
    const { dpr, props, container } = this
    const { widthList, onClick, useDivInner, isPad } = props
    const { height, divInner } = listItem
    const isDivInner = typeof divInner === 'undefined' ? useDivInner : divInner
    return new Promise((resolve, reject) => {
      const box = document.createElement('div')
      box.className = 'imager-container'
      if (height) box.style.height = height
      if (container) container.appendChild(box)
      const nodeType = isDivInner ? 'div' : 'img'
      const imgEl = document.createElement(nodeType)
      imgEl.className = 'imager-inner'
      if (onClick) {
        imgEl.addEventListener('click', event => {
          onClick(event, listItem, index)
        })
      }
      const list =
        widthList && widthList.length
          ? widthList
          : isPad
          ? PAD_WIDTH_LIST
          : PHONE_WIDTH_LIST
      const resource = new Image()
      const miniImg = listItem[list[0]]
      const suitableImg = listItem[list[dpr - 1]]

      resource.onload = () => {
        box.style.backgroundImage = `url(${miniImg})`
        box.style.backgroundSize = `100% 100%`
        if (isDivInner) {
          imgEl.style.backgroundImage = `url(${suitableImg})`
        } else {
          const el = imgEl as HTMLImageElement
          el.src = suitableImg
          el.style.height = height
        }
        box.appendChild(imgEl)
        resolve()
      }
      resource.onerror = err => {
        reject(err)
      }

      resource.src = miniImg
    })
  }
  public onTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
    const { pageX, pageY } = e.touches[0]
    this.touchX = pageX
    this.touchY = pageY
    this.touchTimer = Date.now()
  }
  public onTouchEnd = (e: React.TouchEvent<HTMLDivElement>) => {
    const touchDuration = Date.now() - (this.touchTimer as number)
    const { pageX, pageY } = e.changedTouches[0]
    const { onLongTouch } = this.props
    const { touchX, touchY } = this
    const isMove =
      pageX - touchX > MAX_MOVE ||
      touchX - pageX > MAX_MOVE ||
      pageY - touchY > MAX_MOVE ||
      touchY - pageY > MAX_MOVE
    if (touchDuration > LONG_TOUCH_DURATION && onLongTouch && !isMove) {
      onLongTouch()
    }
  }
  public render() {
    const { className, isPad } = this.props
    return (
      <div
        onTouchStart={this.onTouchStart}
        onTouchEnd={this.onTouchEnd}
        onTouchCancel={this.onTouchEnd}
        ref={el => (this.container = el)}
        className={classnames('ph-imager', className, { 'is-pad': isPad })}
      />
    )
  }
}
