<template>
  <yt-live-chat-renderer
    class="style-scope yt-live-chat-app"
    style="--scrollbar-width:11px;"
    hide-timestamps
    @mousemove="refreshCantScrollStartTime"
  >
    <ticker
      v-model:messages="paidMessages"
      class="style-scope yt-live-chat-renderer"
      :show-gift-name="showGiftName || undefined"
    />
    <yt-live-chat-item-list-renderer
      class="style-scope yt-live-chat-renderer"
      allow-scroll
    >
      <div
        id="item-scroller"
        ref="scroller"
        class="style-scope yt-live-chat-item-list-renderer animated"
        @scroll="onScroll"
      >
        <div
          id="item-offset"
          ref="itemOffset"
          class="style-scope yt-live-chat-item-list-renderer"
        >
          <div
            id="items"
            ref="items"
            class="style-scope yt-live-chat-item-list-renderer"
            style="overflow: hidden"
            :style="{ transform: `translateY(${Math.floor(scrollPixelsRemaining)}px)` }"
          >
            <template
              v-for="message in messages"
              :key="message.id"
            >
              <text-message
                v-if="message.type === MESSAGE_TYPE_TEXT"
                class="style-scope yt-live-chat-item-list-renderer"
                :time="message.time"
                :avatar-url="message.avatarUrl"
                :author-name="message.authorName"
                :author-type="message.authorType"
                :privilege-type="message.privilegeType"
                :content-parts="getShowContentParts(message)"
                :repeated="message.repeated"
              />
              <paid-message
                v-else-if="message.type === MESSAGE_TYPE_GIFT"
                class="style-scope yt-live-chat-item-list-renderer"
                :time="message.time"
                :avatar-url="message.avatarUrl"
                :author-name="getShowAuthorName(message)"
                :price="message.price"
                :price-text="message.price <= 0 ? getGiftShowNameAndNum(message) : ''"
                :content="message.price <= 0 ? '' : getGiftShowContent(message, showGiftName)"
              />
              <membership-item
                v-else-if="message.type === MESSAGE_TYPE_MEMBER"
                class="style-scope yt-live-chat-item-list-renderer"
                :time="message.time"
                :avatar-url="message.avatarUrl"
                :author-name="getShowAuthorName(message)"
                :privilege-type="message.privilegeType"
                :title="message.title"
              />
              <paid-message
                v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
                class="style-scope yt-live-chat-item-list-renderer"
                :time="message.time"
                :avatar-url="message.avatarUrl"
                :author-name="getShowAuthorName(message)"
                :price="message.price"
                :content="getShowContent(message)"
              />
            </template>
          </div>
        </div>
      </div>
    </yt-live-chat-item-list-renderer>
  </yt-live-chat-renderer>
</template>

<script>
import _ from 'lodash'
import Ticker from './Ticker.vue'
import TextMessage from './TextMessage.vue'
import MembershipItem from './MembershipItem.vue'
import PaidMessage from './PaidMessage.vue'
import * as constants from './constants'
import { defineComponent } from 'vue'
import { useDebounceFn } from '@vueuse/core'

// 瑕佹坊鍔犵殑娑堟伅绫诲瀷
const ADD_MESSAGE_TYPES = [
  constants.MESSAGE_TYPE_TEXT,
  constants.MESSAGE_TYPE_GIFT,
  constants.MESSAGE_TYPE_MEMBER,
  constants.MESSAGE_TYPE_SUPER_CHAT,
]
// 鍙戦€佹秷鎭椂闂撮棿闅旇寖鍥?
const MESSAGE_MIN_INTERVAL = 80
const MESSAGE_MAX_INTERVAL = 1000

// 姣忔鍙戦€佹秷鎭悗澧炲姞鐨勫姩鐢绘椂闂达紝瑕佹瘮MESSAGE_MIN_INTERVAL绋嶅井澶т竴鐐癸紝澶皬浜嗗姩鐢讳笉杩炵画锛屽お澶т簡鍙戦€佹秷鎭椂浼氫腑鏂姩鐢?
// 84 = ceil((1000 / 60) * 5)
const CHAT_SMOOTH_ANIMATION_TIME_MS = 84
// 婊氬姩鏉¤窛绂诲簳閮ㄥ皬浜庡灏戝儚绱犲垯璁や负鍦ㄥ簳閮?
const SCROLLED_TO_BOTTOM_EPSILON = 15

