/**
 * 画布上各组件的通用对象，主要是各部件的通用函数，
 * 例如组件的缩放、平移、上移、下移、删除等
 */
import Dom from '../libs/dom'
import Event from '../libs/event'
import util from '../libs/util'
import Source from '../source'
import defaultPaint from '../constants/default_paint'

class Widget extends Event {
  constructor(layer, screen) {
    super()
    this.id = layer.id
    this.type = layer.type
    this.name = layer.name
    this.layer = layer
    this._screen = screen
    this.originalData = []
    this.listeners = []
    this.el = Dom.createElement('div', 'geoeye-widget')
    this.el.id = this.id
    this._bindEvent(this.el)
    this.setLayout()
    this.setSource()
  }

  /**
   * 根据layout选项值更新部件在画布上的位置
   */
  setLayout() {
    if (!this.layer.layout) {
      return
    }
    this.setListener()
    let layout = this.processPlaceholder(this.layer.layout)
    Dom.css(this.el, {
      width: layout.width + 'px',
      height: layout.height + 'px',
      top: layout.offsetY + 'px',
      left: layout.offsetX + 'px',
      display: layout.visible === 'true' || layout.visible ? 'block' : 'none'
    })
  }

  /**
   * 根据source选项值获取组件的数据，并监听source.load事件，触发视图更新
   */
  setSource() {
    this.setListener()
    if (this.sourceInstance) {
      this.sourceInstance.destroy()
    }
    let source = this.layer.source
    if (!source || Object.keys(source).length === 0) {
      setTimeout(() => {
        this.setPaint()
      }, 0)
    } else {
      source = this.processPlaceholder(source)
      this.sourceInstance = Source.create(this.layer.id, source, this)
    }
  }

  /**
   * 根据{{}}占位符设置组件的联动事件监听
   */
  setListener() {
    this.deleteAllListener()
    let sourcePlaceholders = util.scanPlaceholder(this.layer.source)
    sourcePlaceholders.forEach(p => {
      this.on(p, () => {
        this.setSource()
      })
      if (this.listeners.indexOf(p) === -1) {
        this.listeners.push(p)
      }
    })
    this.on('source.load', e => {
      // 监听source.load事件
      this.originalData = e.data || []
      this.setPaint()
    })
    this.listeners.push('source.load')
    let paintPlaceholders = util.scanPlaceholder(this.layer.paint)
    paintPlaceholders.forEach(p => {
      this.on(p, () => {
        this.setPaint()
      })
      if (this.listeners.indexOf(p) === -1) {
        this.listeners.push(p)
      }
    })
    let layoutPlaceholders = util.scanPlaceholder(this.layer.layout)
    layoutPlaceholders.forEach(p => {
      this.on(p, () => {
        this.setLayout()
      })
      if (this.listeners.indexOf(p) === -1) {
        this.listeners.push(p)
      }
    })
  }

  /**
   * 删除所有的联动事件监听
   */
  deleteAllListener() {
    this.listeners.forEach(name => {
      let listenersLength = this._listeners[name].length
      for (let i = 0; i < listenersLength; i++) {
        this.off(name, this._listeners[name][i])
      }
    })
    this.listeners = []
  }

  /**
   * 删除组件
   */
  remove() {
    this._onRemove()
  }

  _onRemove() {
    this.deleteAllListener() // 删除所有监听器
    this.el.remove() //移除元素
  }

  /**
   * 处理占位符，将对象中的占位符替换为对应的值
   */
  processPlaceholder(obj) {
    let res = util.deepCopy(obj)
    let placeholders = util.scanPlaceholder(res)
    if (placeholders.length > 0) {
      let opts = {}
      placeholders.forEach(p => {
        opts[p] = this._screen.store.get(p)
      })
      util.replacePlaceholder(res, opts)
    }
    return res
  }

  _mergePaint() {
    this.setListener()
    let paint = this.processPlaceholder(this.layer.paint)
    let newPaint = util.mergeObjs(
      JSON.parse(JSON.stringify(defaultPaint[this.type])),
      paint
    )
    return newPaint
  }

  _bindEvent(el) {
    el.onmouseenter = e => {
      this._screen.fire('on-mouseenter', {
        event: e,
        data: this.layer
      })
    }
    el.onmouseleave = e => {
      this._screen.fire('on-mouseleave', {
        event: e,
        data: this.layer
      })
    }
    el.onmousedown = e => {
      this._screen.fire('on-mousedown', {
        event: e,
        data: this.layer
      })
    }
    el.onmouseup = e => {
      this._screen.fire('on-mouseup', {
        event: e,
        data: this.layer
      })
    }
    el.onclick = e => {
      this._screen.fire('on-click', {
        event: e,
        data: this.layer
      })
    }
  }
}

export default Widget
