<template>
  <div 
    class="thumbnails"
    @mousedown="() => setThumbnailsFocus(true)"
    v-click-outside="() => setThumbnailsFocus(false)"
    v-contextmenu="contextmenusThumbnails"
  >
    <div class="add-slide">
      <div class="btn" @click="createSlide()"><IconPlus class="icon" />添加幻灯片</div>
      <Popover trigger="click" placement="bottom-start" v-model:value="presetLayoutPopoverVisible" center>
        <template #content>
          <LayoutPool @select="slide => { createSlideByTemplate(slide); presetLayoutPopoverVisible = false }" />
        </template>
        <div class="select-btn" v-if="layoutSlides.length>0"><IconDown /></div>
      </Popover>
    </div>

    <Draggable 
      class="thumbnail-list"
      ref="thumbnailsRef"
      :modelValue="slides"
      :animation="200"
      :scroll="true"
      :scrollSensitivity="50"
      @end="handleDragEnd"
      itemKey="id"
    >
      <template #item="{ element, index }">
        <div
          class="thumbnail-item"
          :class="{
            'active': slideIndex === index,
            'selected': selectedSlidesIndex.includes(index),
          }"
          @mousedown="$event => handleClickSlideThumbnail($event, index)"
          @dblclick="enterScreening()"
          v-contextmenu="contextmenusThumbnailItem"
        >
          <div class="label" :class="{ 'offset-left': index >= 99 }">{{ fillDigit(index + 1, 2) }}</div>
          <ThumbnailSlide class="thumbnail" :slide="element" :size="120" :visible="index < slidesLoadLimit" />
        </div>
      </template>
    </Draggable>

    <div class="page-number">幻灯片 {{slideIndex + 1}} / {{slides.length}}</div>
  </div>
</template>

<script lang="ts" setup>
import { computed, nextTick, ref, reactive, watch, inject } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore, useKeyboardStore, useScreenStore } from '@/store'
import { fillDigit } from '@/utils/common'
import { isElementInViewport } from '@/utils/element'
import type { ContextmenuItem } from '@/components/Contextmenu/types'
import useSlideHandler from '@/hooks/useSlideHandler'
import useScreening from '@/hooks/useScreening'
import useLoadSlides from '@/hooks/useLoadSlides'
import { injectKeyDataSource, injectKeyTemplate } from '@/types/injectKey'
import ConfigService from '@/services/ConfigService'
import { getHtmlPlainText } from '@/utils/common'
import useEditor from '@/utils/Editor/index'

import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
import LayoutPool from './LayoutPool.vue'
import Popover from '@/components/Popover.vue'
import Draggable from 'vuedraggable'
import { ElLoading, ElMessageBox } from 'element-plus'
import FileService from '@/services/FileService'
import { VIEWPORT_SIZE, VIEWPORT_VER_SIZE } from '@/configs/canvas'

const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const layoutsStore = useSlidesStore()
const keyboardStore = useKeyboardStore()
const { selectedSlidesIndex: _selectedSlidesIndex, thumbnailsFocus } = storeToRefs(mainStore)
const { slides, currentSlide, slideIndex, layoutSlides } = storeToRefs(slidesStore)
const { ctrlKeyState, shiftKeyState } = storeToRefs(keyboardStore)

const { slidesLoadLimit } = useLoadSlides()
const TemplateTypeStore = useScreenStore()
const CoverImgStore = useScreenStore()
const dataLoadingStore = useScreenStore()
const { ConfigId, TemplateDataSource, TemplateType, dataLoading, TempId, SourceLoading } = storeToRefs(TemplateTypeStore)
const selectedSlidesIndex = computed(() => [..._selectedSlidesIndex.value, slideIndex.value])
const { setNewDatasList } = useEditor()

const presetLayoutPopoverVisible = ref(false)

const {
  copySlide,
  pasteSlide,
  createSlide,
  createSlideByTemplate,
  copyAndPasteSlide,
  deleteSlide,
  cutSlide,
  selectAllSlide,
  sortSlides,
} = useSlideHandler()

