<template>
  <div class="chart-data-editor">
    <div class="editor-content">
      <div class="range-box">
        <div 
          class="temp-range" 
          :style="{
            width: tempRangeSize.width + 'px',
            height: tempRangeSize.height + 'px',
          }"
        ></div>
        <div 
          :class="['range-line', line.type]" 
          v-for="line in rangeLines" 
          :key="line.type" 
          :style="line.style"
        ></div>
        <div 
          class="resizable" 
          :style="resizablePointStyle"
          @mousedown.stop="changeSelectRange($event)"
        ></div>
      </div>
      <table>
        <tbody>
          <tr v-for="rowIndex in 30" :key="rowIndex">
            <td v-for="colIndex in 7" :key="colIndex" :class="{ 'head': colIndex === 1 && rowIndex <= selectedRange[1] }">
              <input 
                :class="['item', { 'selected': rowIndex <= selectedRange[1] && colIndex <= selectedRange[0] }]"
                :id="`cell-${rowIndex - 1}-${colIndex - 1}`"
                autocomplete="off"
              >
            </td>
          </tr>
        </tbody>
      </table>
    </div>

    <div class="btns">
      <Button class="btn" @click="closeEditor()">取消</Button>
      <Button type="primary" class="btn" @click="getTableData()">确认</Button>
    </div>
  </div>
</template>

<script lang="ts">
import { ChartData } from '@/types/slides'
import { computed, defineComponent, onMounted, PropType, ref } from 'vue'

const CELL_WIDTH = 100
const CELL_HEIGHT = 32

export default defineComponent({
  name: 'chart-data-editor',
  props: {
    data: {
      type: Object as PropType<ChartData>,
      required: true,
    }
  },
  setup(props, { emit }) {
    const selectedRange = ref([0, 0])
    const tempRangeSize = ref({ width: 0, height: 0 })

    // 当前选区的边框线条位置
    const rangeLines = computed(() => {
      const width = selectedRange.value[0] * CELL_WIDTH
      const height = selectedRange.value[1] * CELL_HEIGHT
      return [
        { type: 't', style: {width: width + 'px'} },
        { type: 'b', style: {top: height + 'px', width: width + 'px'} },
        { type: 'l', style: {height: height + 'px'} },
        { type: 'r', style: {left: width + 'px', height: height + 'px'} },
      ]
    })

    // 当前选区的缩放点位置
    const resizablePointStyle = computed(() => {
      const width = selectedRange.value[0] * CELL_WIDTH
      const height = selectedRange.value[1] * CELL_HEIGHT
      return { left: width + 'px', top: height + 'px' }
    })

    // 初始化图表数据：将数据格式化并填充到DOM
    const initData = () => {
      const _data: string[][] = []

      const { labels, series } = props.data
      const rowCount = labels.length
      const colCount = series.length

      for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
        const row = [labels[rowIndex]]
        for (let colIndex = 0; colIndex < colCount; colIndex++) {
          row.push(series[colIndex][rowIndex] + '')
        }
        _data.push(row)
      }

      for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
        for (let colIndex = 0; colIndex < colCount + 1; colIndex++) {
          const inputRef = document.querySelector(`#cell-${rowIndex}-${colIndex}`) as HTMLInputElement
          if (!inputRef) continue
          inputRef.value = _data[rowIndex][colIndex] + ''
        }
      }

      selectedRange.value = [colCount + 1, rowCount]
    }

    onMounted(initData)

    // 获取当前图表DOM中的数据，整理格式化后传递出去
    const getTableData = () => {
      const [col, row] = selectedRange.value

      const labels: string[] = []
      const series: number[][] = []

      // 第一列为系列名，实际数据从第二列开始
      for (let rowIndex = 0; rowIndex < row; rowIndex++) {
        let labelsItem = `类别${rowIndex + 1}`
        const labelInputRef = document.querySelector(`#cell-${rowIndex}-0`) as HTMLInputElement
        if (labelInputRef && labelInputRef.value) labelsItem = labelInputRef.value
        labels.push(labelsItem)
      }

      for (let colIndex = 1; colIndex < col; colIndex++) {
        const seriesItem = []
        for (let rowIndex = 0; rowIndex < row; rowIndex++) {
          const valueInputRef = document.querySelector(`#cell-${rowIndex}-${colIndex}`) as HTMLInputElement
          let value = 0
          if (valueInputRef && valueInputRef.value && !!(+valueInputRef.value)) {
            value = +valueInputRef.value
          }
          seriesItem.push(value)
        }
        series.push(seriesItem)
      }
      const data = { labels, series }
      emit('save', data)
    }

    // 关闭图表数据编辑器
    const closeEditor = () => emit('close')

    // 鼠标拖拽修改选中的数据范围
    const changeSelectRange = (e: MouseEvent) => {
      let isMouseDown = true

      const startPageX = e.pageX
      const startPageY = e.pageY

      const originWidth = selectedRange.value[0] * CELL_WIDTH
      const originHeight = selectedRange.value[1] * CELL_HEIGHT

      document.onmousemove = e => {
        if (!isMouseDown) return

        const currentPageX = e.pageX
        const currentPageY = e.pageY

        const x = currentPageX - startPageX
        const y = currentPageY - startPageY

        const width = originWidth + x
        const height = originHeight + y

        tempRangeSize.value = { width, height }
      }

      document.onmouseup = e => {
        isMouseDown = false
        document.onmousemove = null
        document.onmouseup = null

        const endPageX = e.pageX
        const endPageY = e.pageY

        if (startPageX === endPageX && startPageY === endPageY) return

        // 拖拽结束时，范围超过格子一半自动扩大到下一格（如拖动到一格半多的位置，会自动扩展到两格，横竖都同理）
        let width = tempRangeSize.value.width
        let height = tempRangeSize.value.height
        if (width % CELL_WIDTH > CELL_WIDTH * 0.5) width = width + (CELL_WIDTH - width % CELL_WIDTH)
        if (height % CELL_HEIGHT > CELL_HEIGHT * 0.5) height = height + (CELL_HEIGHT - height % CELL_HEIGHT)

        let row = Math.round(height / CELL_HEIGHT)
        let col = Math.round(width / CELL_WIDTH)

        if (row < 3) row = 3
        if (col < 2) col = 2

        selectedRange.value = [col, row]
        tempRangeSize.value = { width: 0, height: 0 }
      }
    }

    return {
      tempRangeSize,
      rangeLines,
      resizablePointStyle,
      changeSelectRange,
      selectedRange,
      getTableData,
      closeEditor,
    }
  },
})
</script>

