import TyComponent from '../common/component'

const indexList = () => {
  const indexList = []
  const charCodeOfA = 'A'.charCodeAt(0)

  for (let i = 0; i < 26; i++) {
    indexList.push(String.fromCharCode(charCodeOfA + i))
  }

  return indexList
}

TyComponent({
  source: true,
  /**
   * 组件的属性列表
   */
  relations: {
    '../ht-index-anchor/index': {
      type: 'descendant',
      linked() {
        this.updateData()
      },
      linkChanged() {
        this.updateData()
      },
      unlinked() {
        this.updateData()
      }
    }
  },
  properties: {
    sticky: {
      type: Boolean,
      value: true
    },
    zIndex: {
      type: Number,
      value: 1
    },
    highlightColor: {
      type: String,
      value: '#228ef8'
    },
    scrollTop: {
      type: Number,
      value: 0,
      observer: 'onScroll'
    },
    stickyOffsetTop: {
      type: Number,
      value: 0
    },
    indexList: {
      type: Array,
      value: indexList()
    }
  },

  data: {
    activeAnchorIndex: null,
    showSidebar: false
  },

  methods: {
    updateData() {
      this.timer && clearTimeout(this.timer)

      this.timer = setTimeout(() => {
        this.children = this.getRelationNodes('../ht-index-anchor/index')
        this.setData({
          showSidebar: !!this.children.length
        })

        this.setRect().then(() => {
          this.onScroll()
        })
      }, 0)
    },

    setRect() {
      return Promise.all([
        this.setAnchorsRect(),
        this.setListRect(),
        this.setSiderbarRect()
      ])
    },

    setAnchorsRect() {
      return Promise.all(
        this.children.map(anchor => anchor
          .getRect('.ht-index-anchor-wrapper')
          .then(
            (rect) => {
              Object.assign(anchor, {
                height: rect.height,
                top: rect.top + this.data.scrollTop
              })
            }
          ))
      )
    },

    setListRect() {
      return this.getRect('.ht-index-bar').then(
        (rect) => {
          Object.assign(this, {
            height: rect.height,
            top: rect.top + this.data.scrollTop
          })
        }
      )
    },

    setSiderbarRect() {
      return this.getRect('.ht-index-bar__sidebar').then(res => {
        this.sidebar = {
          height: res.height,
          top: res.top
        }
      })
    },

    setDiffData({target, data}) {
      const diffData = {}
      Object.keys(data).forEach(key => {
        if (target.data[key] !== data[key]) {
          diffData[key] = data[key]
        }
      })

      if (Object.keys(diffData).length) {
        target.setData(diffData)
      }
    },

    getAnchorRect(anchor) {
      return anchor
        .getRect('.ht-index-anchor-wrapper')
        .then((rect) => ({
          height: rect.height,
          top: rect.top
        }))
    },

    getActiveAnchorIndex() {
      const {children} = this
      const {sticky, scrollTop, stickyOffsetTop} = this.data

      for (let i = this.children.length - 1; i >= 0; i--) {
        const preAnchorHeight = i > 0 ? children[i - 1].height : 0
        const reachTop = sticky ? preAnchorHeight + stickyOffsetTop : 0

        if (reachTop + scrollTop >= children[i].top) {
          return i
        }
      }

      return -1
    },

    onScroll(test) {
      const {children = []} = this

      if (!children.length) {
        return
      }
      const {
        sticky,
        stickyOffsetTop,
        zIndex,
        highlightColor,
        scrollTop
      } = this.data

      const active = this.getActiveAnchorIndex()
      this.setDiffData({
        target: this,
        data: {
          activeAnchorIndex: active
        }
      })

      if (sticky) {
        let isActiveAnchorSticky = false

        if (active !== -1) {
          isActiveAnchorSticky =
            children[active].top <= stickyOffsetTop + scrollTop
        }

        children.forEach((item, index) => {
          if (index === active) {
            let wrapperStyle = ''
            let anchorStyle = `
              color: ${highlightColor};
            `

            if (isActiveAnchorSticky) {
              wrapperStyle = `
                height: ${children[index].height}px;
              `

              anchorStyle = `
                position: fixed;
                top: ${stickyOffsetTop}px;
                z-index: ${zIndex};
                color: ${highlightColor};
              `
            }

            this.setDiffData({
              target: item,
              data: {
                active: true,
                anchorStyle,
                wrapperStyle
              }
            })
          } else if (index === active - 1) {
            const currentAnchor = children[index]

            const currentOffsetTop = currentAnchor.top
            const targetOffsetTop =
              index === children.length - 1
                ? this.top
                : children[index + 1].top

            const parentOffsetHeight = targetOffsetTop - currentOffsetTop
            const translateY = parentOffsetHeight - currentAnchor.height

            const anchorStyle = `
              position: relative;
              transform: translate3d(0, ${translateY}px, 0);
              z-index: ${zIndex};
              color: ${highlightColor};
            `

            this.setDiffData({
              target: item,
              data: {
                active: true,
                anchorStyle
              }
            })
          } else {
            this.setDiffData({
              target: item,
              data: {
                active: false,
                anchorStyle: '',
                wrapperStyle: ''
              }
            })
          }
        })
      }
    },

    onClick(event) {
      this.scrollToAnchor(event.target.dataset.index)
    },

    onTouchMove(event) {
      const sidebarLength = this.children.length
      const touch = event.touches[0]
      const itemHeight = this.sidebar.height / sidebarLength
      let index = Math.floor((touch.clientY - this.sidebar.top) / itemHeight)

      if (index < 0) {
        index = 0
      } else if (index > sidebarLength - 1) {
        index = sidebarLength - 1
      }

      this.scrollToAnchor(index)
    },

    onTouchStop() {
      this.scrollToAnchorIndex = null
    },

    scrollToAnchor(index) {
      if (typeof index !== 'number' || this.scrollToAnchorIndex === index) {
        return
      }

      this.scrollToAnchorIndex = index

      const anchor = this.children.find(
        (item) => item.data.index === this.data.indexList[index]
      )

      if (anchor) {
        this.$emit('select', anchor.data.index)
        wx.pageScrollTo({
          duration: 0,
          scrollTop: anchor.top
        })
      }
    }
  }
})