const datas = reactive({
  DataSource:{}
})
const queryObj = ref({} as any)
const searchData = ref({} as any)
datas.DataSource = inject(injectKeyDataSource)
searchData.value = inject(injectKeyTemplate)

watch(() => slideIndex.value, () => {
  // 清除多选状态的幻灯片
  if (selectedSlidesIndex.value.length) {
    mainStore.updateSelectedSlidesIndex([])
  }

  // 检查当前页缩略图是否在可视范围，不在的话需要滚动到对应的位置
  nextTick(() => {
    const activeThumbnailRef: HTMLElement = thumbnailsRef.value?.$el?.querySelector('.thumbnail-item.active')
    if (thumbnailsRef.value && activeThumbnailRef && !isElementInViewport(activeThumbnailRef, thumbnailsRef.value.$el)) {
      setTimeout(() => {
        activeThumbnailRef.scrollIntoView({ behavior: 'smooth' })
      }, 100)
    }
  })
})

// 监听请求保存成功 重新请求数据 1模版
watch(() => dataLoading.value, (n,o) =>{
  if(dataLoading.value==1){
    GetTripTemplate()
  }else if(dataLoading.value==2){
    GetTripConfig()
  }
})

// 监听数据源是否更新
watch(() => SourceLoading.value, (n,o) =>{
  if(n){
    
  }
})


queryObj.value = inject(injectKeyDataSource).queryObj

/**
 * 行程保存成功获取行程详情
 */
 const GetTripConfig = async () =>{
  console.log('行程详情--------')
  try {
      let queryMsg = {
          ConfigId: ConfigId.value
      }
      let datasRes = await ConfigService.triptemplateGetTripConfig(queryMsg);
      if (datasRes.data.resultCode == 1 && datasRes.data.data && datasRes.data.data.TempId) {
        let dataObj = datasRes.data.data
        let SlidesData = JSON.parse(dataObj.TempData)
        let newSlides = []
        SlidesData.forEach((x,i)=>{
          newSlides.push(x)
          if(SlidesData.length-1==i){
            slidesStore.setSlides(newSlides)
          }
        })
      }
  } catch (error) {
      
  }
}

// 获取行程团数据初始化数据
const GetTripFiledData = async () =>{
  const loadingObj = ElLoading.service({
    text:'正在渲染团队数据',
    lock:true
  })
  let isHideOverflowText = false
  try {
    await ElMessageBox.confirm('行程数据可能会超出模板预设宽度或高度，是否自动裁剪','提示',{
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
      closeOnClickModal:false
    })
    isHideOverflowText = true
  } catch (error) {}
  let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE, viewportRatio = slidesStore.viewportRatio
  if(viewportRatio<1){
    maxWidth = VIEWPORT_VER_SIZE
    maxHeight = VIEWPORT_SIZE
  }
  const slidesData = slides.value
  try {
      let queryMsg = {
        ConfigId: ConfigId.value
      }
      let dataRes = await ConfigService.triptemplateGetTripFiledData(queryMsg);
      if (dataRes.data.resultCode == 1) {
        if(!dataRes.data.data) return
        const travel = dataRes.data.data
        const cursors = [] as Array<any>
        for (let index = 0; index < slidesData.length; index++) {
          const x = slidesData[index] as any;
          for (let j = 0; j < x.elements.length; j++) {
            const y = x.elements[j];
            if(y.TemplateDataSource && y.TemplateDataSource.Content){
                let dataPath = y.TemplateDataSource.Content.split('.')
                let value=JSON.parse(JSON.stringify(travel));
                for (let i = 0; i < dataPath.length; i++) {
                  const oo = dataPath[i];
                  if(value && value[oo]){
                    if(i==0 && Array.isArray(value[oo])){
                      let temp = cursors.find(item=>item.key==oo)
                      if(temp){
                        temp.index++
                      }else{
                        temp={key:oo,index:0}
                        cursors.push(temp)
                      }
                      if(y.TemplateDataSource.index!=null&&y.TemplateDataSource.index>=0){
                        if(value[oo].length>temp.index) value=value[oo][y.TemplateDataSource.index]
                      }else{
                        if(value[oo].length>temp.index) value=value[oo][temp.index]
                        else value=value[oo]
                      }
                    }else{
                      value=value[oo]
                    }
                  }else{
                    value = null
                  }
                }
                if(value && typeof(value) == 'string'){
                  //替换
                  if(isHideOverflowText){
                    const maxLength = getHtmlPlainText(y.content).length
                    const newValue = maxLength<value.length?value.substring(0,maxLength):value
                    console.log(newValue,value,maxLength)
                    y.content= y.content.replace(getHtmlPlainText(y.content),newValue)
                  }else{
                    y.content= y.content.replace(getHtmlPlainText(y.content),value)
                  }
                  
                }else if(value && Array.isArray(value)){
                  //替换
                  if(y.type=='image'&&value[0]!=''){
                    try {
                      let tempSize = await FileService.getImageSizeWithoutDownloading(value[0])
                      if(tempSize.width>maxWidth){
                        let ratio = maxWidth/tempSize.width
                        tempSize.width = maxWidth
                        tempSize.height = tempSize.height*ratio
                      }
                      if(tempSize.height>maxHeight){
                        let ratio = maxHeight/tempSize.height
                        tempSize.height = maxHeight
                        tempSize.width = tempSize.width*ratio
                      }
                      if(y.left<0)y.left=0
                      if(y.top<0)y.top=0
                      y.width = tempSize.width
                      y.height = tempSize.height
                    } catch (error) { }
                    y.src = value[0]
                  } 
                }
              }
          }
        }
        slidesStore.setSlides(slidesData)
      }
  } catch (error) {
      console.log("triptemplateGetTripFiledData", error);
  }
  loadingObj.close()
}

