<template>
  <div class="pc-player-container">
    <div class="pc-player-wrapper">
      <div class="pc-player-left">
        <div class="video-section">
          <div class="video-title">{{ media.name || tpl.name }}</div>
          <div class="video-player-wrap">
            <video-player id="myVideo1" ref="videoPlayer" :playsinline="true" @pause="onPlayerPause($event)"
              @timeupdate="onPlayerTimeupdate($event)" @play="onPlayerStart($event)" @ready="playerReadied"
              @ended="onPlayerEnded($event)" @error="onPlayerError($event)"
              :globalOptions="{controls:true}" :options="options">
            </video-player>
          </div>
          <div class="dialog-footer pt30">
            <el-row class="media-footer">
              <el-col :span="6" class="media-time">
                <span>{{curTimes|useTime}}</span>
                <strong>/</strong>
                <span>{{media.duration|useTime}}</span>
              </el-col>
              <el-col :span="12" class="media-center">
                <span class="volume-control">
                  <span class="vol-icon" @click="toggleMute">音量</span>
                  <el-slider class="volume-slider" :value="volume" :min="0" :max="1" :step="0.05"
                    @input="setVolume"></el-slider>
                </span>
              </el-col>
              <el-col :span="6" class="media-select">
                <div style="margin-top:0px;float: right;">
                  <el-select placeholder="流畅" :value="mediaType" class="media-el-select"  @change="changeMedia">
                    <el-option label="流畅" value="ld"></el-option>
                    <el-option label="标清" value="hls"></el-option>
                  </el-select>
                </div>
              </el-col>
            </el-row>
          </div>
        </div>
      </div>

      <div class="panel-toggle" @click="showPanel = !showPanel">
        <i :class="showPanel ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'"></i>
      </div>

      <div class="pc-player-right" v-show="showPanel">
        <div class="playlist-header">
          <div class="playlist-title">
            <i class="el-icon-video-list"></i>
            课程列表
          </div>
          <div class="playlist-header-row">
            <div class="playlist-count">
              共 {{ list.length }} 个视频
              <span class="finished-count" v-if="finishedCount">，已完成 {{ finishedCount }} 个</span>
            </div>
            <el-switch
              v-model="hideFinished"
              active-text="隐藏已完成"
              inactive-text=""
              size="small"
            ></el-switch>
          </div>
        </div>
        <div class="playlist-body">
          <div class="playlist-item" v-for="(item, index) in filteredList" :key="item.id" :class="{
              'is-active': media.id === item.id,
              'is-finished': item.isFinish == 1,
              'is-current-playing': media.id === item.id && onPlay
            }" @click="loadMedia(item)">
            <div class="item-index" :class="{ 'active-index': media.id === item.id }">
              <span v-if="item.isFinish == 1" class="finish-badge">
                <i class="el-icon-check"></i>
              </span>
              <span v-else>{{ index + 1 }}</span>
            </div>
            <div class="item-info">
              <div class="item-name">{{ item.name }}</div>
              <div class="item-meta">
                <span class="item-duration">学时：{{ item.xs / 10 }}</span>
                <span class="item-progress">{{item|percent}}%</span>
              </div>
            </div>
            <div class="item-action">
              <el-button v-if="media.id === item.id && onPlay" type="warning" size="mini" icon="el-icon-video-pause"
                circle @click.stop="doPause"></el-button>
              <el-button v-else-if="media.id === item.id" type="primary" size="mini" icon="el-icon-video-play" circle
                @click.stop="doPlay"></el-button>
              <el-button v-else type="text" size="mini" icon="el-icon-video-play"
                @click.stop="loadMedia(item)">播放</el-button>
            </div>
          </div>
          <div class="playlist-empty" v-if="list.length === 0">
            <i class="el-icon-document"></i>
            <p>暂无视频</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  import { mapActions } from "vuex";
  import {
    getPercent
  } from '@/utils/index.js'
  import {
    videoPlayer
  } from 'vue-video-player';
  import 'video.js/dist/video-js.css'
  import {
    httpServer
  } from "@/components/httpServer/httpServer.js";
  export default {
    data() {
      return {
        courseId: 0,
        mediaId: 0,
        media: {},
        options: {
          controls: true,
          autoplay: true,
          muted: true,
          loop: false,
          preload: "auto",
          language: 'zh-CN',
          aspectRatio: '16:9',
          fluid: true,
          sources: [],
          poster: '',
          notSupportedMessage: '无法播放媒体源',
          playtimes: '',
          controlBar: {
            timeDivider: true,
            durationDisplay: true,
            remainingTimeDisplay: false,
            fullscreenToggle: true
          }
        },
        curTimes: 0,
        tpl: {},
        info: {isBan:0},
        list: [],
        timer: false,
        tickNum: 0,
        prevTime: 0,
        isReady: false,
        onPlay: false,
        volume: 0.5,
        isMuted: false,
        mediaType: 'hls',
        hasSeeked: false,
        hideFinished: false,
        showPanel: true
      };
    },
    components: {
      videoPlayer
    },
    computed: {
      finishedCount() {
        return this.list.filter(i => i.isFinish == 1).length;
      },
      filteredList() {
        if (this.hideFinished) {
          return this.list.filter(i => i.isFinish != 1)
        }
        return this.list
      },
      playPercent() {
        if (!this.media.duration) return 0;
        if(this.media.isFinish ==1) return 100;
        return +((this.curTimes / this.media.duration) * 100).toFixed(1);
      }
    },
    filters: {
      percent(item){
        if( item.isFinish == 1) return 100;
        return (item.position * 100 / item.duration).toFixed(1)
      },
      useTime(val) {
        val = parseInt(val) || 0
        let timestr = ""
        let hour = parseInt(val / 3600);
        let min = parseInt(val / 60 % 60);
        let sec = parseInt(val % 60);
        if (hour < 10) hour = "0" + hour;
        if (min < 10) min = "0" + min;
        if (sec < 10) sec = "0" + sec;
        return hour + ":" + min + ":" + sec
      }
    },
    methods: {
      getCourse() {
        let param = {
          courseId: this.courseId
        }
        httpServer("course.getCourse", param).then(res => {
          if (res.code == 200) {
            let {
              info,
              extra,
              list,
              tpl
            } = res.data;
            this.info = Object.assign(info, extra || {});
            this.tpl = tpl || {};
            this.list = list.map((item) => {
              item.percent = getPercent(item) || 0;
              return item;
            })
            let target = null
            if (this.mediaId) {
              target = this.list.find(i => i.id == this.mediaId || i.mediaId == this.mediaId)
            }
            if (!target) {
              target = this.list.find(i => i.isFinish != 1)
            }
            if (!target && this.list.length) {
              target = this.list[0]
            }
            if (target) {
              this.loadMedia(target)
            }
          }
        })
      },
      setVolume(val) {
        this.volume = val
        this.isMuted = val === 0
        let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
        if (player && player.volume) {
          player.volume(val)
          player.muted(this.isMuted)
        }
      },
      changeMedia(val) {
        if (!val || !this.mediaUrl) return;
        let mediaUrl = this.mediaUrl;
        if (val == 'ld') {
          mediaUrl = mediaUrl.replace('/hls/', '/ld/');
        } else {
          mediaUrl = mediaUrl.replace('/ld/', '/hls/');
        }
        this.mediaType = val;
        this.stopTick()
        this.onPlay = false
        this.curTimes = 0
        this.hasSeeked = false
        this.options = {
          ...this.options,
          sources: [{ src: mediaUrl, type: "application/x-mpegURL" }],
          playtimes: this.media.position || 0,
          autoplay: true
        }
      },
      startTick() {
        let tick = this.tryTick;
        if (this.timer) clearInterval(this.timer);
        this.timer = setInterval(tick, 5 * 1000);
      },
      stopTick() {
        if (this.timer) clearInterval(this.timer);
      },
      tryTick() {
        let that = this;
        try {
          that.tick()
        } catch (err) {
          that.reportErr("play", '' + err.message)
        }
      },
      playerReadied(audio) {
        this.isReady = true
      },
      onPlayerError(event) {
        console.error("Video error:", event)
      },
      onPlayerTimeupdate(player) {
        let curTimes = player.currentTime();
        this.curTimes = curTimes || 0
        if (this.media.isFinish) {
          return;
        }
      },
      safePlay(player) {
        if (!player) return
        try {
          const p = player.play()
          if (p && p.catch) p.catch(() => {})
        } catch (e) {}
      },
      setposition(position) {
        if (position > this.media.duration) position = this.media.duration;
        let player = this.$refs.videoPlayer.player;
        player.currentTime(position);
        this.safePlay(player)
        if (this.media.isFinish) return;
        if (this.media.position >= this.media.duration - 10 && !this.media.isFinish) {
          this.tick(true)
        }
      },
      onPlayerPause(event) {
        this.reportErr("play", 'pause');
        this.stopTick()
        this.onPlay = false
      },
      onPlayerEnded(event) {
        this.reportErr("play", 'end');
        this.tick(true)
        this.playNext()
      },
      playNext() {
        let currentIndex = this.list.findIndex(i => i.id === this.media.id)
        let nextItem = null
        for (let i = currentIndex + 1; i < this.list.length; i++) {
          if (this.list[i].isFinish != 1) {
            nextItem = this.list[i]
            break
          }
        }
        if (!nextItem) {
          for (let i = 0; i < currentIndex; i++) {
            if (this.list[i].isFinish != 1) {
              nextItem = this.list[i]
              break
            }
          }
        }
        if (nextItem) {
          this.$message.success(`即将播放: ${nextItem.name}`)
          setTimeout(() => {
            this.loadMedia(nextItem)
          }, 1000)
        }
      },
      onClose() {
        this.reportErr("play", 'close')
        this.doPause()
      },
      doPause() {
        this.stopTick()
        this.onPlay = false
        let myPlayer = this.$refs.videoPlayer.player;
        myPlayer && myPlayer.pause()
      },
      doPlay() {
        this.onPlay = true
        this.startTick();
        if (!this.$refs.videoPlayer || !this.$refs.videoPlayer.player) return;
        this.safePlay(this.$refs.videoPlayer.player)
        this.tickNum = 0
      },
      onPlayerStart() {
        this.reportErr("play", 'start');
        this.startTick()
        this.onPlay = true
        if (!this.hasSeeked && this.media.position > 5 && this.media.position < this.media.duration - 10) {
          this.hasSeeked = true
          let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
          if (player && player.currentTime) {
            player.currentTime(this.media.position)
          }
        }
      },
      toggleMute() {
        this.isMuted = !this.isMuted
        let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
        if (player && player.volume) {
          player.muted(this.isMuted)
        }
      },
      reportErr(action, msg) {
        httpServer("course.report", { action, msg }, true)
      },
      tick(force = false) {
        let media = this.media;
        this.tickNum++
        if (this.media.isFinish) {
          return;
        }
        let myPlayer = this.$refs.videoPlayer.player;
        if (!myPlayer) return;
        let curTimes = parseInt(myPlayer.currentTime());
        if (curTimes < 4) {
          return;
        }
        if (this.media.position > curTimes + 5 && !force) return;
        let isFinish = force ? 1 : 0
        if (curTimes >= media.duration) isFinish = 1;
        if (!isFinish) {
          if (!this.onPlay) return;
        }
        if (window.navigator.webdriver) {
          this.reportErr("play", 'webdriver');
          this.doPause()
          return
        }
        let param = {
          id: media.id,
          position: curTimes,
          isFinish
        };
        httpServer("course.tick", param, true).then(res => {
          if (res.code == 200) {
            let {
              skip,
              position,
              pause,
              closed
            } = res.data
            if (pause || closed) {
              this.doPause();
              if (closed) {
                this.$message.errorMsg("禁止多处同时学习", 5);
              } else if (pause) {
                this.$message.errorMsg("禁止多处同时学习", 5);
              }
              return
            }
            if (!skip) {
              let curTimes = parseInt(myPlayer.currentTime())
              if (position < curTimes + 5) {
                this.setposition(position)
              }
            }
            Object.assign(this.media, res.data)
          }
        })
      },
      loadMedia(item) {
        this.media = item;
        httpServer('course.GetMedia', {
          id: item.id
        }).then(res => {
          if (res.code != 200) return;
          let {
            mediaUrl,
            id,
            position,
            marks,
            duration
          } = res.data || {};
          this.mediaUrl = mediaUrl;
          this.media.position = position;
          this.media.duration = duration;
          this.media.id = id;
          this.curTimes = 0
          this.onPlay = false
          this.hasSeeked = false
          this.stopTick()
          this.options = {
            ...this.options,
            sources: [{
              src: mediaUrl,
              type: "application/x-mpegURL"
            }],
            playtimes: position || 1,
            autoplay: true
          }
        });
      }
    },
    created() {
      let { uid, token, courseId, mediaId } = this.$route.query
      this.courseId = +courseId || +this.$route.params.courseId
      this.mediaId = mediaId || this.$route.query.mediaId || 0
      if (uid) localStorage.uid = uid
      if (token) localStorage.token = token
      if (this.courseId) {
        this.getCourse()
      }
    },
    beforeDestroy() {
      this.stopTick()
      this.reportErr("play", 'destroy')
    }
  }