export default defineComponent({
  name: 'ChatRenderer',
  components: {
    Ticker,
    TextMessage,
    MembershipItem,
    PaidMessage
  },
  props: {
    maxNumber: {
      type: Number,
      default: 60
    },
    showGiftName: {
      type: Boolean,
      default: false
    },
    customCss: {
      type: String,
      default: ''
    },
  },
  data() {
    let customStyleElement = document.createElement('style')
    document.head.appendChild(customStyleElement)
    const setCssDebounce = useDebounceFn(() => {
      customStyleElement.innerHTML = this.customCss ?? ''
      console.log('[blivechat] 宸茶缃嚜瀹氫箟鏍峰紡')
    }, 1000)
    return {
      MESSAGE_TYPE_TEXT: constants.MESSAGE_TYPE_TEXT,
      MESSAGE_TYPE_GIFT: constants.MESSAGE_TYPE_GIFT,
      MESSAGE_TYPE_MEMBER: constants.MESSAGE_TYPE_MEMBER,
      MESSAGE_TYPE_SUPER_CHAT: constants.MESSAGE_TYPE_SUPER_CHAT,

      messages: [],                        // 鏄剧ず鐨勬秷鎭?
      paidMessages: [],                    // 鍥哄畾鍦ㄤ笂鏂圭殑娑堟伅

      smoothedMessageQueue: [],            // 骞虫粦娑堟伅闃熷垪锛岀敱澶栭儴璋冪敤addMessages绛夋柟娉曟坊鍔?
      emitSmoothedMessageTimerId: null,    // 娑堣垂骞虫粦娑堟伅闃熷垪鐨勫畾鏃跺櫒ID
      enqueueIntervals: [],                // 鏈€杩戣繘闃熷垪鐨勬椂闂撮棿闅旓紝鐢ㄦ潵浼拌涓嬫杩涢槦鍒楃殑鏃堕棿
      lastEnqueueTime: null,               // 涓婃杩涢槦鍒楃殑鏃堕棿
      estimatedEnqueueInterval: null,      // 浼拌鐨勪笅娆¤繘闃熷垪鏃堕棿闂撮殧

      messagesBuffer: [],                  // 鏆傛椂鏈樉绀虹殑娑堟伅锛屽綋涓嶈兘鑷姩婊氬姩鏃朵細绉帇鍦ㄨ繖
      preinsertHeight: 0,                  // 鎻掑叆鏂版秷鎭箣鍓峣tems鐨勯珮搴?
      isSmoothed: true,                    // 鏄惁骞虫粦婊氬姩锛屽綋娑堟伅澶揩鏃朵笉骞虫粦婊氬姩
      chatRateMs: 1000,                    // 鐢ㄦ潵璁＄畻娑堟伅閫熷害
      scrollPixelsRemaining: 0,            // 骞虫粦婊氬姩鍓╀綑鍍忕礌
      scrollTimeRemainingMs: 0,            // 骞虫粦婊氬姩鍓╀綑鏃堕棿
      lastSmoothChatMessageAddMs: null,    // 涓婃showNewMessages鏃堕棿
      smoothScrollRafHandle: null,         // 骞虫粦婊氬姩requestAnimationFrame鍙ユ焺
      lastSmoothScrollUpdate: null,        // 骞虫粦婊氬姩涓婁竴甯ф椂闂?

      atBottom: true,                      // 婊氬姩鍒板簳閮紝鐢ㄦ潵鍒ゆ柇鑳藉惁鑷姩婊氬姩
      cantScrollStartTime: null,            // 寮€濮嬩笉鑳借嚜鍔ㄦ粴鍔ㄧ殑鏃堕棿锛岀敤鏉ラ槻姝㈠崱浣?

      customStyleElement,

      setCssDebounce,
    }
  },
  computed: {
    canScrollToBottom() {
      return this.atBottom/* || this.allowScroll */
    }
  },
  watch: {
    canScrollToBottom(val) {
      this.cantScrollStartTime = val ? null : new Date()
    },
    watchCustomCss: {
      immediate: true,
      handler(val, oldVal) {
        this.setCssDebounce(val)
      }
    }
  },
  mounted() {
    this.scrollToBottom()
  },
  beforeUnmount() {
    if (this.emitSmoothedMessageTimerId) {
      window.clearTimeout(this.emitSmoothedMessageTimerId)
      this.emitSmoothedMessageTimerId = null
    }
    this.clearMessages()
    document.head.removeChild(this.customStyleElement)
  },
  methods: {
    getGiftShowContent(message) {
      return constants.getGiftShowContent(message, this.showGiftName)
    },
    getGiftShowNameAndNum: constants.getGiftShowNameAndNum,
    getShowContent: constants.getShowContent,
    getShowContentParts: constants.getShowContentParts,
    getShowAuthorName: constants.getShowAuthorName,

    addMessage(message) {
      this.addMessages([message])
    },
    addMessages(messages) {
      this.enqueueMessages(messages)
    },
    setCss(css) {
      this.setCssDebounce(css)
    },
    // 鍚庢倲鍔犺繖涓姛鑳戒簡
    mergeSimilarText(content) {
      content = content.trim().toLowerCase()
      for (let message of this.iterRecentMessages(5)) {
        if (message.type !== constants.MESSAGE_TYPE_TEXT) {
          continue
        }

        let messageContent = message.content.trim().toLowerCase()
        let longer, shorter
        if (messageContent.length > content.length) {
          longer = messageContent
          shorter = content
        } else {
          longer = content
          shorter = messageContent
        }

        if (
          longer.indexOf(shorter) !== -1 // 闀跨殑鍖呭惈鐭殑
          && longer.length - shorter.length < shorter.length // 闀垮害宸緝灏?
        ) {
          this.updateMessage(message.id, {
            $add: {
              repeated: 1
            }
          })
          return true
        }
      }
      return false
    },
    mergeSimilarGift(authorName, price, _freePrice, giftName, num) {
      for (let message of this.iterRecentMessages(5)) {
        if (
          message.type === constants.MESSAGE_TYPE_GIFT
          && message.authorName === authorName
          && message.giftName === giftName
        ) {
          this.updateMessage(message.id, {
            $add: {
              price: price,
              // freePrice: freePrice, // 鏆傛椂娌＄敤鍒?
              num: num
            }
          })
          return true
        }
      }
      return false
    },
    // 浠庢柊鍒拌€佽凯浠um鏉℃秷鎭紝娉ㄦ剰浼氳凯浠moothedMessageQueue锛屼笉浼氳凯浠aidMessages
    *iterRecentMessages(num, onlyCountAddMessages = true) {
      if (num <= 0) {
        return
      }
      for (let arr of this.iterMessageArrs()) {
        for (let i = arr.length - 1; i >= 0 && num > 0; i--) {
          let message = arr[i]
          yield message
          if (!onlyCountAddMessages || this.isAddMessage(message)) {
            num--
          }
        }
        if (num <= 0) {
          break
        }
      }
    },
    // 浠庢柊鍒拌€佽凯浠ｆ秷鎭殑鏁扮粍
    *iterMessageArrs() {
      for (let i = this.smoothedMessageQueue.length - 1; i >= 0; i--) {
        yield this.smoothedMessageQueue[i]
      }
      yield this.messagesBuffer
      yield this.messages
    },
    delMessage(id) {
      this.delMessages([id])
    },
    delMessages(ids) {
      this.enqueueMessages(ids.map(
        id => ({
          type: constants.MESSAGE_TYPE_DEL,
          id
        })
      ))
    },
    clearMessages() {
      this.messages = []
      this.paidMessages = []
      this.smoothedMessageQueue = []
      this.messagesBuffer = []
      this.isSmoothed = true
      this.lastSmoothChatMessageAddMs = null
      this.chatRateMs = 1000
      this.lastSmoothScrollUpdate = null
      this.scrollTimeRemainingMs = this.scrollPixelsRemaining = 0
      this.smoothScrollRafHandle = null
      this.preinsertHeight = 0
      this.maybeResizeScrollContainer()
      if (!this.atBottom) {
        this.scrollToBottom()
      }
    },
    updateMessage(id, newValuesObj) {
      this.enqueueMessages([{
        type: constants.MESSAGE_TYPE_UPDATE,
        id,
        newValuesObj
      }])
    },

    enqueueMessages(messages) {
      // 浼拌杩涢槦鍒楁椂闂撮棿闅?
      if (!this.lastEnqueueTime) {
        this.lastEnqueueTime = new Date()
      } else {
        let curTime = new Date()
        let interval = curTime - this.lastEnqueueTime
        // 鐪熷疄鐨勮繘闃熷垪鏃堕棿闂撮殧妯″紡澶ф鏄繖鏍凤細2500, 300, 300, 300, 2500, 300, ...
        // B绔欐秷鎭湁缂撳啿锛屼細涓€娆″彂澶氭潯娑堟伅銆傝繖閲屾妸娉㈠嘲瑙嗕负鍙戦€佷簡涓€娆＄湡瀹炵殑WS娑堟伅锛屾墍浠ヨ杩囨护鎺夐棿闅斿お灏忕殑
        if (interval > 1000 || this.enqueueIntervals.length < 5) {
          this.enqueueIntervals.push(interval)
          if (this.enqueueIntervals.length > 5) {
            this.enqueueIntervals.splice(0, this.enqueueIntervals.length - 5)
          }
          // 杩欒竟浼拌寰楀敖閲忓ぇ锛屽彧瑕佷笉澶棭鎶婃秷鎭紦鍐插彂瀹屽氨鏄钩婊戠殑銆傛湁MESSAGE_MAX_INTERVAL淇濆簳锛屼笉浼氳娑堟伅寤惰繜澶ぇ
          // 鍏跺疄鍙互鐢ㄥ崟璋冮槦鍒楁眰鏈€澶у€硷紝鍋锋噿涓嶅啓浜?
          this.estimatedEnqueueInterval = Math.max(...this.enqueueIntervals)
        }
        // 涓婃鍏ラ槦鏃堕棿杩樻槸瑕佽缃紝鍚﹀垯浼氬お鏃╂妸娑堟伅缂撳啿鍙戝畬锛岀劧鍚庤緝闀挎椂闂存病鏈夋柊娑堟伅
        this.lastEnqueueTime = curTime
      }

      // 鎶妋essages鍒嗘垚messageGroup锛屾瘡涓粍閲屾渶澶氭湁1涓渶瑕佸钩婊戠殑娑堟伅
      let messageGroup = []
      for (let message of messages) {
        messageGroup.push(message)
        if (this.isAddMessage(message)) {
          this.smoothedMessageQueue.push(messageGroup)
          messageGroup = []
        }
      }
      // 杩樺墿涓嬩笉闇€瑕佸钩婊戠殑娑堟伅
      if (messageGroup.length > 0) {
        if (this.smoothedMessageQueue.length > 0) {
          // 鍜屼笂涓€缁勫悎骞?
          let lastMessageGroup = this.smoothedMessageQueue[this.smoothedMessageQueue.length - 1]
          for (let message of messageGroup) {
            lastMessageGroup.push(message)
          }
        } else {
          // 鑷繁涓€涓粍
          this.smoothedMessageQueue.push(messageGroup)
        }
      }

      if (!this.emitSmoothedMessageTimerId) {
        this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages)
      }
    },
    isAddMessage({ type }) {
      return ADD_MESSAGE_TYPES.indexOf(type) !== -1
    },
    emitSmoothedMessages() {
      this.emitSmoothedMessageTimerId = null
      if (this.smoothedMessageQueue.length <= 0) {
        return
      }

      // 浼拌鐨勪笅娆¤繘闃熷垪鍓╀綑鏃堕棿
      let estimatedNextEnqueueRemainTime = 10 * 1000
      if (this.estimatedEnqueueInterval) {
        estimatedNextEnqueueRemainTime = Math.max(this.lastEnqueueTime - new Date() + this.estimatedEnqueueInterval, 1)
      }
      // 璁＄畻鍙戦€佺殑娑堟伅鏁帮紝淇濊瘉鍦ㄤ笅娆¤繘闃熷垪涔嬪墠鍙戝畬
      // 涓嬫杩涢槦鍒椾箣鍓嶅簲璇ュ彂澶氬皯鏉℃秷鎭?
      let shouldEmitGroupNum = Math.max(this.smoothedMessageQueue.length, 0)
      // 涓嬫杩涢槦鍒椾箣鍓嶆渶澶氳兘鍙戝灏戞
      let maxCanEmitCount = estimatedNextEnqueueRemainTime / MESSAGE_MIN_INTERVAL
      // 杩欐鍙戝灏戞潯娑堟伅
      let groupNumToEmit
      if (shouldEmitGroupNum < maxCanEmitCount) {
        // 闃熷垪涓秷鎭暟寰堝皯锛屾瘡娆″彂1鏉′篃鑳藉彂瀹?
        groupNumToEmit = 1
      } else {
        // 姣忔鍙?鏉′互涓婏紝淇濊瘉鎸夋渶蹇€熷害鑳藉彂瀹?
        groupNumToEmit = Math.ceil(shouldEmitGroupNum / maxCanEmitCount)
      }

      // 鍙戞秷鎭?
      let messageGroups = this.smoothedMessageQueue.splice(0, groupNumToEmit)
      let mergedGroup = []
      for (let messageGroup of messageGroups) {
        for (let message of messageGroup) {
          mergedGroup.push(message)
        }
      }
      this.handleMessageGroup(mergedGroup)

      if (this.smoothedMessageQueue.length <= 0) {
        return
      }
      // 娑堟伅娌″彂瀹岋紝璁＄畻涓嬫鍙戞秷鎭椂闂?
      let sleepTime
      if (groupNumToEmit === 1) {
        // 闃熷垪涓秷鎭暟寰堝皯锛岄殢渚垮畾涓猍MESSAGE_MIN_INTERVAL, MESSAGE_MAX_INTERVAL]鐨勬椂闂?
        sleepTime = estimatedNextEnqueueRemainTime / this.smoothedMessageQueue.length
        sleepTime *= 0.5 + Math.random()
        if (sleepTime > MESSAGE_MAX_INTERVAL) {
          sleepTime = MESSAGE_MAX_INTERVAL
        } else if (sleepTime < MESSAGE_MIN_INTERVAL) {
          sleepTime = MESSAGE_MIN_INTERVAL
        }
      } else {
        // 鎸夋渶蹇€熷害鍙?
        sleepTime = MESSAGE_MIN_INTERVAL
      }
      this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages, sleepTime)
    },

    handleMessageGroup(messageGroup) {
      if (messageGroup.length <= 0) {
        return
      }

      for (let message of messageGroup) {
        switch (message.type) {
          case constants.MESSAGE_TYPE_TEXT:
          case constants.MESSAGE_TYPE_GIFT:
          case constants.MESSAGE_TYPE_MEMBER:
          case constants.MESSAGE_TYPE_SUPER_CHAT:
            // 杩欓噷澶勭悊鐨勭被鍨嬭鍜?ADD_MESSAGE_TYPES 涓€鑷?
            this.handleAddMessage(message)
            break
          case constants.MESSAGE_TYPE_DEL:
            this.handleDelMessage(message)
            break
          case constants.MESSAGE_TYPE_UPDATE:
            this.handleUpdateMessage(message)
            break
        }
      }

      this.maybeResizeScrollContainer()
      this.flushMessagesBuffer()
      this.$nextTick(this.maybeScrollToBottom)
    },
    handleAddMessage(message) {
      // 娣诲姞涓€涓湰鍦版椂闂寸粰Ticker鐢紝闃叉鏈湴鏃堕棿鍜屾湇鍔″櫒鏃堕棿鐩稿樊寰堝ぇ鐨勬儏鍐?
      message.addTime = new Date()

      if (message.type !== constants.MESSAGE_TYPE_TEXT) {
        this.paidMessages.unshift(_.cloneDeep(message))
        const MAX_PAID_MESSAGE_NUM = 100
        if (this.paidMessages.length > MAX_PAID_MESSAGE_NUM) {
          this.paidMessages.splice(MAX_PAID_MESSAGE_NUM, this.paidMessages.length - MAX_PAID_MESSAGE_NUM)
        }
      }

      // 涓嶇煡閬揷loneDeep鎷疯礉Vue鐨勫搷搴斿紡瀵硅薄浼氫笉浼氭湁闂锛屼繚闄╄捣瑙佹妸杩欏彞鏀惧湪鍚庨潰
      this.messagesBuffer.push(message)
    },
    handleDelMessage({ id }) {
      let arrs = [this.messages, this.paidMessages, this.messagesBuffer]
      let needResetSmoothScroll = false
      for (let arr of arrs) {
        for (let i = 0; i < arr.length; i++) {
          if (arr[i].id !== id) {
            continue
          }
          arr.splice(i, 1)
          if (arr === this.messages) {
            needResetSmoothScroll = true
          }
          break
        }
      }
      if (needResetSmoothScroll) {
        this.resetSmoothScroll()
      }
    },
    handleUpdateMessage({ id, newValuesObj }) {
      let arrs = [this.messages, this.paidMessages, this.messagesBuffer]
      let needResetSmoothScroll = false
      for (let arr of arrs) {
        for (let message of arr) {
          if (message.id !== id) {
            continue
          }
          this.doUpdateMessage(message, newValuesObj)
          if (arr === this.messages) {
            needResetSmoothScroll = true
          }
          break
        }
      }
      if (needResetSmoothScroll) {
        this.resetSmoothScroll()
      }
    },
    doUpdateMessage(message, newValuesObj) {
      // +=
      let addValuesObj = newValuesObj.$add
      if (addValuesObj !== undefined) {
        for (let name in addValuesObj) {
          message[name] += addValuesObj[name]
        }
      }

      // =
      for (let name in newValuesObj) {
        if (!name.startsWith('$')) {
          message[name] = newValuesObj[name]
        }
      }
    },

    async flushMessagesBuffer() {
      if (this.messagesBuffer.length <= 0) {
        return
      }
      if (!this.canScrollToBottomOrTimedOut()) {
        if (this.messagesBuffer.length > this.maxNumber) {
          // 鏈樉绀烘秷鎭暟 > 鏈€澶у彲鏄剧ず鏁帮紝涓㈠純
          this.messagesBuffer.splice(0, this.messagesBuffer.length - this.maxNumber)
        }
        return
      }

      let removeNum = Math.max(this.messages.length + this.messagesBuffer.length - this.maxNumber, 0)
      if (removeNum > 0) {
        this.messages.splice(0, removeNum)
        // 闃叉鍚屾椂娣诲姞鍜屽垹闄ら」鐩椂鎵€鏈夌殑椤圭洰閲嶆柊娓叉煋 https://github.com/vuejs/vue/issues/6857
        await this.$nextTick()
      }

      this.preinsertHeight = this.$refs.items.clientHeight
      for (let message of this.messagesBuffer) {
        this.messages.push(message)
      }
      this.messagesBuffer = []
      // 绛塱tems楂樺害鍙樺寲
      await this.$nextTick()
      this.showNewMessages()
    },
    showNewMessages() {
      let hasScrollBar = this.$refs.items.clientHeight > this.$refs.scroller.clientHeight
      this.$refs.itemOffset.style.height = `${this.$refs.items.clientHeight}px`
      if (!this.canScrollToBottomOrTimedOut() || !hasScrollBar) {
        return
      }

      // 璁＄畻鍓╀綑鍍忕礌
      this.scrollPixelsRemaining += this.$refs.items.clientHeight - this.preinsertHeight
      this.scrollToBottom()

      // 璁＄畻鏄惁骞虫粦婊氬姩銆佸墿浣欐椂闂?
      if (!this.lastSmoothChatMessageAddMs) {
        this.lastSmoothChatMessageAddMs = performance.now()
      }
      let interval = performance.now() - this.lastSmoothChatMessageAddMs
      this.chatRateMs = (0.9 * this.chatRateMs) + (0.1 * interval)
      if (this.isSmoothed) {
        if (this.chatRateMs < 400) {
          this.isSmoothed = false
        }
      } else {
        if (this.chatRateMs > 450) {
          this.isSmoothed = true
        }
      }
      this.scrollTimeRemainingMs += this.isSmoothed ? CHAT_SMOOTH_ANIMATION_TIME_MS : 0

      if (!this.smoothScrollRafHandle) {
        this.smoothScrollRafHandle = window.requestAnimationFrame(this.smoothScroll)
      }
      this.lastSmoothChatMessageAddMs = performance.now()
    },
    smoothScroll(time) {
      if (!this.lastSmoothScrollUpdate) {
        // 绗竴甯?
        this.lastSmoothScrollUpdate = time
        this.smoothScrollRafHandle = window.requestAnimationFrame(this.smoothScroll)
        return
      }

      let interval = time - this.lastSmoothScrollUpdate
      if (
        this.scrollPixelsRemaining <= 0 || this.scrollPixelsRemaining >= 400  // 宸茬粡婊氬姩鍒板簳閮ㄦ垨鑰呯搴曢儴澶繙鍒欑粨鏉?
        || interval >= 1000 // 绂讳笂涓€甯ф椂闂村お涔咃紝鍙兘鐢ㄦ埛鍒囨崲鍒板叾浠栫綉椤?
        || this.scrollTimeRemainingMs <= 0 // 鏃堕棿宸茬粨鏉?
      ) {
        this.resetSmoothScroll()
        return
      }

      let pixelsToScroll = interval / this.scrollTimeRemainingMs * this.scrollPixelsRemaining
      this.scrollPixelsRemaining -= pixelsToScroll
      if (this.scrollPixelsRemaining < 0) {
        this.scrollPixelsRemaining = 0
      }
      this.scrollTimeRemainingMs -= interval
      if (this.scrollTimeRemainingMs < 0) {
        this.scrollTimeRemainingMs = 0
      }
      this.lastSmoothScrollUpdate = time
      this.smoothScrollRafHandle = window.requestAnimationFrame(this.smoothScroll)
    },
    resetSmoothScroll() {
      this.scrollTimeRemainingMs = this.scrollPixelsRemaining = 0
      this.lastSmoothScrollUpdate = null
      if (this.smoothScrollRafHandle) {
        window.cancelAnimationFrame(this.smoothScrollRafHandle)
        this.smoothScrollRafHandle = null
      }
    },

    maybeResizeScrollContainer() {
      this.$refs.itemOffset.style.height = `${this.$refs.items.clientHeight}px`
      this.$refs.itemOffset.style.minHeight = `${this.$refs.scroller.clientHeight}px`
      this.maybeScrollToBottom()
    },
    maybeScrollToBottom() {
      if (this.canScrollToBottomOrTimedOut()) {
        this.scrollToBottom()
      }
    },
    scrollToBottom() {
      this.$refs.scroller.scrollTop = Math.pow(2, 24)
      this.atBottom = true
    },
    onScroll() {
      this.refreshCantScrollStartTime()
      let scroller = this.$refs.scroller
      this.atBottom = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < SCROLLED_TO_BOTTOM_EPSILON
      this.flushMessagesBuffer()
    },
    canScrollToBottomOrTimedOut() {
      if (this.canScrollToBottom) {
        return true
      }
      // 闃叉鍦∣BS涓崱浣忥紝瓒呰繃涓€瀹氭椂闂翠篃鍙互鑷姩婊氬姩
      return new Date() - this.cantScrollStartTime >= 5 * 1000
    },
    refreshCantScrollStartTime() {
      // 鏈夐紶鏍囦簨浠舵椂鍒锋柊锛岄槻姝㈢敤鎴风湅寮瑰箷鏃惰嚜鍔ㄦ粴鍔?
      if (this.cantScrollStartTime) {
        this.cantScrollStartTime = new Date()
      }
    }
  }
})
</script>

<style src="@/assets/css/youtube/yt-html.css"></style>
<style src="@/assets/css/youtube/yt-live-chat-renderer.css"></style>
<style src="@/assets/css/youtube/yt-live-chat-item-list-renderer.css"></style>