// 获取行程模版数据
const GetTripTemplate = async () =>{
  console.log('模版数据------')
  if(!searchData.value.TempId&&!TempId.value) {
    let list = [
      {
        id: 'test-slide-1',
        pageType: 1,
        elements: [],
        background: {
          type: 'solid',
          color: '#ffffff',
        },
      }
    ]
    return slidesStore.setSlides(list)
  }
  try {
      let queryMsg = {
        TempId: 0
      }
      if(searchData.value.TempId) queryMsg.TempId = searchData.value.TempId
      else if(TempId.value) queryMsg.TempId = TempId.value
      let dataRes = await ConfigService.GetTripTemplateSlide(queryMsg);
      if (dataRes.data.resultCode == 1) {
        let viewportRatio = 1.414
        if(dataRes.data.data.TempType==1) viewportRatio = 0.7069
        slidesStore.setViewportRatio(viewportRatio)

        let SlidesData = JSON.parse(dataRes.data.data.TempData)
        let newSlides = []
        if(typeof SlidesData=='object'&&!SlidesData.length){
          let obj = {
            pageType: 1,
            ...SlidesData
          }
          newSlides.push(obj)          
        }else if(SlidesData.length>0){
          newSlides = SlidesData
        }
        newSlides.forEach(x=>{
          x.elements.forEach(y=>{
            if(x.pageType!=1&&y.TemplateDataSource&&y.TemplateDataSource.index==null){
              Reflect.set(y.TemplateDataSource, 'index', null)
            }
          })
        })
        slidesStore.setSlides(newSlides)
        layoutsStore.setLayouts(JSON.parse(JSON.stringify(newSlides)))
        CoverImgStore.setCoverImg(dataRes.data.data.CoverImg)

        queryObj.value.TempId = dataRes.data.data.TempId
        queryObj.value.LineId = dataRes.data.data.LineId
        queryObj.value.LineName = dataRes.data.data.LineName
        queryObj.value.LtId = dataRes.data.data.LtId
        queryObj.value.Title = dataRes.data.data.Title
        queryObj.value.TempData = dataRes.data.data.TempData
        queryObj.value.CoverImg = dataRes.data.data.CoverImg
        queryObj.value.CountryName = dataRes.data.data.CountryName
        queryObj.value.SeasonName = dataRes.data.data.SeasonName
        queryObj.value.ColorName = dataRes.data.data.ColorName
        queryObj.value.ColorStr = dataRes.data.data.ColorStr
        queryObj.value.TempType = dataRes.data.data.TempType
        slidesStore.updateSlideIndex(0)
        datas.DataSource.pageType = newSlides[0].pageType
        if(TempId.value&&!searchData.value.TempId){
          await GetTripConfig()
          return
        }
        if(ConfigId.value==0) return 
        await GetTripFiledData()
      }
  } catch (error) {
      console.log("GetTripTemplateSlide", error);
  }
}

