import { defineComponent, onMounted, PropType, ref, Ref, toRefs, onUpdated, onUnmounted, watch } from 'vue'
import { EngravingRules, IMusicScore, OpenSheetMusicDisplay, OSMDOptions } from '/osmd-extended/src'
import { useEngravingRules, useOsmd, useOsmdLoader } from './uses'
import SettingState from '/src/pages/detail/setting-state'
import detailState from '/src/pages/detail/state'
import event from '/src/components/music-score/event'
import styles from '/src/components/music-score/index.module.less'
import SectionWrap from '../pages/detail/section-box'
import runtime, * as RuntimeUtils from '/src/pages/detail/runtime'
import { Toast } from 'vant'
import axios from 'umi-request'
import { Cursor } from '../helpers/cursor'
import { formatZoom } from '../helpers/utils'

export type onRerenderType = (osmd?: OpenSheetMusicDisplay | null) => void

export type SetRenderOptions = {
  score?: string
}

const rendered = ref(false)

export default defineComponent({
  name: 'music-score',
  props: {
    isSoundEffect: {
      type: Boolean,
      default: false,
    },
    score: {
      type: String,
      default: '',
    },
    showPartNames: {
      type: Boolean,
      default: false,
    },
    opotions: {
      type: Object as PropType<OSMDOptions>,
      default: () => {},
    },
    EngravingRules: {
      type: Object as PropType<{ [P in keyof EngravingRules]?: EngravingRules[P] | undefined }>,
      default: () => {},
    },
    showSection: {
      type: Boolean,
      default: true,
    },
  },
  emits: ['rerender', 'startRender', 'renderError', 'loaddingEnd'],
  setup(props, { emit, expose }) {
    // 获取svg文件
    const getSvgXml = async (url: string) => {
      event.off('section-click', RuntimeUtils.noteClick)
      emit('startRender')
      const svg = await axios.get(url)
      if (container.value) {
        let svgDiv = document.createElement('div')
        svgDiv.innerHTML = svg
        container.value?.appendChild(svgDiv.firstElementChild!) //innerHTML =  svg
        let svgEL = document.querySelector('#osmdSvgPage1')
        const svgContainer = document.getElementById('svgContainer')
        let width: any = svgEL?.getAttribute('width')
        let height: any = svgEL?.getAttribute('height')
        width = isNaN(Number(width)) ? 0 : Number(width)
        height = isNaN(Number(height)) ? 0 : Number(height)
        if (width && height) {
          let ratio = height / width
          let _w: any = svgContainer?.offsetWidth || document.body.clientWidth
          detailState.zoom = _w / width
          svgEL?.setAttribute('width', _w)
          svgEL?.setAttribute('height', _w * ratio + '')
        }
      }
    }
    const getNotePosition = (times: any[]) => {
      for (let i = 0; i < times.length; i++) {
        const item = times[i]

        if (item?.svgElelent?.bbox) {
          item.svgElelent.bbox.x *= detailState.zoom
          item.svgElelent.bbox.y *= detailState.zoom
          item.svgElelent.bbox.w *= detailState.zoom
          item.svgElelent.bbox.h *= detailState.zoom
        }
        if (item.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.boundingBox) {
          item.noteElement.sourceMeasure.verticalMeasureList[0].boundingBox.absolutePosition.x *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].boundingBox.absolutePosition.y *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].boundingBox.size.width *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].boundingBox.size.height *= detailState.zoom
        }
        if (item.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.stave) {
          item.noteElement.sourceMeasure.verticalMeasureList[0].stave.x *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].stave.y *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].stave.width *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].stave.height *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].stave.start_x *= detailState.zoom
          item.noteElement.sourceMeasure.verticalMeasureList[0].stave.end_x *= detailState.zoom
        }
        if (item?.cursorBox?.move) {
          console.log(detailState.zoom)
          // console.log("🚀 ~ item.cursorBox.x", item.cursorBox.x * detailState.zoom, item?.svgElelent?.bbox?.x)
          // item.cursorBox.x = item?.svgElelent?.bbox?.x ? formatZoom(item.svgElelent.bbox.x) : item.cursorBox.x * detailState.zoom
          item.cursorBox.x *= detailState.zoom
          item.cursorBox.y = item.cursorBox.y * detailState.zoom + 10
          item.cursorBox.w *= detailState.zoom
          item.cursorBox.h *= detailState.zoom
        }
      }
      return times
    }
    // 获取json数据
    const getMusicJson = async (url: string) => {
      const res = await axios.get(url)
      SettingState.sett.scoreSize = res?.osmd?.scoreSize || 'middle'
      if (res && Array.isArray(res.times)) {
        detailState.times = getNotePosition(res.times) // res.times
        if (detailState.times?.[0]?.cursorBox?.move) {
          // 初始化cursor
          res.osmd.cursor = new Cursor({ ...detailState.times?.[0]?.cursorBox })
          // console.log("🚀 ~ res.osmd.cursor", res.osmd.cursor)
          container.value?.appendChild(res.osmd.cursor.img)
        }
        detailState.renderType = 'cache'
        emit('rerender', res.osmd)
        runtime.isFirstPlay = false
        event.on('section-click', RuntimeUtils.noteClick)
      }
    }
    const { EngravingRules, opotions, showPartNames, score } = toRefs(props)
    const container: Ref<HTMLDivElement | undefined> = ref()
    let osmd: Ref<OpenSheetMusicDisplay | undefined> = ref()

    const setOsdm = () => {
      event.off('section-click', RuntimeUtils.noteClick)
      emit('startRender')
      osmd.value = useOsmd(
        container,
        { ...opotions.value, drawPartNames: showPartNames.value },
        EngravingRules.value
      ).value
      if (osmd.value) {
        useEngravingRules(osmd.value, EngravingRules.value)
        runtime.isFirstPlay = false
        event.on('section-click', RuntimeUtils.noteClick)
      }
    }

    // 直接读取渲染数据
    const productJsonAndSvg = async (renderData: any) => {
      await getSvgXml(renderData.svg)
      getMusicJson(renderData.json)
    }

    onMounted(async () => {
      if (rendered.value) return
      // console.log( SettingState.sett.keySignature, detailState.activeDetail)
      if (
        (SettingState.sett.type === 'staff' && !detailState?.activeDetail?.musicSvg) || // 没有五线谱缓存数据
        (SettingState.sett.type === 'jianpu' &&
          SettingState.sett.keySignature &&
          !detailState?.activeDetail?.musicFirstSvg) || // 没有固定调缓存数据
        (SettingState.sett.type === 'jianpu' &&
          !SettingState.sett.keySignature &&
          !detailState?.activeDetail?.musicJianSvg) // 没有首调缓存数据
      ) {
        setOsdm()
        return
      }
      let renderData: any = null
      try {
        if (SettingState.sett.type === 'staff' && detailState.activeDetail?.musicSvg) {
          renderData = JSON.parse(detailState.activeDetail.musicSvg)
        } else {
          if (SettingState.sett.keySignature && detailState.activeDetail?.musicFirstSvg) {
            //固定调
            renderData = JSON.parse(detailState.activeDetail.musicFirstSvg)
          } else {
            // 首调
            if (detailState.activeDetail?.musicJianSvg) {
              renderData = JSON.parse(detailState.activeDetail.musicJianSvg)
            }
          }
        }
      } catch (error) {
        console.error(error)
        detailState.renderType = 'native'
      }
      if (renderData && !props.isSoundEffect) {
        productJsonAndSvg(renderData)
      } else {
        setOsdm()
      }
    })

    onUnmounted(() => {
      event.off('section-click', RuntimeUtils.noteClick)
    })

    watch(
      [score, osmd],
      async () => {
        if (osmd && osmd.value && score.value) {
          try {
            emit('startRender')
            await useOsmdLoader(osmd.value, score.value)
            emit('rerender', osmd.value)
            event.emit('loaded')
            resetFormate()
          } catch (error) {
            console.error(error)
            emit('renderError')
          }
        }
      },
      { immediate: true }
    )

    /**
     * 重新渲染曲谱方法
     * @param {SetRenderOptions} options
     */
    const setRender = async ({ score: s }: SetRenderOptions = {}) => {
      const renderScore = s || score.value
      // console.log(renderScore)
      // Toast('加载中，请稍后...')
      setTimeout(async () => {
        if (osmd && osmd.value && renderScore) {
          await osmd.value.clear()
          setOsdm()
          try {
            detailState.section = []
            detailState.sectionStatus = false
            await useOsmdLoader(osmd.value, renderScore)
            emit('rerender', osmd.value)
            event.emit('loaded')
            resetFormate()
          } catch (error) {
            console.error(error)
            emit('renderError')
          }
        }
      }, 100)
    }

    const reRender = async () => {
      // if (detailState.renderType == 'cache'){
      //   detailState.times = getNotePosition(detailState.times)
      // }
      await osmd.value?.render()
    }

    const resetFormate = () => {
      const stafflines: SVGAElement[] = Array.from((container.value as HTMLElement).querySelectorAll('.staffline'))
      const baseStep = 4 // 两个元素相间，的间距
      for (let i = 0, len = stafflines.length; i < len; i++) {
        const staffline = stafflines[i]
        const stafflineBox = staffline.getBBox()
        const stafflineCenter = stafflineBox.y + stafflineBox.height / 2
        const vfmeasures: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-measure'))
        const vfcurve: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-curve'))
        const vfvoices: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-measure > .vf-voices'))
        const vfbeams: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-measure > .vf-beams'))
        const vfties: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-ties'))
        const vflines: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-line'))
        const texts: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-measure > .vf-stave text'))
        const rects: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-measure > .vf-stave rect[fill=none]'))
        const staveSection: SVGAElement[] = Array.from(staffline.querySelectorAll('.vf-measure .vf-staveSection'))
        // 反复标记 和 小节碰撞
        const repetWord = ['To Coda', 'D.S. al Coda', 'Coda']
        texts
          .filter((n) => repetWord.includes(n.textContent || ''))
          .forEach((t) => {
            vfbeams.forEach((curve) => {
              const result = collisionDetection(t, curve)
              const prePath: SVGAElement = t?.previousSibling as unknown as SVGAElement
              if (result.isCollision) {
                const shift_y = Number(t.getAttribute('y')) - (result.b1 - result.t2) - baseStep + ''
                t.setAttribute('y', shift_y)
                if (
                  prePath &&
                  prePath.getAttribute('stroke-width') === '0.3' &&
                  prePath.getAttribute('stroke') === 'none' &&
                  (prePath.getAttribute('d')?.length || 0) > 3000
                ) {
                  prePath.style.transform = `translateY(${-(result.b1 - result.t2 + baseStep)}px)`
                }
              }
            })
            vfvoices.forEach((curve) => {
              const result = collisionDetection(t, curve)
              const prePath: SVGAElement = t?.previousSibling as unknown as SVGAElement
              if (result.isCollision) {
                const shift_y = Number(t.getAttribute('y')) - (result.b1 - result.t2) - baseStep + ''
                t.setAttribute('y', shift_y)
                if (
                  prePath &&
                  prePath.getAttribute('stroke-width') === '0.3' &&
                  prePath.getAttribute('stroke') === 'none' &&
                  (prePath.getAttribute('d')?.length || 0) > 3000
                ) {
                  prePath.style.transform = `translateY(${-(result.b1 - result.t2 + baseStep)}px)`
                }
              }
            })
          })
        // 文字方框和飞线碰撞
        // console.log(staveSection, 'staveSection out')
        staveSection.forEach((t) => {
          let shift_y = 0
          ;[...vfcurve, ...vfties, ...vfvoices].forEach((curve) => {
            const result = collisionDetection(t, curve)
            // console.log(result, curve)
            if (result.isCollision) {
              shift_y = Math.min(shift_y, result.t2 - result.b1 - baseStep)
            }
          })
          t.style.transform = `translateY(${shift_y}px)`
        })

        // 文字和小节碰撞
        let vftexts = Array.from(staffline.querySelectorAll('.vf-text > text')).filter(
          (n: any) => n.getBBox().y < stafflineCenter
        )
        for (let i = 0; i < vftexts.length; i++) {
          const _text = vftexts[i]
          for (let j = 0; j < vftexts.length; j++) {
            if (_text.parentNode === vftexts[j].parentNode) continue
            const result = collisionDetection(_text as SVGAElement, vftexts[j] as SVGAElement)
            if (result.isCollision) {
              if (_text.textContent === vftexts[j].textContent) {
                vftexts[j].parentNode?.removeChild(vftexts[j])
                continue
              }
            }
          }
        }
        vftexts = Array.from(staffline.querySelectorAll('.vf-text > text')).filter(
          (n: any) => n.getBBox().y < stafflineCenter
        )
        let maxY = 0
        let _vftexts: SVGAElement[] = []

        vftexts.forEach((vftext: any) => {
          const textBox = vftext.getBBox()
          if (textBox.y < stafflineCenter) {
            maxY = Math.max(maxY, textBox.y + textBox.height)
            _vftexts.push(vftext as SVGAElement)
          }
        })
        if (maxY !== 0 && _vftexts.length > 1) {
          _vftexts.forEach((vftext) => {
            vftext.setAttribute('y', maxY + '')
          })
        }
        vftexts.forEach((vftext) => {
          ;[...vfcurve, ...vfmeasures, ...vflines].forEach((vfmeasure) => {
            let result = collisionDetection(vftext as SVGAElement, vfmeasure)
            if (result.isCollision && result.b1 < result.b2 && result.t1 < result.b2 - (result.b2 - result.t2) / 2) {
              const shift_y = Number(vftext.getAttribute('y')) - (result.b1 - result.t2) - baseStep + ''
              vftext.setAttribute('y', shift_y)
            }
          })
        })

        vftexts.forEach((vftext) => {
          vftexts.forEach((text) => {
            if (
              vftext.parentNode !== text.parentNode &&
              !['marcato', 'legato'].includes(vftext.textContent as string)
            ) {
              if (['marcato', 'legato'].includes(text.textContent as string)) {
                const result = collisionDetection(vftext as SVGAElement, text as SVGAElement, 30, 30)
                if (result.isCollision) {
                  const textBBox = (vftext as SVGAElement).getBBox()
                  text.setAttribute('x', textBBox.x + textBBox.width + 5 + '')
                  text.setAttribute('y', textBBox.y + textBBox.height - 5 + '')
                }
              } else {
                const result = collisionDetection(vftext as SVGAElement, text as SVGAElement)
                if (result.isCollision) {
                  const _y = Number(vftext.getAttribute('y'))
                  const shift_y = result.b2 - result.t2 < 24 ? 24 : result.b2 - result.t2
                  text.setAttribute('y', _y - shift_y - 0.5 + '')
                }
              }
            }
          })
        })

        const vftextBottom = Array.from(staffline.querySelectorAll('.vf-text > text')).filter(
          (n: any) => n.getBBox().y > stafflineCenter
        )
        const vflineBottom = Array.from(staffline.querySelectorAll('.vf-line')).filter(
          (n: any) => n.getBBox().y > stafflineCenter
        )
        // 去重
        for (let i = 0; i < vftextBottom.length; i++) {
          const _text = vftextBottom[i]
          for (let j = 0; j < vftextBottom.length; j++) {
            if (_text.parentNode === vftextBottom[j].parentNode) continue
            const result = collisionDetection(_text as SVGAElement, vftextBottom[j] as SVGAElement)
            if (result.isCollision) {
              if (_text.textContent === vftextBottom[j].textContent) {
                vftextBottom[j].parentNode?.removeChild(vftextBottom[j])
                continue
              }
            }
          }
        }
        // 1,2线谱底部文字重叠问题
        vftextBottom.forEach((vftext) => {
          ;[...vfmeasures].forEach((n) => {
            let result = collisionDetection(vftext as SVGAElement, n)
            if (result.isCollision) {
              vftext.setAttribute('y', result.b2 + Math.abs(result.t1 - Number(vftext.getAttribute('y'))) + '')
            }
          })
        })
        // 如果渐弱渐强有平行的文字
        vflineBottom.forEach((line) => {
          const texts: any[] = []
          if (line.nextElementSibling?.classList.contains('vf-line')) {
            vftextBottom.forEach((text) => {
              let result = collisionDetection(line as SVGAElement, text as SVGAElement, 20, 20)
              if (result.isCollision) {
                texts.push({
                  text: text as SVGAElement,
                  result,
                })
              }
            })
          }
          if (texts.length === 1) {
            const result = texts[0].result
            const text = texts[0].text
            if (result.x2 + result.w2 < result.x1) {
              // 左
              if (Math.abs(result.y2 - result.y1) > 10) {
                text.setAttribute('y', result.y1 + result.h2 / 2 + '')
              }
            } else if (result.x2 > result.x1 + result.w1) {
              // 右
              if (Math.abs(result.y2 - result.y1) > 10) {
                text.setAttribute('y', result.y1 + result.h2 / 2 + '')
              }
            } else {
              if (Math.abs(result.x2 - result.x1) < Math.abs(result.x2 + result.w2 - result.x1 - result.w1)) {
                // console.log(text, '有交集', '靠左')
                text.setAttribute('x', result.x1 - result.w2 - 5 + '')
                if (Math.abs(result.y2 - result.y1) > 10) {
                  text.setAttribute('y', result.y1 + result.h2 / 2 + '')
                }
              } else {
                // console.log(text, '有交集', '靠右')
                text.setAttribute('x', result.x1 + result.w1 + 5 + '')
                if (Math.abs(result.y2 - result.y1) > 10) {
                  text.setAttribute('y', result.y1 + result.h2 / 2 + '')
                }
              }
            }
          } else if (texts.length === 2) {
            const result1 = texts[0].result
            const text1 = texts[0].text
            const result2 = texts[1].result
            const text2 = texts[1].text
            text1.setAttribute('x', result1.x1 - result1.w2 - 5 + '')
            if (Math.abs(result1.y2 - result1.y1) > 10) {
              text1.setAttribute('y', result1.y1 + result1.h2 / 2 + '')
            }
            text2.setAttribute('x', result2.x1 + result2.w1 + 5 + '')
            if (Math.abs(result2.y2 - result2.y1) > 10) {
              text2.setAttribute('y', result2.y1 + result2.h2 / 2 + '')
            }
          } else if (texts.length === 3) {
            // console.log(texts)
          }
        })

        vftextBottom.forEach((vftext) => {
          vftextBottom.forEach((text) => {
            if (
              vftext.parentNode !== text.parentNode &&
              !['marcato', 'legato', 'cresc.', 'Cantabile'].includes(vftext.textContent as string)
            ) {
              if (['marcato', 'legato', 'cresc.', 'Cantabile'].includes(text.textContent as string)) {
                const result = collisionDetection(vftext as SVGAElement, text as SVGAElement, 30, 30)
                if (result.isCollision) {
                  const textBBox = (vftext as SVGAElement).getBBox()
                  text.setAttribute('x', textBBox.x + textBBox.width + 5 + '')
                  text.setAttribute('y', textBBox.y + textBBox.height - 5 + '')
                }
              } else {
                const result = collisionDetection(vftext as SVGAElement, text as SVGAElement)
                if (result.isCollision) {
                  text.setAttribute('y', result.y1 + result.h1 + result.h2 + '')
                }
              }
            }
          })
        })
      }

      setTimeout(() => resetGlobalText())
    }
    // 技巧文本
    const resetGlobalText = () => {
      if (!container.value) return
      const svg = container.value.querySelector('svg')
      if (!svg) return
      const svgBBox = svg.getBBox()
      let vfstavetempo: SVGAElement[] = Array.from(container.value.querySelectorAll('.vf-stavetempo')).reduce(
        (eles: SVGAElement[], value: any) => {
          if (eles.find((n) => n.outerHTML === value.outerHTML)) value?.parentNode?.removeChild(value)
          else eles.push(value)
          return eles
        },
        []
      )
      const staffline: SVGAElement[] = Array.from(container.value.querySelectorAll('.staffline'))
      const vfmeasures: SVGAElement[] = Array.from(container.value.querySelectorAll('.staffline > .vf-measure'))
      const vftexts: SVGAElement[] = Array.from(container.value.querySelectorAll('.staffline > .vf-text'))
      const vfcurves: SVGAElement[] = Array.from(container.value.querySelectorAll('.staffline > .vf-curve'))

      vfstavetempo.forEach((child: SVGAElement) => {
        let _y = 0
        ;[...vfmeasures, ...vftexts, ...vfcurves].forEach((ele) => {
          const result = collisionDetection(child as SVGAElement, ele)
          if (result.isCollision && (result.b1 < result.b2 || result.r1 > result.l2 || result.l1 < result.r2)) {
            _y = Math.min(_y, result.t2 - result.b1)
          }
        })
        if (_y !== 0) {
          child.style.transform = `translateY(${_y}px)`
        }

        const childBBox = child.getBBox()
        const rightY = (childBBox.x + childBBox.width) * 0.7 - Number(svg.getAttribute('width'))
        if (rightY > 0) {
          ;[...staffline, ...vfstavetempo].forEach((tempo) => {
            if (child != tempo) {
              const result = collisionDetection(child as SVGAElement, tempo, Math.abs(rightY), Math.abs(_y))
              if (result.isCollision) {
                _y = result.t2 - result.b1
              }
            }
          })
          child.style.transform = `translate(-${rightY / 0.7}px,${_y}px)`
        }
      })

      if (svgBBox.y < 0) {
        svg.setAttribute('height', Number(svg.getAttribute('height')) - svgBBox.y + 10 + '')
      }
    }

    // 碰撞检测
    const collisionDetection = (a: SVGAElement, b: SVGAElement, distance: number = 0, distance_y: number = 0) => {
      const abbox = a.getBBox()
      const bbbox = b.getBBox()
      let t1 = abbox.y - distance_y
      let l1 = abbox.x - distance
      let r1 = abbox.x + abbox.width + distance
      let b1 = abbox.y + abbox.height + distance_y

      let t2 = bbbox.y
      let l2 = bbbox.x
      let r2 = bbbox.x + bbbox.width
      let b2 = bbbox.y + bbbox.height
      if (b1 < t2 || l1 > r2 || t1 > b2 || r1 < l2) {
        // 表示没碰上
        return {
          isCollision: false,
          t1,
          l1,
          r1,
          b1,
          t2,
          l2,
          r2,
          b2,
          x1: abbox.x,
          y1: abbox.y,
          x2: bbbox.x,
          y2: bbbox.y,
          h1: abbox.height,
          h2: bbbox.height,
          w1: abbox.width,
          w2: bbbox.width,
        }
      } else {
        return {
          isCollision: true,
          t1,
          l1,
          r1,
          b1,
          t2,
          l2,
          r2,
          b2,
          x1: abbox.x,
          y1: abbox.y,
          x2: bbbox.x,
          y2: bbbox.y,
          h1: abbox.height,
          h2: bbbox.height,
          w1: abbox.width,
          w2: bbbox.width,
        }
      }
    }

    expose({
      setRender,
      reRender,
    })

    return () => {
      return (
        <div
          id="svgContainer"
          class={styles.container}
          style={{ position: 'relative' }}
        >
          <div ref={container}></div>
          {/* 小节覆盖层 */}
          {props.showSection && (
            <SectionWrap
              style={{
                position: 'absolute',
                width: '100%',
                height: '100%',
                zIndex: 1,
                top: 0,
                left: 0,
              }}
            />
          )}
        </div>
      )
    }
  },
})
