<template>
  <div class="formsWrapper">
    <el-form
      class="form1"
      label-position="left"
    >
      <el-form-item label="场景名或完整url">
        <el-input v-model="sceneNameOrUrl" />
      </el-form-item>
      <el-button
        type="primary"
        @click="getWholeData"
      >
        重新获取全部数据点
      </el-button>
    </el-form>
    <el-form
      class="form2"
      label-position="top"
    >
      <el-form-item label="路径1数据(必填)(在全部节点中的index，以英文逗号分隔)(浅蓝色大圆圈表示)">
        <el-input v-model="formData.path1" />
      </el-form-item>
      <el-form-item label="路径2数据（选填）(在全部节点中的index，以英文逗号分隔)（深蓝色小圆圈表示）">
        <el-input v-model="formData.path2" />
      </el-form-item>
      <el-button
        type="primary"
        @click="renderPath"
      >
        显示路径
      </el-button>
      <el-button @click="onResetPath">
        Reset
      </el-button>
    </el-form>
    <el-form
      class="form3"
      label-position="top"
    >
      <el-form-item label="起点坐标(以英文逗号分隔)(红色小圆圈表示)">
        <el-input v-model="formData.startPoint" />
      </el-form-item>
      <el-form-item label="终点坐标(以英文逗号分隔)(绿色小圆圈表示)">
        <el-input v-model="formData.endPoint" />
      </el-form-item>
      <el-button
        type="primary"
        @click="renderStartEndPoint"
      >
        显示起点终点
      </el-button>
      <el-button @click="onResetStartEndPoint">
        Reset
      </el-button>
    </el-form>
  </div>

  <div class="infoText">
    {{ infoText }}
  </div>
  <div class="map">
    <div class="svgWrapper" />
    <div class="button-group">
      <el-button
        class="btn"
        type="primary"
        @click="zoomInToStartPoint"
      >
        zoom in到起点
      </el-button>
      <el-button
        class="btn"
        type="primary"
        @click="zoomInToEndPoint"
      >
        zoom in到终点
      </el-button>
      <el-button
        clas="btn"
        @click="resetZoom"
      >
        reset zoom
      </el-button>
    </div>
  </div>
</template>

<script>
import * as d3 from "d3";
import { getWholeData } from "./api.js";
import { ElLoading } from 'element-plus'

const LENGTH_PER_POINT = 0.36

// 视口尺寸
let svgWidth = document.documentElement.clientWidth - 200
let svgHeight = document.documentElement.clientHeight - 280
let viewportRatio = svgWidth / svgHeight

let pxPerUnitLength = 0 // 原始单位长度对应的像素数
let rawWholeData = []
let wholeDataForRender = []
let startPoint = null
let endPoint = null

// 全部数据点的分布中心
let xCenter = 0 
let yCenter = 0 

function resetGlobalVars() {
  pxPerUnitLength = 0 // 原始单位长度对应的像素数
  rawWholeData = []
  wholeDataForRender = []
  xCenter = 0
  yCenter = 0
  startPoint = null
  endPoint = null
}

let svgNode = null
let gNode = null
let zoomObj = null