<style lang="scss" scoped>
.chart-data-editor {
  width: 600px;
  position: relative;
}
.editor-content {
  width: 100%;
  height: 360px;
  overflow: overlay;
  position: relative;
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}
.range-box {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  user-select: none;
}
.temp-range {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  background-color: rgba($color: #888, $alpha: .3);
}
.range-line {
  position: absolute;
  width: 0;
  height: 0;
  left: 0;
  top: 0;
  border: 0 solid $themeColor;

  &.t {
    border-top-width: 1px;
  }
  &.b {
    border-bottom-width: 1px;
  }
  &.l {
    border-left-width: 1px;
  }
  &.r {
    border-right-width: 1px;
  }
}
.resizable {
  position: absolute;
  width: 12px;
  height: 12px;
  left: 0;
  top: 0;
  margin: -9px 0 0 -9px;
  cursor: nwse-resize;

  &::after {
    content: '';
    position: absolute;
    width: 4px;
    height: 12px;
    right: 0;
    top: 0;
    background-color: $themeColor;
  }
  &::before {
    content: '';
    position: absolute;
    width: 12px;
    height: 4px;
    right: 0;
    bottom: 0;
    background-color: $themeColor;
  }
}
table {
  width: 100%;
  height: 100%;
  user-select: none;
  table-layout: fixed;

  td {
    text-align: center;
    border: 1px solid #ccc;
    vertical-align: middle;
    width: 100px;
    height: 32px;

    &.head {
      background-color: rgba($color: $themeColor, $alpha: .1);
    }
  }
  .item {
    width: 100%;
    height: 100%;
    border: 0;
    outline: 0;
    font-size: 13px;
    text-align: center;
    background-color: transparent;

    &.selected {
      background-color: rgba($color: $themeColor, $alpha: .02);
    }
  }
}
.btns {
  margin-top: 10px;
  text-align: right;

  .btn {
    margin-left: 10px;
  }
}
</style>