<template>
  <div ref="viewport" class="viewport-wrapper" v-loading="loading" :element-loading-text="NSTip"
    element-loading-background="rgba(0, 0, 0, 0.8)" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
    @mouseleave="sliderMouseleave">
    <div v-if="series && taskInfo" class="left-top-text">
      <div v-if="taskInfo.IsExistsClinicalData" class="cd-info" :title="$t('trials:reading:button:clinicalData')">
        <svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
          @click.stop="viewCD(series.TaskInfo.VisitTaskId)" />
      </div>
      <h2 class="subject-info">
        {{ taskInfo.IsReadingShowSubjectInfo ? series.TaskInfo.SubjectCode + ' ' + series.TaskInfo.TaskBlindName : series.TaskInfo.TaskBlindName }}
      </h2>
      <div>Series: #{{ series.SeriesNumber }}</div>
      <div v-if="series.Stack">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
      <div>{{ series.Modality }}</div>
    </div>
    <div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1" class="top-center-tool">
      <div class="toggle-visit-container">
        <div class="arrw_icon"
          :style="{ cursor: series.TaskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: series.TaskInfo.VisitTaskNum !== 0 ? '#fff' : '#6b6b6b' }"
          @click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, -1)"
          @dblclick.stop="preventDefault($event)">
          <i class="el-icon-caret-left" />
        </div>
        <div class="arrow_text">
          {{ series.TaskInfo.TaskBlindName }}
        </div>
        <div class="arrw_icon"
          :style="{ cursor: series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff' : '#6b6b6b' }"
          @click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, 1)"
          @dblclick.stop="preventDefault($event)">
          <i class="el-icon-caret-right" />
        </div>
      </div>
    </div>
    <div v-if="series" class="right-top-text">
      <div>{{ series.Description }}</div>
    </div>
    <div v-if="series" class="left-bottom-text">
      <div v-show="mousePosition.index.length > 0">
        Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}
      </div>
      <div
        v-if="(series.Modality === 'CT' || series.Modality === 'DR' || series.Modality === 'CR') && mousePosition.value">
        HU: {{ mousePosition.value }}
      </div>
      <div v-else-if="(series.Modality === 'PT' && mousePosition.value)">
        {{ mousePosition.modalityUnit }}: {{ digitPlaces === -1 ? mousePosition.value.toFixed(3)
          : mousePosition.value.toFixed(digitPlaces) }}
      </div>
      <div v-else-if="mousePosition.value">
        Density: {{ mousePosition.value }}
      </div>
      <div v-show="imageInfo.size">
        W*H: {{ imageInfo.size }}
      </div>
      <div v-show="imageInfo.zoom">
        Zoom: {{ imageInfo.zoom }}
      </div>
    </div>
    <div v-if="series" class="right-bottom-text">
      <div v-show="imageInfo.location">Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm` }}</div>
      <div v-show="series.SliceThickness">Slice Thickness: {{ `${Number(series.SliceThickness).toFixed(digitPlaces)} mm`
      }}</div>
      <div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
    </div>
    <div class="orientation-top">
      {{ markers.top }}
    </div>
    <div class="orientation-right">
      {{ markers.right }}
    </div>

    <div class="orientation-bottom">
      {{ markers.bottom }}
    </div>
    <div class="orientation-left">
      {{ markers.left }}
    </div>
    <div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)">
      <div :style="{ top: sliderInfo.height + '%' }" class="slider" @click.stop.prevent="() => { return }"
        @mousedown.stop="sliderMousedown($event)" />
    </div>
  </div>
</template>
<script>
// import * as cornerstonejs from '@cornerstonejs/core'
import {
  // RenderingEngine,
  // Enums,
  // imageLoader,
  metaData,
  getRenderingEngine,
  // eventTarget,
  utilities as csUtils
} from '@cornerstonejs/core'
import * as cornerstoneTools from '@cornerstonejs/tools'
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
import {
  utilities as csToolsUtils
} from '@cornerstonejs/tools'
import { getPTImageIdInstanceMetadata } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/getPTImageIdInstanceMetadata'
import ptScalingMetaDataProvider from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/ptScalingMetaDataProvider'
import { calculateSUVScalingFactors } from '@cornerstonejs/calculate-suv'
import { vec3 } from 'gl-matrix'
const InitPrefetchMaxBytes = 2 * 1024 * 1024 * 1024
export default {
  name: 'ImageViewport',
  props: {
    renderingEngineId: {
      type: String,
      required: true
    },
    viewportId: {
      type: String,
      required: true
    },
    viewportIndex: {
      type: Number,
      required: true
    },
    isScrollSync: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      element: '',
      series: null,
      taskInfo: null,
      sliderInfo: {
        oldB: null,
        oldM: null,
        isMove: false,
        height: 0
      },
      mousePosition: {
        index: [],
        value: null,
        modalityUnit: '',
        world: []
      },
      imageInfo: {
        zoom: null,
        size: null,
        location: null,
        sliceThickness: null,
        wwwc: null,
        imageOrientationPatient: [],
        imagePositionPatient: []
      },
      digitPlaces: 2,
      orientationMarkers: [],
      originalMarkers: [],
      markers: { top: '', right: '', bottom: '', left: '' },
      playClipState: false,
      clipFramesPerSecond: null,
      toggleClipPlayTimer: null,
      wwwcIdx: 2,
      loading: false,
      forceFitToWindow: false,
      resizeObserver: null,
      instanceFileSizeMap: {}
    }
  },
  mounted() {
    this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
    const digitPlaces = Number(localStorage.getItem('digitPlaces'))
    this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
    this.$nextTick(() => {
      this.initViewport()
    })
  },
  beforeDestroy() {
    if (this.resizeObserver && this.element) {
      this.resizeObserver.unobserve(this.element)
      this.resizeObserver.disconnect()
    }
    if (this.toggleClipPlayTimer) {
      clearInterval(this.toggleClipPlayTimer)
      this.toggleClipPlayTimer = null
    }
  },
  methods: {
    initViewport() {
      this.element = this.$refs['viewport']
      this.resizeObserver = new ResizeObserver(() => {
        const renderingEngine = getRenderingEngine(this.renderingEngineId)
        if (renderingEngine) {
          renderingEngine.resize(true, true)
          if (this.forceFitToWindow) {
            this.resize(true)
          }
        }
      })
      this.element.oncontextmenu = (e) => e.preventDefault()
      this.resizeObserver.observe(this.element)
      this.element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
      this.element.addEventListener('wheel', this.handleWheelSync)
      // this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
      // this.element.addEventListener('CORNERSTONE_CAMERA_MODIFIED', this.cameraModified)

      this.element.addEventListener('CORNERSTONE_IMAGE_RENDERED', this.imageRendered)
      this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
      this.element.addEventListener('mouseleave', () => {
        this.mousePosition.index = []
        this.mousePosition.value = null
      })
      // console.log(cornerstoneTools)
      // element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
    },
    stackNewImage(e) {
      const { detail } = e
      if (this.series?.Modality === 'PT' && detail?.image) {
        this.cachePTMetadata([detail.image])
      }
      this.series.SliceIndex = detail.imageIdIndex
      this.sliderInfo.height = detail.imageIdIndex * 100 / (this.series.Stack.length - 1)
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      const zoom = viewport.getZoom()
      this.imageInfo.zoom = zoom.toFixed(4)
      this.imageInfo.size = `${detail.image.columns}*${detail.image.rows}`
      const imagePlaneModule = metaData.get('imagePlaneModule', detail.imageId)
      this.imageInfo.imageOrientationPatient = imagePlaneModule.imageOrientationPatient
      this.imageInfo.imagePositionPatient = imagePlaneModule.imagePositionPatient
      this.imageInfo.location = imagePlaneModule.sliceLocation
      this.getOrientationMarker()
      const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
      if (toolGroup) {
        toolGroup.setToolEnabled('ScaleOverlay')
      }
    },
    imageRendered(e) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      const properties = viewport.getProperties()
      if (properties && properties.voiRange) {
        var { lower, upper } = properties.voiRange
        const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
          lower,
          upper
        )
        this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
      }
      const zoom = viewport.getZoom()
      this.imageInfo.zoom = zoom.toFixed(4)
    },
    cameraModified(e) {
      console.log(e)
    },
    voiModified(e) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      const properties = viewport.getProperties()
      if (properties && properties.voiRange) {
        var { lower, upper } = properties.voiRange
        const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
          lower,
          upper
        )
        this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
      }
      const zoom = viewport.getZoom()
      this.imageInfo.zoom = zoom.toFixed(4)
    },
    getOrientationMarker() {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      const { viewUp, viewPlaneNormal } = viewport.getCamera()

      const viewRight = vec3.create()
      vec3.cross(viewRight, viewUp, viewPlaneNormal)

      const columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]]
      const rowCosines = viewRight
      const rowString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(rowCosines)
      const columnString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(columnCosines)
      const oppositeRowString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(rowString)
      const oppositeColumnString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(columnString)
      this.markers.top = oppositeColumnString
      this.markers.right = rowString
      this.markers.bottom = columnString
      this.markers.left = oppositeRowString
      this.orientationMarkers = [oppositeColumnString, rowString, columnString, oppositeRowString]
      if (this.originalMarkers.length === 0) {
        this.originalMarkers = [...this.orientationMarkers]
      }
    },
    setMarkers() {
      const markers = [...this.orientationMarkers]
      for (const key in this.markers) {
        const v = markers.shift(0)
        this.markers[key] = v
      }
    },
    resetOrientationMarkers() {
      if (this.originalMarkers.length > 0) {
        this.orientationMarkers = [...this.originalMarkers]
        this.setMarkers()
      }
    },
    rotateOrientationMarkers(type) {
      if (this.orientationMarkers.length > 0) {
        if (type === 1) {
          this.resetOrientationMarkers()
          return
        }
        const markers = [...this.orientationMarkers]
        if (type === 2) {
          // 垂直翻转
          this.orientationMarkers[0] = markers[2]
          this.orientationMarkers[2] = markers[0]
        } else if (type === 3) {
          // 水平翻转
          this.orientationMarkers[1] = markers[3]
          this.orientationMarkers[3] = markers[1]
        } else if (type === 4) {
          // 左转90度
          this.orientationMarkers = markers.slice(1, 4).concat(markers[0])
        } else if (type === 5) {
          // 右转90度
          this.orientationMarkers = [markers[3]].concat(markers.slice(0, 3))
        }
        this.setMarkers()
      }
    },
    toggleClipPlay(isPlay, framesPerSecond) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      if (!renderingEngine) return
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport?.element) return

      const parsedFramesPerSecond = Number(framesPerSecond)
      const nextFramesPerSecond = Number.isFinite(parsedFramesPerSecond)
        ? parsedFramesPerSecond
        : (this.clipFramesPerSecond || 15)
      const isSameSpeedWhilePlaying = this.playClipState &&
        isPlay &&
        this.clipFramesPerSecond === nextFramesPerSecond

      if (isPlay) {
        if (isSameSpeedWhilePlaying) return

        cornerstoneTools.utilities.cine.stopClip(viewport.element)
        if (this.toggleClipPlayTimer) {
          clearInterval(this.toggleClipPlayTimer)
          this.toggleClipPlayTimer = null
        }

        const frameInterval = Math.max(16, Math.round(1000 / nextFramesPerSecond))
        this.toggleClipPlayTimer = setInterval(() => {
          const imageIds = viewport.getImageIds()
          if (!imageIds?.length) return

          let index = viewport.getCurrentImageIdIndex() + 1
          if (index > imageIds.length - 1) {
            index = 0
          }

          csUtils.jumpToSlice(viewport.element, {
            imageIndex: index,
            debounceLoading: false
          })
        }, frameInterval)
        this.clipFramesPerSecond = nextFramesPerSecond
        this.playClipState = true
      } else {
        cornerstoneTools.utilities.cine.stopClip(viewport.element)
        if (this.toggleClipPlayTimer) {
          clearInterval(this.toggleClipPlayTimer)
          this.toggleClipPlayTimer = null
        }
        this.clipFramesPerSecond = null
        this.playClipState = false
      }
    },
    syncViewportStaticState() {
      this.getOrientationMarker()
      this.$emit('renderAnnotations', this.series)
      const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
      if (toolGroup) {
        toolGroup.setToolEnabled('ScaleOverlay')
      }
    },
    handleWheelSync(e) {
      if (!this.isScrollSync) return

      const deltaY = typeof e.deltaY === 'number' ? e.deltaY : -e.wheelDeltaY
      if (!deltaY) return

      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine && renderingEngine.getViewport(this.viewportId)
      if (!viewport) return

      const currentImageIdIndex = viewport.getCurrentImageIdIndex()
      const numImages = viewport.getImageIds().length
      if (numImages <= 1) return
      const direction = deltaY > 0 ? 1 : -1
      let type = direction

      if (direction === 1 && currentImageIdIndex >= numImages - 1) {
        type = 0
        csUtils.jumpToSlice(viewport.element, { imageIndex: 0 })
      } else if (direction === -1 && currentImageIdIndex <= 0) {
        type = 99999
        csUtils.jumpToSlice(viewport.element, { imageIndex: numImages - 1 })
      }

      this.$emit('scrollSync', { viewportIndex: this.viewportIndex, type })
    },
    scrollPage(type) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      const currentImageIdIndex = viewport.getCurrentImageIdIndex()
      const numImages = viewport.getImageIds().length
      let newImageIdIndex = null
      if (type === 0) {
        newImageIdIndex = 0
      } else if (type === -1) {
        newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1
      } else if (type === 1) {
        newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : currentImageIdIndex + 1
      } else if (type === 99999) {
        newImageIdIndex = numImages - 1
      }
      // viewport.setImageIdIndex(newImageIdIndex)
      csUtils.jumpToSlice(viewport.element, { imageIndex: newImageIdIndex })
      if (this.toggleClipPlayTimer) {
        clearInterval(this.toggleClipPlayTimer)
        this.toggleClipPlayTimer = null
      }
      cornerstoneTools.utilities.cine.stopClip(viewport.element)
      this.clipFramesPerSecond = null
      this.playClipState = false
    },
    setZoom(ratio) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)

      const zoom = viewport.getZoom()
      if (ratio > 0) {
        viewport.setZoom(zoom * 1.05)
      } else {
        viewport.setZoom(zoom / 1.05)
      }
      viewport.render()
    },
    resize(forceFitToWindow) {
      this.forceFitToWindow = !!forceFitToWindow
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport) return

      if (forceFitToWindow) {
        viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
        viewport.render()
        return
      }

      viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
      const canvas = viewport.getCanvas() || this.element.querySelector('canvas')
      const imageData = viewport.getImageData()?.imageData
      const dimensions = imageData?.getDimensions()
      const imageWidth = dimensions?.[0]
      const imageHeight = dimensions?.[1]
      const canvasWidth = canvas?.clientWidth
      const canvasHeight = canvas?.clientHeight

      if (!imageWidth || !imageHeight || !canvasWidth || !canvasHeight) {
        viewport.render()
        return
      }

      const fitScale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight)
      if (fitScale > 0) {
        viewport.setZoom(1 / fitScale)
      }

      viewport.render()
    },
    formatBytes(bytes) {
      const size = Number(bytes)
      if (!Number.isFinite(size) || size <= 0) return '0 B'
      if (size >= 1024 * 1024 * 1024) return `${(size / 1024 / 1024 / 1024).toFixed(2)} GB`
      if (size >= 1024 * 1024) return `${(size / 1024 / 1024).toFixed(2)} MB`
      if (size >= 1024) return `${(size / 1024).toFixed(2)} KB`
      return `${size} B`
    },
    getImageInstanceId(imageId) {
      try {
        const rawImageId = imageId.includes(':') ? imageId.slice(imageId.indexOf(':') + 1) : imageId
        const queryIndex = rawImageId.indexOf('?')
        if (queryIndex === -1) return ''
        const searchParams = new URLSearchParams(rawImageId.slice(queryIndex + 1))
        return searchParams.get('instanceId') || ''
      } catch (error) {
        return ''
      }
    },
    buildInstanceFileSizeMap() {
      console.log('buildInstanceFileSizeMap')
      const fileSizeMap = {}
      const instanceList = this.series?.InstanceInfoList || []
      instanceList.forEach((item) => {
        const instanceId = item?.Id
        const fileSize = Number(item.FileSize)
        if (instanceId !== undefined && instanceId !== null && fileSize > 0) {
          fileSizeMap[String(instanceId)] = fileSize
        }
      })
      this.instanceFileSizeMap = fileSizeMap
    },
    getImageFileSizeByImageId(imageId) {
      const instanceId = this.getImageInstanceId(imageId)
      if (!instanceId) return 0
      return Number(this.instanceFileSizeMap[instanceId] || 0)
    },
    // async prefetchMetadataInformation(imageIdsToPrefetch, modality, initialLoadedBytes = 0, enableInitPrefetchLimit = false) {
    //   let totalLoadedBytes = initialLoadedBytes
    //   let taskPromises = []
    //   for (let i = 0; i < imageIdsToPrefetch.length; i++) {
    //     const imageId = imageIdsToPrefetch[i]
    //     const nextImageBytes = this.getImageFileSizeByImageId(imageId)
    //     // 初始化预取累计超过 2G 后停止，剩余切片在用户滚动时按需加载
    //     if (enableInitPrefetchLimit && (totalLoadedBytes >= InitPrefetchMaxBytes || (nextImageBytes > 0 && totalLoadedBytes + nextImageBytes > InitPrefetchMaxBytes))) {
    //       console.info(`[Viewport] 初始化预取累计下载达到 ${this.formatBytes(totalLoadedBytes)}，停止继续预取。`)
    //       break
    //     }
    //     taskPromises.push(cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]).promise)
    //     totalLoadedBytes += nextImageBytes
    //     if (taskPromises.length >= 6 || i === imageIdsToPrefetch.length - 1) {
    //       let res = await Promise.all(taskPromises)
    //       if (modality === 'PT') {
    //         this.cachePTMetadata(res)
    //       }
    //       taskPromises = []
    //     }
    //   }
    // },
    async prefetchMetadataInformation(imageIdsToPrefetch, modality, initialLoadedBytes = 0, enableInitPrefetchLimit = false) {
      if (!imageIdsToPrefetch?.length) return

      const CONCURRENT_LIMIT = 6 // 最大并发数
      let totalLoadedBytes = initialLoadedBytes
      let currentIndex = 0
      // 单个任务：不断取下一个切片加载，直到全部完成或中止
      const runWorker = async () => {
        while (currentIndex < imageIdsToPrefetch.length) {
          const index = currentIndex++
          const imageId = imageIdsToPrefetch[index]
          const nextImageBytes = this.getImageFileSizeByImageId(imageId)

          // 大小限制判断
          if (enableInitPrefetchLimit && (totalLoadedBytes >= InitPrefetchMaxBytes || (nextImageBytes > 0 && totalLoadedBytes + nextImageBytes > InitPrefetchMaxBytes))) {
            console.info(`[Viewport] 初始化预取累计下载达到 ${this.formatBytes(totalLoadedBytes)}，停止继续预取。`)
            return
          }
          totalLoadedBytes += nextImageBytes

          try {
            const image = await cornerstoneDICOMImageLoader.wadouri.loadImage(imageId).promise
            // PT模态缓存元数据（也可批量收集后统一缓存）
            if (modality === 'PT') {
              this.cachePTMetadata([image])
            }
          } catch (err) {
            console.warn(`[Viewport] 预加载失败: ${imageId}`, err)
          }
        }
      }

      // 启动 N 个并发，形成并发池
      const workers = Array.from({ length: CONCURRENT_LIMIT }, () => runWorker())
      await Promise.all(workers)
    },
    cachePTMetadata(images) {
      images.map(i => {
        const instanceMetadata = getPTImageIdInstanceMetadata(i.imageId)
        if (typeof instanceMetadata.CorrectedImage === 'string') {
          instanceMetadata.CorrectedImage = instanceMetadata.CorrectedImage.split('\\')
        }
        if (instanceMetadata) {
          const suvScalingFactors = calculateSUVScalingFactors([instanceMetadata])
          ptScalingMetaDataProvider.addInstance(i.imageId, suvScalingFactors[0])
        }
      })
    },
    async setSeriesInfo(obj, isLocate = false, option = {}) {
      try {
        if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description && !isLocate) {
          obj.SliceIndex = this.series.SliceIndex
        }
        // if (isLocate && obj.SliceIndex === this.series.SliceIndex) return
        const isSameSeries = this.series && obj.Id === this.series.Id && obj.Description === this.series.Description
        if (isSameSeries && this.series?.Stack && obj.Stack[obj.SliceIndex] === this.series.Stack[this.series.SliceIndex]) return
        this.toggleClipPlay(false)
        this.series = { ...obj }
        this.buildInstanceFileSizeMap()
        const renderingEngine = getRenderingEngine(this.renderingEngineId)
        const viewport = renderingEngine.getViewport(this.viewportId)
        let imageId = obj.ImageIds[obj.SliceIndex] ? obj.ImageIds[obj.SliceIndex] : obj.ImageIds[0]
        this.loading = true
        const image = await cornerstoneDICOMImageLoader.wadouri.loadImage(imageId).promise
        this.loading = false
        if (obj.Modality === 'PT') {
          this.cachePTMetadata([image])
        }
        const currentImageIndex = obj.ImageIds[obj.SliceIndex] ? obj.SliceIndex : 0
        const initialLoadedBytes = this.getImageFileSizeByImageId(imageId)
        const prefetchImageIds = obj.ImageIds.filter((item, index) => index !== currentImageIndex)
        if (option.skipInitPrefetch !== true) {
          this.prefetchMetadataInformation(prefetchImageIds, obj.Modality, initialLoadedBytes, option.limitInitPrefetch === true)
        }
        await viewport.setStack(this.series.Stack, obj.SliceIndex)
        this.syncViewportStaticState()

        viewport.render()
      } catch (e) {
        console.log(e)
      }
    },
    cornerstoneToolsMouseMove(e) {
      const { currentPoints } = e.detail
      const worldPoint = currentPoints.world
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      let referencedImageId = viewport.getCurrentImageId()
      const data = viewport.getImageData()
      if (!data) return
      const { dimensions, imageData, metadata, voxelManager } = data
      const index = imageData.worldToIndex(worldPoint)
      index[0] = Math.floor(index[0])
      index[1] = Math.floor(index[1])
      index[2] = Math.floor(index[2])
      this.mousePosition.index = index
      const modality = metadata.Modality
      let ijk = csUtils.transformWorldToIndex(imageData, worldPoint)
      ijk = vec3.round(ijk, ijk);
      if (csUtils.indexWithinDimensions(ijk, dimensions)) {
        this.isHandleOutsideImage = false
        let value = voxelManager.getAtIJKPoint(ijk)
        ijk[2] = viewport.getCurrentImageIdIndex()
        let modalityUnit
        if (modality === 'US') {
          const calibratedResults = csToolsUtils.getCalibratedProbeUnitsAndValue(data, [ijk])
          const hasEnhancedRegionValues = calibratedResults.values.every(
            (value) => value !== null
          )
          value = (hasEnhancedRegionValues ? calibratedResults.values : value)
          modalityUnit = hasEnhancedRegionValues
            ? calibratedResults.units
            : 'raw';
        } else {
          const scalingModule = referencedImageId && metaData.get('scalingModule', referencedImageId)
          const isSuvScaled = typeof scalingModule?.suvbw === 'number'
          const pixelUnitsOptions = {
            isPreScaled: csToolsUtils.viewport.isViewportPreScaled(viewport, referencedImageId),
            isSuvScaled: isSuvScaled,
          }
          modalityUnit = csToolsUtils.getPixelValueUnits(
            modality,
            referencedImageId,
            pixelUnitsOptions
          );
        }
        this.mousePosition.value = value
        this.mousePosition.modalityUnit = modalityUnit
      }
    },
    toggleTask(evt, visitTaskNum, i) {
      this.$emit('activeViewport', this.viewportIndex)
      const num = visitTaskNum + i
      if (num >= 0 && num <= this.taskInfo.VisitNum) {
        this.$emit('toggleTaskByViewport', { series: this.series, visitTaskNum: num })
      }

      evt.stopImmediatePropagation()
      evt.stopPropagation()
      evt.preventDefault()
    },
    viewCD(taskId) {
      this.$emit('previewCD', taskId)
    },
    setWwwcIdx(idx) {
      this.wwwcIdx = idx
    },
    clickSlider(e) {
      const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
      this.sliderInfo.height = height
      let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
      sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(
        this.viewportId
      )
      // viewport.setImageIdIndex(sliceIdx)
      csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
      viewport.render()
    },
    sliderMouseup(e) {
      this.sliderInfo.isMove = false
      this.$emit('contentMouseup', e)
    },
    sliderMousedown(e) {
      const boxHeight = this.$refs['sliderBox'].clientHeight
      this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
      this.sliderInfo.oldM = e.clientY
      this.sliderInfo.isMove = true
      e.stopImmediatePropagation()
      e.stopPropagation()
      e.preventDefault()
    },
    sliderMousemove(e) {
      if (!this.sliderInfo.isMove) return
      const delta = this.sliderInfo.oldB - (this.sliderInfo.oldM - e.clientY)
      const boxHeight = this.$refs['sliderBox'].clientHeight
      if (delta < 0) return
      if (delta > boxHeight) return
      const height = delta * 100 / boxHeight
      let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
      sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
      this.sliderInfo.height = height
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(
        this.viewportId
      )
      // viewport.setImageIdIndex(sliceIdx)
      csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
      viewport.render()
    },
    sliderMouseleave(e) {
      if (!this.sliderInfo.isMove) return
      this.sliderInfo.isMove = false
    },
    preventDefault(e) {
      e.stopImmediatePropagation()
      e.stopPropagation()
      e.preventDefault()
    }
  },
  computed: {
    NSTip() {
      return `NS: ${this.$store.state.trials.downloadTip}`
    }
  },
}
</script>
<style lang="scss" scoped>
.viewport-wrapper {
  width: 100%;
  height: 100%;
  position: relative;
  cursor: default !important;

  .left-top-text {
    position: absolute;
    left: 5px;
    top: 5px;
    color: #ddd;
    z-index: 1;
    font-size: 12px;

    .cd-info {
      color: #ddd;
      font-size: 18px;
    }

    .subject-info {
      color: #f44336;
      padding: 5px 0px;
      margin: 0;
    }
  }

  .top-center-tool {
    position: absolute;
    left: 50%;
    top: 5px;
    transform: translateX(-50%);
    z-index: 1;

    .toggle-visit-container {
      display: flex;
    }

    .arrw_icon {
      width: 20px;
      height: 20px;
      background-color: #3f3f3f;
      text-align: center;
      line-height: 20px;
      border-radius: 10%;
    }

    .arrow_text {
      height: 20px;
      line-height: 20px;
      background-color: #00000057;
      color: #fff;
      padding: 0 10px;
      font-size: 14px;
    }
  }

  .right-top-text {
    position: absolute;
    right: 5px;
    top: 5px;
    color: #ddd;
    z-index: 1;
    font-size: 12px;
  }

  .left-bottom-text {
    position: absolute;
    left: 5px;
    bottom: 5px;
    color: #ddd;
    z-index: 1;
    font-size: 12px;
  }

  .right-bottom-text {
    position: absolute;
    right: 5px;
    bottom: 5px;
    color: #ddd;
    z-index: 1;
    font-size: 12px;
  }

  .right-slider-box {
    position: absolute;
    right: 1px;
    height: calc(100% - 140px);
    transform: translateY(-50%);
    top: calc(50% - 30px);
    width: 10px;
    background: #333;
    z-index: 1;
    cursor: pointer;
  }

  .right-slider-box:after {
    content: '';
    position: absolute;
    bottom: -20px;
    left: 0;
    height: 20px;
    width: 100%;
    background: #333;
  }

  .slider {
    height: 20px;
    width: 100%;
    position: absolute;
    top: 0;
    z-index: 10;
    background: #9e9e9e;
    cursor: move
  }

  .orientation-top {
    position: absolute;
    left: 50%;
    top: 30px;
    color: #f44336;
    transform: translateX(-50%);
    z-index: 1;
  }

  .orientation-bottom {
    position: absolute;
    left: 50%;
    bottom: 15px;
    color: #f44336;
    transform: translateX(-50%);
    z-index: 1;
  }

  .orientation-left {
    position: absolute;
    top: 50%;
    left: 15px;
    color: #f44336;
    transform: translateY(-50%);
    z-index: 1;
  }

  .orientation-right {
    position: absolute;
    top: 50%;
    right: 15px;
    color: #f44336;
    transform: translateY(-50%);
    z-index: 1;
  }
}
</style>