export default {
  name: 'App',
  data() {
    return {
      sceneNameOrUrl: 't-8KbK1JjubE',
      infoText: '',
      loadingHandler: null,
      formData: {
        path1: '',
        path2: '',
        startPoint: '',
        endPoint: '',
      }
    }
  },
  computed: {
  },
  mounted() {
    svgNode = d3.select('.svgWrapper').append("svg")
      .attr("width", svgWidth)
      .attr('height', svgHeight)
    gNode = svgNode.append('g')
    this.getWholeData()
  },
  methods: {
    inputPathStringToArray(input) {
      let temp = input.trim()
      if(temp[0] === '[') { temp = temp.substr(1)}
      if (temp[temp.length - 1] === ']') { temp = temp.substr(0, temp.length - 1) }
      temp = temp.trim()
      if (!temp) {
        return []
      }
      let mapSuccess = true
      temp = temp.split(',').map((item) => {
        item = item.trim()
        if (!item) {
          return undefined
        } else {
          const num = Number(item)
          if (!Number.isSafeInteger(num)) {
            mapSuccess = false
            return undefined
          } else {
            return num
          }
        }
      })
      if (!mapSuccess) {
        window.alert(`解析输入路径失败：${input}`)
        return -1
      } else {
        return temp.filter((item) => {
          return item !== undefined
        })
      }
    },
    getWholeData() {
      if (!this.sceneNameOrUrl.trim()) {
        window.alert('场景名或完整url必填！')
        return
      }
      this.loadingHandler = ElLoading.service({
        lock: true,
        text: 'Loading',
        background: 'rgba(0, 0, 0, 0.7)',
      })

      resetGlobalVars()
      gNode.selectAll('rect').remove()
      gNode.selectAll('circle').remove()
      

      const that = this
      getWholeData(this.sceneNameOrUrl).then((res) => {
        rawWholeData = res
        // 原始字符串数组处理成二维数组
        let wholeInputPointArray = rawWholeData.map((eachString, index) => {
          console.assert(eachString.split(' ').length === 12, `index ${index}处数据点 数字个数不是12！`)
          return eachString.split(' ')
        })

        // 所有点的分布情况
        let xArray = wholeInputPointArray.map((eachPoint) => {
          return eachPoint[0]
        })
        let xLength = Math.max(...xArray) - Math.min(...xArray)
        xCenter = (Math.max(...xArray) + Math.min(...xArray)) / 2

        let yArray = wholeInputPointArray.map((eachPoint) => {
          return eachPoint[1]
        })
        let yLength = Math.max(...yArray) - Math.min(...yArray)
        yCenter = (Math.max(...yArray) + Math.min(...yArray)) / 2

        let areaRatio = xLength / yLength

        // 各个点坐标映射到视口坐标
        if (viewportRatio >= areaRatio) { // 分布范围应略小于svg尺寸
          pxPerUnitLength = svgHeight / yLength * 0.9
        } else {
          pxPerUnitLength = svgWidth / xLength * 0.9
        }
        let wholeXArrayInPx = xArray.map((eachX) => {
          return (eachX - xCenter) * pxPerUnitLength + svgWidth / 2
        })
        let wholeYArrayInPx = yArray.map((eachY) => {
          return (eachY - yCenter) * pxPerUnitLength + svgHeight / 2
        })

        // 组合成最终数据用来渲染
        for (let index = 0; index < wholeInputPointArray.length; index++) {
          wholeDataForRender.push([wholeXArrayInPx[index], wholeYArrayInPx[index], ...wholeInputPointArray[index], index])
        }

        gNode.selectAll('rect').data(wholeDataForRender).enter().append('rect')
          .attr('x', (d) => d[0] - LENGTH_PER_POINT * pxPerUnitLength / 2)
          .attr('y', (d) => d[1] - LENGTH_PER_POINT * pxPerUnitLength / 2)
          .attr('width', LENGTH_PER_POINT * pxPerUnitLength)
          .attr('height', LENGTH_PER_POINT * pxPerUnitLength)
          .attr('fill', 'black')
          .attr('render-data', (d) => {
            return d
          })

        gNode.selectAll('rect').on('mouseover', function(e) {
          d3.select(this).attr('fill', 'orange')
          let renderDataItem = e.target.attributes['render-data'].value.split(',')
          that.infoText = `数据点index(从0数起）：${renderDataItem[renderDataItem.length - 1]}, \n具体值： ${renderDataItem.slice(2, 14).join(', ')}`
        }).on('mouseleave', function (e) {
          d3.select(this).attr('fill', 'black')
          that.infoText = ''
        })

        zoomObj = d3.zoom().on("zoom", zoomed)
        svgNode.call(zoomObj);

        function zoomed({transform}) {
          gNode.attr("transform", transform);
        }
      }).finally(() => {
        this.loadingHandler.close()
      })
    },
    renderPath() {
      if (!this.formData.path1.trim()) {
        window.alert('路径1必填！')
        return
      }
      
      gNode.selectAll('circle.path1').remove()
      gNode.selectAll('circle.path2').remove()
      
      
      let rawPathDataIndex = this.inputPathStringToArray(this.formData.path1)
      if (rawPathDataIndex === -1) {
        return
      }
      let rawPathDataIndex2 = this.inputPathStringToArray(this.formData.path2)
      if (rawPathDataIndex2 === -1) {
        return
      }
      // 基于path index 拿到path节点数组
      let rawPathData = {
        data: [
        ]
      }
      for (let index = 0; index < rawPathDataIndex.length; index++) {
        const element = rawWholeData[rawPathDataIndex[index]];
        rawPathData.data.push(element)
      }
      let rawPathData2 = {
        data: [
        ]
      }
      for (let index = 0; index < rawPathDataIndex2.length; index++) {
        const element = rawWholeData[rawPathDataIndex2[index]];
        rawPathData2.data.push(element)
      }

      // 原始字符串数组处理成二维数组
      let pathInputPointArray = rawPathData.data.map((eachString) => {
        return eachString.split(' ')
      })
      let pathInputPointArray2 = rawPathData2.data.map((eachString) => {
        return eachString.split(' ')
      })

      // 各个点坐标映射到视口坐标
      let pathXArrayInPx = pathInputPointArray.map((eachPoint) => {
        return eachPoint[0]
      }).map((eachX) => {
        return (eachX - xCenter) * pxPerUnitLength + svgWidth / 2
      })
      let pathYArrayInPx = pathInputPointArray.map((eachPoint) => {
        return eachPoint[1]
      }).map((eachY) => {
        return (eachY - yCenter) * pxPerUnitLength + svgHeight / 2
      })
      let pathXArrayInPx2 = pathInputPointArray2.map((eachPoint) => {
        return eachPoint[0]
      }).map((eachX) => {
        return (eachX - xCenter) * pxPerUnitLength + svgWidth / 2
      })
      let pathYArrayInPx2 = pathInputPointArray2.map((eachPoint) => {
        return eachPoint[1]
      }).map((eachY) => {
        return (eachY - yCenter) * pxPerUnitLength + svgHeight / 2
      })

      // 组合成最终数据用来渲染
      let pathDataForRender = []
      for (let index = 0; index < pathInputPointArray.length; index++) {
        pathDataForRender.push([pathXArrayInPx[index], pathYArrayInPx[index], ...pathInputPointArray[index]])
      }
      let pathDataForRender2 = []
      for (let index = 0; index < pathInputPointArray2.length; index++) {
        pathDataForRender2.push([pathXArrayInPx2[index], pathYArrayInPx2[index], ...pathInputPointArray2[index]])
      }

      // 进行渲染
      gNode.selectAll('circle.path1').data(pathDataForRender).enter().append('circle')
        .classed('path1', true)
        .attr('cx', (d) => d[0])
        .attr('cy', (d) => d[1])
        .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 2)
        .attr('fill', '#000088')
        .attr('pointer-events', 'none')
      gNode.selectAll('circle.path2').data(pathDataForRender2).enter().append('circle')
        .classed('path2', true)
        .attr('cx', (d) => d[0])
        .attr('cy', (d) => d[1])
        .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 3)
        .attr('fill', 'blue')
        .attr('pointer-events', 'none')
    },
    onResetPath() {
      this.formData.path1 = ''
      this.formData.path2 = ''
      gNode.selectAll('circle.path1').remove()
      gNode.selectAll('circle.path2').remove()
    },
    renderStartEndPoint() {
      gNode.selectAll('circle.start').remove()
      gNode.selectAll('circle.end').remove()

      startPoint = this.formData.startPoint.trim()
      if(startPoint[0] === '[') { startPoint = startPoint.substr(1)}
      if (startPoint[startPoint.length - 1] === ']') { startPoint = startPoint.substr(0, startPoint.length - 1) }
      startPoint = startPoint.trim()
      startPoint = startPoint.split(',').map((item) => {
        item = item.trim()
        if (!item) {
          return undefined
        } else {
          return Number(item)
        }
      })
      startPoint = startPoint.filter((item) => {
        return item !== undefined
      })
      if (startPoint.length !== 2) {
        window.alert(`解析起点坐标失败`)
        startPoint = null
      }

      endPoint = this.formData.endPoint.trim()
      if(endPoint[0] === '[') { endPoint = endPoint.substr(1)}
      if (endPoint[endPoint.length - 1] === ']') { endPoint = endPoint.substr(0, endPoint.length - 1) }
      endPoint = endPoint.trim()
      endPoint = endPoint.split(',').map((item) => {
        item = item.trim()
        if (!item) {
          return undefined
        } else {
          return Number(item)
        }
      })
      endPoint = endPoint.filter((item) => {
        return item !== undefined
      })
      if (endPoint.length !== 2) {
        window.alert(`解析终点坐标失败`)
        endPoint = null
      }
      
      if (startPoint) {
        // 起点坐标映射到视口坐标
        startPoint[0] = (startPoint[0] - xCenter) * pxPerUnitLength + svgWidth / 2
        startPoint[1] = (startPoint[1] - yCenter) * pxPerUnitLength + svgHeight / 2
        // 进行渲染
        gNode.selectAll('circle.start').data([startPoint]).enter().append('circle')
          .classed('start', true)
          .attr('cx', (d) => d[0])
          .attr('cy', (d) => d[1])
          .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 3.5)
          .attr('fill', 'red')
          .attr('pointer-events', 'none')
      }

      if (endPoint) {
        // 终点坐标映射到视口坐标
        endPoint[0] = (endPoint[0] - xCenter) * pxPerUnitLength + svgWidth / 2
        endPoint[1] = (endPoint[1] - yCenter) * pxPerUnitLength + svgHeight / 2
        // 进行渲染
        gNode.selectAll('circle.end').data([endPoint]).enter().append('circle')
          .classed('end', true)
          .attr('cx', (d) => d[0])
          .attr('cy', (d) => d[1])
          .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 3.5)
          .attr('fill', 'green')
          .attr('pointer-events', 'none')
      }
    },
    onResetStartEndPoint() {
      this.formData.startPoint = ''
      this.formData.endPoint = ''
      gNode.selectAll('circle.start').remove()
      gNode.selectAll('circle.end').remove()
    },
    zoomIn(coordinate) {
      if (!Array.isArray(coordinate) || coordinate.length !== 2) {
        return
      }
      svgNode.transition().duration(1000).call(
        zoomObj.transform,
        d3.zoomIdentity.translate(svgWidth / 2, svgHeight / 2).scale(100 / pxPerUnitLength).translate(- coordinate[0], - coordinate[1])
      );
    },
    zoomInToStartPoint() {
      this.zoomIn(startPoint)
    },
    zoomInToEndPoint() {
      this.zoomIn(endPoint)
    },
    resetZoom() {
      svgNode.transition().duration(1000).call(
        zoomObj.transform,
        d3.zoomIdentity.scale(1)
      )
    }
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
.formsWrapper {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.formsWrapper .form1 {
  width: 25vw;
}
.formsWrapper .form2 {
  width: 45vw;
}
.formsWrapper .form3 {
  width: 25vw;
}
.infoText {
  min-height: 2em;
}
.map {
  display: flex;
  align-items: center;
}
.map .button-group {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.map .button-group .btn {
  margin: 10px;
}
.svgWrapper {
  display: inline-block;
  overflow: hidden;
  background: #eee;
}
</style>
