import $ from 'jquery'
import { registerConstant, constants as C, list } from '../constants'
import Block from './ADBlock'

import './ToolBar.css'

const _properties = new WeakMap()

const template = (
`<div class="toolbar">
  <button id="toolbar-select" class="toolbar-select">Sel</button>
</div>`
)

const templateCtrl = (
  `<div class="ctrl selector">
    <div class="wrapper">
      <div class="ctrlPoint lt"></div>
      <div class="ctrlPoint ct"></div>
      <div class="ctrlPoint rt"></div>
      <div class="ctrlPoint lm"></div>
      <div class="ctrlPoint cm"></div>
      <div class="ctrlPoint rm"></div>
      <div class="ctrlPoint lb"></div>
      <div class="ctrlPoint cb"></div>
      <div class="ctrlPoint rb"></div>
    </div>
  </div>`
)

function ToolBar (layouter) {
  if (!this) return null

  _properties.set(this, {
    layouter,
    $this: $(template),
    $control: $(templateCtrl),
    mouse: {},
    ctrlBound: {},
    bound: {}
  })
  const _ = _properties.get(this)

  function _showControlPoint (immediately) {
    const _select = _.layouter.state.select()
    if (_select.length) {
      const bound = _select.bound()
      let t, r, b, l
      for (let i = 0; i < bound.length; i++) {
        t = t === undefined ? bound[i].y.px : Math.min(t, bound[i].y.px)
        l = l === undefined ? bound[i].x.px : Math.min(l, bound[i].x.px)
        b = b === undefined ? bound[i].y.px + bound[i].height.px : Math.max(b, bound[i].y.px + bound[i].height.px)
        r = r === undefined ? bound[i].x.px + bound[i].width.px : Math.max(r, bound[i].x.px + bound[i].width.px)
      }
      _select.after(_.$control)
      if (_.$control.is(':visible') || immediately) {
        setTimeout(function () { _.$control.css({ left: l, top: t, width: r - l, height: b - t }).show() }, 0)
      } else {
        _.$control.show().css({ left: l, top: t, width: r - l, height: b - t })
      }
    } else {
      _.$control.hide()
    }
  }

  _.layouter.append('center', _.$control)

  registerConstant([
    'ADLEVENT_BLOCK_MOUSEDOWN',
    'ADLEVENT_BLOCK_DBLCLICK',
    'ADLEVENT_CANVAS_MOUSEMOVE',
    'ADLEVENT_CANVAS_MOUSEUP',
    'ADLEVENT_CANVAS_MOUSEDOWN',
    'ADLEVENT_BLOCK_CONTROL_POINT_MOUSEDOWN',
    'ADLEVENT_BLOCK_CONTROL_POINT_MOUSEUP',
    'ADLEVENT_BLOCK_SELECT_CHANGED',
    'ADLEVENT_DOCUMENT_MODIFIED',
    'ADLEVENT_TOOLBAR_BUTTON_CLICK',
    'ADLEVENT_TOOLBAR_CONTROL_POINT_SHOW',
    'ADLEVENT_BLOCK_PASTE'
  ])

  _.layouter
    .registerEvent(C`ADLEVENT_BLOCK_MOUSEDOWN`, function (e) {
      if (_.layouter.state.tool() === 'select') {
        var b = Block(e.currentTarget)
        var sel = b.select()
        if (!b.edit() || !(e.target instanceof window.HTMLSpanElement)) {
          _.layouter.state.status('tool_bar', 'move')
          if (sel) {
            if (e.shiftKey) {
            } else if (e.ctrlKey) {
              b.select(false)
            }
          } else {
            if (e.shiftKey || e.ctrlKey) {
            } else {
              _.layouter.block().select(false)
            }
            b.select(true)
          }
          const scrollTop = document.getElementById('layoutWrapper').scrollTop
          const scrollLeft = document.getElementById('layoutWrapper').scrollLeft
          _.mouse.x = _.mouse.startX = e.pageX + scrollLeft
          _.mouse.y = _.mouse.startY = e.pageY + scrollTop
          _.mouse.press = e.buttons
          _.bound = _.layouter.state.select().bound()
        }
      }
    })

    .registerEvent(C`ADLEVENT_BLOCK_DBLCLICK`, function (e) {
      var block = new Block(e.currentTarget)
      if (_.layouter.state.tool() === 'select') {
        if (block.edit() === false) {
          block.edit(true)
          const x = e.pageX; const y = e.pageY
          var el = document.elementFromPoint(x, y)
          var nodes = el.childNodes
          const selection = document.getSelection()
          if (selection.rangeCount > 0) selection.removeAllRanges()
          for (let i = 0, l = nodes.length; i < l; i++) {
            const node = nodes[i]
            if (node.nodeType === 3) {
              const range = document.createRange()
              range.selectNode(node)
              const rects = range.getClientRects()
              for (let j = 0, rl = rects.length; j < rl; j++) {
                const rect = rects[j]
                if (x > rect.left && x < rect.right && y > rect.top && y < rect.bottom) {
                  for (let c = 0, cl = node.length - 1; c < cl; c++) {
                    const cRange = document.createRange()
                    cRange.setStart(node, c)
                    cRange.setEnd(node, c + 1)
                    const cRect = cRange.getClientRects()[0]
                    if (x > cRect.left && x < cRect.right && y > cRect.top && y < cRect.bottom) {
                      cRange.collapse(x - cRect.left < cRect.right - x)
                      selection.addRange(cRange)
                      break
                    }
                  }
                } else {
                  continue
                }
                if (selection.rangeCount > 0) break
              }
            } else {
              continue
            }
            if (selection.rangeCount > 0) break
          }
          if (selection.rangeCount === 0) {
            const range = document.createRange()
            const nodes = e.currentTarget.childNodes
            const node = nodes[nodes.length - 1]
            range.selectNodeContents(node)
            range.collapse()
            selection.addRange(range)
          }
        }
      }
    })

    .registerEvent(C`ADLEVENT_CANVAS_MOUSEMOVE`, function (e) {
      if (_.layouter.state.tool() === 'select') {
        if (_.mouse.press) {
          if (_.mouse.press === e.buttons) {
            const blocks = _.layouter.state.select()
            const scrollTop = document.getElementById('layoutWrapper').scrollTop
            const scrollLeft = document.getElementById('layoutWrapper').scrollLeft
            const x = _.mouse.x = e.pageX + scrollLeft - _.mouse.startX
            const y = _.mouse.y = e.pageY + scrollTop - _.mouse.startY
            const status = _.layouter.state.status('tool_bar')
            const gridSize = e.ctrlKey ? 1 : _.layouter.grid()

            const snap2grid = (block, side) => {
              const bound = block.bound()
              let offset = 0
              switch (side) {
                case 'left':
                case 'x':
                  offset = bound.x - (Math.round(bound.x / gridSize) * gridSize)
                  if (offset !== 0) {
                    bound.x -= offset
                    bound.width += offset
                  }
                  break
                case 'top':
                case 'y':
                  offset = bound.y - (Math.round(bound.y / gridSize) * gridSize)
                  if (offset !== 0) {
                    bound.y -= offset
                    bound.height += offset
                  }
                  break
                case 'right':
                case 'r':
                  offset = (Math.round((bound.x + bound.width) / gridSize) * gridSize) - (bound.x + bound.width)
                  if (offset !== 0) {
                    bound.width += offset
                  }
                  break
                case 'bottom':
                case 'b':
                  offset = (Math.round((bound.y + bound.height) / gridSize) * gridSize) - (bound.y + bound.height)
                  if (offset !== 0) {
                    bound.height += offset
                  }
                  break
              }
              if (offset) block.bound(bound)
            }
            let rate
            let origin
            let pos
            switch (status) {
              case 'move':
                _.$control.hide()
                _.layouter.state.status('tool_bar', 'move-dragged')
                for (let i = 0, l = blocks.length; i < l; i++) {
                  const p = { x: _.bound[i].x.px + x, y: _.bound[i].y.px + y }
                  blocks[i]
                    .ghost(true)
                    .pos(p.x - (p.x % gridSize), p.y - (p.y % gridSize))
                    .edit(false)
                }
                break
              case 'move-dragged':
                for (let i = 0, l = blocks.length; i < l; i++) {
                  const p = { x: _.bound[i].x.px + x, y: _.bound[i].y.px + y }
                  blocks[i]
                    .ghost(true)
                    .pos(p.x - (p.x % gridSize), p.y - (p.y % gridSize))
                    .edit(false)
                }
                break
              case 'lt-resize':
              case 'ct-resize':
              case 'rt-resize':
              case 'lm-resize':
              case 'cm-resize':
              case 'rm-resize':
              case 'lb-resize':
              case 'cb-resize':
              case 'rb-resize':
                _.layouter.state.status('tool_bar', status + '-dragged')
                pos = _.$control.position()
                _.ctrlBound = { x: pos.left, y: pos.top, width: _.$control.width(), height: _.$control.height() }
                break
              case 'lt-resize-dragged':
                rate = { x: (_.ctrlBound.width - x) / _.ctrlBound.width, y: (_.ctrlBound.height - y) / _.ctrlBound.height }
                origin = { x: _.ctrlBound.x + _.ctrlBound.width, y: _.ctrlBound.y + _.ctrlBound.height }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.x > 0 ? 'left' : 'right')
                  snap2grid(blocks[i], rate.y > 0 ? 'top' : 'bottom')
                };
                _showControlPoint(true)
                break
              case 'ct-resize-dragged':
                rate = { x: 1, y: (_.ctrlBound.height - y) / _.ctrlBound.height }
                origin = { x: _.ctrlBound.x + (_.ctrlBound.width / 2), y: _.ctrlBound.y + _.ctrlBound.height }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.y > 0 ? 'top' : 'bottom')
                };
                _showControlPoint(true)
                break
              case 'rt-resize-dragged':
                rate = { x: (_.ctrlBound.width + x) / _.ctrlBound.width, y: (_.ctrlBound.height - y) / _.ctrlBound.height }
                origin = { x: _.ctrlBound.x, y: _.ctrlBound.y + _.ctrlBound.height }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.x > 0 ? 'right' : 'left')
                  snap2grid(blocks[i], rate.y > 0 ? 'top' : 'bottom')
                };
                _showControlPoint(true)
                break
              case 'lm-resize-dragged':
                rate = { x: (_.ctrlBound.width - x) / _.ctrlBound.width, y: 1 }
                origin = { x: _.ctrlBound.x + _.ctrlBound.width, y: _.ctrlBound.y + (_.ctrlBound.height / 2) }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.x > 0 ? 'left' : 'right')
                };
                _showControlPoint(true)
                break
              case 'cm-resize-dragged':
              case 'rm-resize-dragged':
                rate = { x: (_.ctrlBound.width + x) / _.ctrlBound.width, y: 1 }
                origin = { x: _.ctrlBound.x, y: _.ctrlBound.y + (_.ctrlBound.height / 2) }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.x > 0 ? 'right' : 'left')
                };
                _showControlPoint(true)
                break
              case 'lb-resize-dragged':
                rate = { x: (_.ctrlBound.width - x) / _.ctrlBound.width, y: (_.ctrlBound.height + y) / _.ctrlBound.height }
                origin = { x: _.ctrlBound.x + _.ctrlBound.width, y: _.ctrlBound.y }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.x > 0 ? 'left' : 'right')
                  snap2grid(blocks[i], rate.y > 0 ? 'bottom' : 'top')
                };
                _showControlPoint(true)
                break
              case 'cb-resize-dragged':
                rate = { x: 1, y: (_.ctrlBound.height + y) / _.ctrlBound.height }
                origin = { x: _.ctrlBound.x + (_.ctrlBound.width / 2), y: _.ctrlBound.y }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.y > 0 ? 'bottom' : 'top')
                };
                _showControlPoint(true)
                break
              case 'rb-resize-dragged':
                rate = { x: (_.ctrlBound.width + x) / _.ctrlBound.width, y: (_.ctrlBound.height + y) / _.ctrlBound.height }
                origin = { x: _.ctrlBound.x, y: _.ctrlBound.y }
                for (let i = 0, l = blocks.length; i < l; i++) {
                  blocks[i]
                    .bound(_.bound[i])
                    .size((rate.x * 100) + '%', (rate.y * 100) + '%', origin.x, origin.y)
                  snap2grid(blocks[i], rate.x > 0 ? 'right' : 'left')
                  snap2grid(blocks[i], rate.y > 0 ? 'bottom' : 'top')
                };
                _showControlPoint(true)
                break
            }
            let resized = false
            let moved = false
            for (let i = 0; i < blocks.length; i++) {
              const bound = blocks[i].bound()
              const prevBound = _.prevBound || _.bound
              if (!moved) moved = (bound.x.px !== prevBound[i].x.px) || (bound.y.px !== prevBound[i].y.px)
              if (!resized) resized = (bound.width.px !== prevBound[i].width.px) || (bound.height.px !== prevBound[i].height.px)
              if (resized && moved) break
            }
            if (moved) _.layouter.triggerEvent(C`ADLEVENT_BLOCK_MOVE`)
            if (resized) _.layouter.triggerEvent(C`ADLEVENT_BLOCK_RESIZE`)
            if (_.prevBound === undefined) _.prevBound = []
            _.prevBound = blocks.bound()

            _.layouter.triggerEvent(C`ADLEVENT_DOCUMENT_MODIFIED`, _.layouter.document)
          } else {
            _.layouter.triggerEvent(C`ADLEVENT_CANVAS_MOUSEUP`)
          }
        }
      }
    })

    .registerEvent(C`ADLEVENT_CANVAS_MOUSEUP`, function (e) {
      if (_.layouter.state.tool() === 'select') {
        if (_.mouse.press) {
          _.layouter.state.select().ghost(false)
          _.mouse.press = 0
          switch (_.layouter.state.status('tool_bar')) {
            case 'move-dragged':
              _showControlPoint(true)
              break
          }
          _.layouter.state.status('tool_bar', '')
        }
      }
    })

    .registerEvent(C`ADLEVENT_CANVAS_MOUSEDOWN`, function (e) {
      if (_.layouter.state.tool() === 'select') {
        var allowClass = ['block', 'ctrlPoint']
        var allow = false
        var target = $(e.target)
        for (var i in allowClass) {
          allow |= target.hasClass(allowClass[i]) | (target.closest('.' + allowClass[i]).length > 0)
        }
        if (e.shiftKey || e.ctrlKey) {
        } else {
          if (!allow) {
            _.layouter.block().deselect()
          }
        }
      }
    })

    .registerEvent(C`ADLEVENT_BLOCK_CONTROL_POINT_MOUSEDOWN`, function (e) {
      if (_.layouter.state.tool() === 'select') {
        var ctrlPoint = e.target.classList[1]
        _.layouter.state.status('tool_bar', ctrlPoint + '-resize')
        var scrollTop = document.getElementById('layoutWrapper').scrollTop
        var scrollLeft = document.getElementById('layoutWrapper').scrollLeft
        _.mouse.x = _.mouse.startX = e.pageX + scrollLeft
        _.mouse.y = _.mouse.startY = e.pageY + scrollTop
        _.mouse.press = e.buttons
        _.bound = _.layouter.state.select().bound()
      }
    })

    .registerEvent(C`ADLEVENT_BLOCK_SELECT_CHANGED`, function (e) {
      _.layouter.block().edit(false)
      _showControlPoint()
    })

    .registerEvent(C`ADLEVENT_DOCUMENT_MODIFIED`, function (e) {
      // _showControlPoint() ;
    })

    .registerEvent(C`ADLEVENT_TOOLBAR_BUTTON_CLICK`, function (e, tool) {
      _.layouter.state.tool(tool)
      $(e.currentTarget).addClass('selected')
    })

    .registerEvent(C`ADLEVENT_TOOLBAR_CONTROL_POINT_SHOW`, function (immediately) {
      _showControlPoint(immediately)
    })

    .registerEvent(C`ADLEVENT_BLOCK_PASTE`, function (e) {
    })

  _.$this
    .on('click', 'button#toolbar-select', function (e) {
      _.layouter.triggerEvent(C`ADLEVENT_TOOLBAR_BUTTON_CLICK`, [e, 'select'])
    })

  $(document)
    .on('mousedown', '.ctrlPoint', function (e) {
      _.layouter.triggerEvent(C`ADLEVENT_BLOCK_CONTROL_POINT_MOUSEDOWN`, arguments)
    })
    .on('mouseup', '.ctrlPoint', function (e) {
      _.layouter.triggerEvent(C`ADLEVENT_BLOCK_CONTROL_POINT_MOUSEUP`, arguments)
    })

  _.$this.find('button#toolbar-select').click()
}

ToolBar.prototype.dockTo = function (position) {
  const _ = _properties.get(this) || {}
  const _layouter = _.layouter
  const _$this = _.$this
  if (['top', 'right', 'bottom', 'left', 'center'].includes(position) && _layouter && _$this) _layouter.append(position, _$this)
  return this
}

export default ToolBar