// 页面被切换时
const thumbnailsRef = ref<InstanceType<typeof Draggable>>()

// 切换页面
const changeSlideIndex = (index: number) => {
  if(SourceLoading.value) setNewDatasList(datas.DataSource)
  mainStore.setActiveElementIdList([])
  // 绑定数据源
  const newElements = slides.value.find((slide,indexs) => {
    return index==indexs
  })
  datas.DataSource.pageType = newElements.pageType
  let TemplateList = TemplateDataSource.value.filter(x=>{
    return x.FiledType==newElements.pageType
  })
  datas.DataSource.DataSourceList = []
  newElements&&newElements.elements&&newElements.elements.forEach(slide=>{
    if(slide.type=="text"||slide.type=="image") {
      let contentStr
      if(slide.type=="text") contentStr = getHtmlPlainText(slide.content)
      if(slide.type=="image") contentStr = slide.src
        let Obj = {}
        Obj = {
          ...slide,
          contentStr: contentStr,
          TemplateList: TemplateList,
          TemplateDataSource: {
            Content: slide.TemplateDataSource&&slide.TemplateDataSource.Content?slide.TemplateDataSource.Content:'',
            Name: slide.TemplateDataSource&&slide.TemplateDataSource.Name?slide.TemplateDataSource.Name:'',
            Id: slide.TemplateDataSource&&slide.TemplateDataSource.Id?slide.TemplateDataSource.Id:null as Number,
          }
        }
        if(newElements.pageType!=1){
          let index = slide.TemplateDataSource&&slide.TemplateDataSource.index>=0?slide.TemplateDataSource.index:null as Number
          Reflect.set(Obj.TemplateDataSource, 'index', index)
        }
        if(Obj){
          datas.DataSource.DataSourceList.push(Obj)
        }
    }
    
  })
  
  if (slideIndex.value === index) return
  slidesStore.updateSlideIndex(index)
}

// 点击缩略图
const handleClickSlideThumbnail = (e: MouseEvent, index: number) => {
  const isMultiSelected = selectedSlidesIndex.value.length > 1

  if (isMultiSelected && selectedSlidesIndex.value.includes(index) && e.button !== 0) return

  // 按住Ctrl键，点选幻灯片，再次点击已选中的页面则取消选中
  // 如果被取消选中的页面刚好是当前激活页面，则需要从其他被选中的页面中选择第一个作为当前激活页面
  if (ctrlKeyState.value) {
    if (slideIndex.value === index) {

      if (!isMultiSelected) return

      const newSelectedSlidesIndex = selectedSlidesIndex.value.filter(item => item !== index)
      mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
      changeSlideIndex(selectedSlidesIndex.value[0])
    }
    else {
      if (selectedSlidesIndex.value.includes(index)) {
        const newSelectedSlidesIndex = selectedSlidesIndex.value.filter(item => item !== index)
        mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
      }
      else {
        const newSelectedSlidesIndex = [...selectedSlidesIndex.value, index]
        mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
      }
    }
  }
  // 按住Shift键，选择范围内的全部幻灯片
  else if (shiftKeyState.value) {
    if (slideIndex.value === index && !isMultiSelected) return

    let minIndex = Math.min(...selectedSlidesIndex.value)
    let maxIndex = index

    if (index < minIndex) {
      maxIndex = Math.max(...selectedSlidesIndex.value)
      minIndex = index
    }

    const newSelectedSlidesIndex = []
    for (let i = minIndex; i <= maxIndex; i++) newSelectedSlidesIndex.push(i)
    mainStore.updateSelectedSlidesIndex(newSelectedSlidesIndex)
  }
  // 正常切换页面
  else {
    mainStore.updateSelectedSlidesIndex([])
    changeSlideIndex(index)
  }
}

