import { Text, G } from '@svgdotjs/svg.js'
import { degToRad, camelCaseToHyphen } from '../utils'
import merge from 'deepmerge'
import MindMap from '..'

interface WatermarkOptions {
  mindMap: MindMap
}

interface WatermarkConfig {
  text?: string | number
  lineSpacing?: number
  textSpacing?: number
  angle?: number
  textStyle?: Record<string, any>
  belowNode?: boolean
  onlyExport?: boolean
}

interface TextStyle {
  [key: string]: any
}

// 水印插件
class Watermark {
  static instanceName: string = 'watermark'

  private mindMap: any
  private lineSpacing: number
  private textSpacing: number
  private angle: number
  private text: string
  private textStyle: TextStyle
  private watermarkDraw: G | null
  private isInExport: boolean
  private maxLong: number

  constructor(opt: WatermarkOptions = {} as WatermarkOptions) {
    this.mindMap = opt.mindMap
    this.lineSpacing = 0
    this.textSpacing = 0
    this.angle = 0
    this.text = ''
    this.textStyle = {}
    this.watermarkDraw = null
    this.isInExport = false
    this.maxLong = this.getMaxLong()
    this.updateWatermark(this.mindMap.opt.watermarkConfig || {})
    this.bindEvent()
  }

  private getMaxLong(): number {
    return Math.sqrt(
      Math.pow(this.mindMap.width, 2) + Math.pow(this.mindMap.height, 2)
    )
  }

  private bindEvent(): void {
    this.onResize = this.onResize.bind(this)
    this.mindMap.on('resize', this.onResize)
  }

  private unBindEvent(): void {
    this.mindMap.off('resize', this.onResize)
  }

  private onResize(): void {
    this.maxLong = this.getMaxLong()
    this.draw()
  }

  private createContainer(): void {
    if (this.watermarkDraw) return
    this.watermarkDraw = new G()
      .css({
        pointerEvents: 'none',
        userSelect: 'none'
      })
      .addClass('smm-water-mark-container')
    this.updateLayer()
  }
  private updateLayer(): void {
    if (!this.watermarkDraw) return
    const { belowNode } = this.mindMap.opt.watermarkConfig
    if (belowNode) {
      this.watermarkDraw.insertBefore(this.mindMap.draw)
    } else {
      this.mindMap.svg.add(this.watermarkDraw)
    }
  }

  private removeContainer(): void {
    if (!this.watermarkDraw) {
      return
    }
    this.watermarkDraw.remove()
    this.watermarkDraw = null
  }

  private hasWatermark(): boolean {
    return !!this.text.trim()
  }

  private handleConfig({ text, lineSpacing, textSpacing, angle, textStyle }: WatermarkConfig): void {
    this.text = text === undefined ? '' : String(text).trim()
    this.lineSpacing =
      typeof lineSpacing === 'number' && lineSpacing > 0 ? lineSpacing : 100
    this.textSpacing =
      typeof textSpacing === 'number' && textSpacing > 0 ? textSpacing : 100
    this.angle =
      typeof angle === 'number' && angle >= 0 && angle <= 90 ? angle : 30
    this.textStyle = Object.assign(this.textStyle, textStyle || {})
  }

  private clear(): void {
    if (this.watermarkDraw) this.watermarkDraw.clear()
  }

  private draw(): void {
    this.clear()
    const { onlyExport } = this.mindMap.opt.watermarkConfig
    if (onlyExport && !this.isInExport) return
    if (!this.hasWatermark()) {
      this.removeContainer()
      return
    }
    this.createContainer()
    let x = 0
    while (x < this.mindMap.width) {
      this.drawText(x)
      x += this.lineSpacing / Math.sin(degToRad(this.angle))
    }

    let yOffset =
      this.lineSpacing / Math.cos(degToRad(this.angle)) || this.lineSpacing
    let y = yOffset
    while (y < this.mindMap.height) {
      this.drawText(0, y)
      y += yOffset
    }
  }

  private drawText(x: number, y?: number): void {
    let long = Math.min(
      this.maxLong,
      (this.mindMap.width - x) / Math.cos(degToRad(this.angle))
    )
    let g = new G()
    let bbox = null
    let bboxWidth = 0
    let textHeight = -1
    while (bboxWidth < long) {
      let text = new Text().text(this.text)
      g.add(text)
      text.transform({
        translateX: bboxWidth
      })
      this.setTextStyle(text)
      bbox = g.bbox()
      if (textHeight === -1) {
        textHeight = bbox.height
      }
      bboxWidth = bbox.width + this.textSpacing
    }
    let params = {
      rotate: this.angle,
      origin: 'top left',
      translateX: x,
      translateY: textHeight
    }
    if (y !== undefined) {
      params.translateY = y + textHeight
    }
    g.transform(params)
    this.watermarkDraw?.add(g)
  }

  private setTextStyle(text: Text): void {
    Object.keys(this.textStyle).forEach(item => {
      let value = this.textStyle[item]
      if (item === 'color') {
        text.fill(value)
      } else {
        text.css(camelCaseToHyphen(item) as CSSStyleName, value)
      }
    })
  }

  public updateWatermark(config: WatermarkConfig): void {
    this.mindMap.opt.watermarkConfig = merge(
      this.mindMap.opt.watermarkConfig,
      config
    )
    this.updateLayer()
    this.handleConfig(config)
    this.draw()
  }

  public beforePluginRemove(): void {
    this.unBindEvent()
    this.removeContainer()
  }

  public beforePluginDestroy(): void {
    this.unBindEvent()
    this.removeContainer()
  }
}

export default Watermark