</script>
<style>
  @import url("./play.css");

  .pc-player-container {
    min-height: 100vh;
    background: #f0f2f5;
    padding: 20px;
    box-sizing: border-box;
  }

  .pc-player-wrapper {
    max-width: 1440px;
    margin: 0 auto;
    display: flex;
    gap: 20px;
    height: calc(100vh - 40px);
  }

  .pc-player-left {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }

  .panel-toggle {
    flex-shrink: 0;
    width: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: #999;
    font-size: 14px;
    user-select: none;
    transition: color 0.2s;
  }

  .panel-toggle:hover {
    color: #409EFF;
  }

  .video-section {
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
    overflow: hidden;
  }

  .video-title {
    padding: 16px 20px;
    font-size: 16px;
    font-weight: 600;
    color: #1a1a2e;
    border-bottom: 1px solid #f0f0f0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .video-player-wrap {
    width: 100%;
    background: #000;
    touch-action: none;
  }

  .video-player-wrap .video-js {
    width: 100%;
  }

  .video-info-bar {
    padding: 12px 20px;
    display: flex;
    align-items: center;
    gap: 16px;
    border-top: 1px solid #f0f0f0;
  }

  .video-time {
    font-size: 14px;
    color: #666;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }

  .time-current {
    color: #409EFF;
    font-weight: 600;
  }

  .time-sep {
    margin: 0 4px;
    color: #ccc;
  }

  .video-progress {
    flex: 1;
    min-width: 100px;
  }

  .video-status {
    flex-shrink: 0;
  }

  .pc-player-right {
    width: 380px;
    flex-shrink: 0;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .playlist-header {
    padding: 16px 20px 12px;
    border-bottom: 1px solid #f0f0f0;
    flex-shrink: 0;
  }

  .playlist-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 6px;
  }

  .playlist-title {
    font-size: 16px;
    font-weight: 600;
    color: #1a1a2e;
    margin-bottom: 4px;
  }

  .playlist-title i {
    margin-right: 6px;
    color: #409EFF;
  }

  .playlist-count {
    font-size: 12px;
    color: #999;
  }

  .finished-count {
    color: #67C23A;
  }

  .playlist-body {
    flex: 1;
    overflow-y: auto;
    padding: 8px 0;
  }

  .playlist-body::-webkit-scrollbar {
    width: 6px;
  }

  .playlist-body::-webkit-scrollbar-thumb {
    background: #ddd;
    border-radius: 3px;
  }

  .playlist-body::-webkit-scrollbar-track {
    background: transparent;
  }

  .playlist-item {
    display: flex;
    align-items: center;
    padding: 12px 20px;
    cursor: pointer;
    transition: all 0.2s ease;
    border-left: 3px solid transparent;
    gap: 12px;
  }

  .playlist-item:hover {
    background: #f5f7fa;
  }

  .playlist-item.is-active {
    background: #ecf5ff;
    border-left-color: #409EFF;
  }

  .playlist-item.is-finished {
    opacity: 0.65;
  }

  .playlist-item.is-current-playing {
    background: #ecf5ff;
    border-left-color: #409EFF;
  }

  .item-index {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: #f0f2f5;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 600;
    color: #666;
    flex-shrink: 0;
    transition: all 0.2s;
  }

  .item-index.active-index {
    background: #409EFF;
    color: #fff;
  }

  .finish-badge {
    color: #67C23A;
    font-size: 14px;
  }

  .item-info {
    flex: 1;
    min-width: 0;
  }

  .item-name {
    font-size: 14px;
    color: #333;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 2px;
  }

  .is-finished .item-name {
    color: #999;
    text-decoration: line-through;
  }

  .is-active .item-name {
    color: #409EFF;
  }

  .item-meta {
    display: flex;
    gap: 12px;
    font-size: 12px;
    color: #999;
  }

  .item-progress {
    color: #409EFF;
  }

  .is-finished .item-progress {
    color: #67C23A;
  }

  .item-action {
    flex-shrink: 0;
  }

  .playlist-empty {
    text-align: center;
    padding: 60px 20px;
    color: #ccc;
  }

  .playlist-empty i {
    font-size: 48px;
    display: block;
    margin-bottom: 12px;
  }

  .playlist-empty p {
    font-size: 14px;
    margin: 0;
  }

  .dialog-footer {
  padding: 0 16px;
  border-top: 1px solid #f0f0f0;
  background: #fafafa;
}
.dialog-footer.pt30 {
  padding: 8px 16px;
}
.media-footer {
  display: flex;
  align-items: center;
  line-height: 1;
}
.media-time {
  font-size: 14px;
  color: #666;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  gap: 4px;
}
.media-center {
  display: flex;
  align-items: center;
  justify-content: center;
}
.bicon {
  font-size: 22px !important;
  padding: 4px !important;
}
.volume-control {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.vol-icon {
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  color: #606266;
  user-select: none;
  line-height: 1;
}
.vol-icon:hover {
  color: #409EFF;
}
.volume-slider {
  width: 300px !important;
}
.volume-slider .el-slider__runway {
  height: 4px;
}
.volume-slider .el-slider__bar {
  height: 4px;
}
.volume-slider .el-slider__button {
  width: 12px;
  height: 12px;
}
.media-select {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.media-el-select {
  width: 80px;
}
.media-el-select .el-input__inner {
  height: 28px;
  line-height: 28px;
  font-size: 12px;
}

@media (max-width: 900px) {
    .pc-player-wrapper {
      flex-direction: column;
      height: auto;
    }

    .pc-player-right {
      width: 100%;
      max-height: 400px;
    }

    .pc-player-container {
      padding: 12px;
    }
  }
</style>