// 设置缩略图工具栏聚焦状态（只有聚焦状态下，该部分的快捷键才能生效）
const setThumbnailsFocus = (focus: boolean) => {
  if (thumbnailsFocus.value === focus) return
  mainStore.setThumbnailsFocus(focus)

  if (!focus) mainStore.updateSelectedSlidesIndex([])
}

// 拖拽调整顺序后进行数据的同步
const handleDragEnd = (eventData: { newIndex: number; oldIndex: number }) => {
  const { newIndex, oldIndex } = eventData
  if (newIndex === undefined || oldIndex === undefined || newIndex === oldIndex) return
  sortSlides(newIndex, oldIndex)
}

const { enterScreening, enterScreeningFromStart } = useScreening()

const contextmenusThumbnails = (): ContextmenuItem[] => {
  return [
    {
      text: '粘贴',
      subText: 'Ctrl + V',
      handler: pasteSlide,
    },
    {
      text: '全选',
      subText: 'Ctrl + A',
      handler: selectAllSlide,
    },
    {
      text: '新建页面',
      subText: 'Enter',
      handler: createSlide,
    },
    {
      text: '幻灯片放映',
      subText: 'F5',
      handler: enterScreeningFromStart,
    },
  ]
}

const contextmenusThumbnailItem = (): ContextmenuItem[] => {
  return [
    {
      text: '剪切',
      subText: 'Ctrl + X',
      handler: cutSlide,
    },
    {
      text: '复制',
      subText: 'Ctrl + C',
      handler: copySlide,
    },
    {
      text: '粘贴',
      subText: 'Ctrl + V',
      handler: pasteSlide,
    },
    {
      text: '全选',
      subText: 'Ctrl + A',
      handler: selectAllSlide,
    },
    { divider: true },
    {
      text: '新建页面',
      subText: 'Enter',
      handler: createSlide,
    },
    {
      text: '复制页面',
      subText: 'Ctrl + D',
      handler: copyAndPasteSlide,
    },
    {
      text: '删除页面',
      subText: 'Delete',
      handler: () => deleteSlide(),
    },
    { divider: true },
    {
      text: '从当前放映',
      subText: 'Shift + F5',
      handler: enterScreening,
    },
  ]
}

GetTripTemplate()
</script>

<style lang="scss" scoped>
.thumbnails {
  border-right: solid 1px $borderColor;
  background-color: #fff;
  display: flex;
  flex-direction: column;
  user-select: none;
}
.add-slide {
  height: 40px;
  font-size: 12px;
  display: flex;
  flex-shrink: 0;
  border-bottom: 1px solid $borderColor;
  cursor: pointer;

  .btn {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;

    &:hover {
      background-color: $lightGray;
    }
  }
  .select-btn {
    width: 30px;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    border-left: 1px solid $borderColor;

    &:hover {
      background-color: $lightGray;
    }
  }

  .icon {
    margin-right: 3px;
    font-size: 14px;
  }
}
.thumbnail-list {
  padding: 5px 0;
  flex: 1;
  overflow: auto;
}
.thumbnail-item {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 5px 0;

  .thumbnail {
    outline: 2px solid rgba($color: $themeColor, $alpha: .15);
  }

  &.active {
    .label {
      color: $themeColor;
    }
    .thumbnail {
      outline-color: $themeColor;
    }
  }
  &.selected {
    .thumbnail {
      outline-color: $themeColor;
    }
  }
}
.label {
  font-size: 12px;
  color: #999;
  width: 20px;
  cursor: grab;

  &.offset-left {
    position: relative;
    left: -4px;
  }

  &:active {
    cursor: grabbing;
  }
}
.page-number {
  height: 40px;
  font-size: 12px;
  border-top: 1px solid $borderColor;
  line-height: 40px;
  text-align: center;
  color: #666;
}
</style>