import { makeError, guid } from '../common.js'
import { constants as C } from '../constants'
import { locale as L } from '../lang/zh_TW'
import Page from './ADPage.js'
import Unit from './ADUnit.js'
import $ from 'jquery'

import './ADBlock.css'

const _properties = new WeakMap()

/**
 * @memberof AD.Layout.Types
 * @class Block
 * @constructor
 * @param property {Object}
 * @param [target] {JQuery|HTMLElement}
.**/
function Block (property, target) {
  /**
...* AD.Layout.Types.Block實體的jQuery
...* @private { jQuery }
...**/

  var _$this = (function (p) {
    for (var k = 0, l = p.length; k < l; k++) {
      if (p[k] instanceof $) return p[k]
      if (p[k] instanceof window.HTMLElement) return $(p[k])
    }
  })([target, property, property && property.target])

  if (_$this && _$this.length === 0) {
    return null
  }
  if (_$this && _$this.length > 0) {
    if (_$this[0].adlblock && _$this[0].adlblock instanceof Block) {
      return _$this[0].adlblock
    } else {
      if (!(this instanceof Block)) {
        return new Block(property, _$this)
      } else {
        if (_$this.prop('tagName') === 'DIV') {
          if (!_$this.hasClass('block')) {
            _$this.addClass('block')
          }
          if (!_$this[0].id) {
            _$this[0].id = 'block' + guid()
          }
        } else {
          return null
        }
      }
    }
  } else if (!_$this) {
    if (!(this instanceof Block)) {
      return new Block(property)
    }
    _$this = $('<div class="block" id="block' + guid() + '">')
  }

  _properties.set(this || _$this[0].adlblock, {
    $this: _$this
  })

  _$this[0].adlblock = this
  this.update(property)
}

var prototype = Block.prototype
prototype.update = function update (property) {
  const _ = _properties.get(this) || {}
  const _$this = _.$this
  property = {
    height: 100,
    width: 100,
    left: 0,
    top: 0,
    ...property
  }
  _.vertical = _$this.hasClass('text_vertical') ? 'vertical' : 'horizontal'
  if ($.isPlainObject(property)) {
    for (const attribute in property) {
      switch (attribute) {
        case 'id':
          _$this.attr('id', property.id)
          break
        case 'left':
        case 'x':
          _$this.css('left', Unit(property[attribute]).PX)
          break
        case 'top':
        case 'y':
          _$this.css('top', Unit(property[attribute]).PX)
          break
        case 'right':
        case 'bottom':
          _$this.css(attribute, Unit(property[attribute]).PX)
          break
        case 'width':
        case 'w':
          _$this.width(property[attribute])
          break
        case 'height':
        case 'h':
          _$this.height(property[attribute])
          break
        case 'bound':
          if (property.x && property.y && property.w && property.h) {
            _$this.css('left', property.x)
            _$this.css('top', property.y)
            _$this.width(property.w)
            _$this.height(property.h)
          } else if (property.bound.length >= 4) {
            _$this.css('left', property.bound[0])
            _$this.css('top', property.bound[1])
            _$this.width(property.bound[2])
            _$this.height(property.bound[3])
          }
          break
        case 'backgroundColor':
          _$this.css('background-color', property.backgroundColor)
          break
        case 'borderWidths':
          switch ($.type(property.borderWidths)) {
            case 'array':
              _$this.css('border-width', property.borderWidths.join(' '))
              break
            case 'sting':
            case 'number':
              _$this.css('border-width', property.borderWidths)
              break
          }
          break
        case 'fontSize':
          _$this.css('font-size', property.fontSize)
          break
        case 'content':
          if (property.content instanceof Array) {
            for (var i = 0; i < property.content.length; i++) {
              var node = property.content[i]
              var span = $('<span>').text(node.text)
              node.style && span[0].setAttribute('style', node.style)
              _$this.append(span)
            }
          } else if (typeof property.content === 'string') {
            _$this.append($('<span>').text(property.content))
          }
          break
        case 'direction':
          _.vertical = property.direction === 'vertical'
          break
      }
    }
  }
}

prototype.editor = function editor () {
  const _ = _properties.get(this) || {}
  const _editor = _.editor
  if (_editor !== undefined) return _editor
  const layout = _.$this.closest('.adlayout')
  if (layout.length && layout[0].adl) {
    _.editor = layout[0].adl
    return layout[0].adl
  } else {
    return null
  }
}

prototype.page = function page () {
  const _ = _properties.get(this) || {}
  return Page(_.$this.closest('.page'))
}

prototype.id = function id (newId) {
  const _ = _properties.get(this) || {}
  if (newId === undefined) {
    return _.$this.attr('id')
  } else {
    _.$this.attr('id', newId)
    return this
  }
}

prototype.appendTo = function appendTo (target) {
  const _ = _properties.get(this) || {}
  if (target instanceof $) target.append(_.$this)
  return this
}

prototype.after = function after (val) {
  const _ = _properties.get(this) || {}
  _.$this.after(val)
}

prototype.remove = function remove () {
  const _ = _properties.get(this) || {}
  this.deselect()
  _.$this.remove()
  return this
}

prototype.edit = function edit (toggle) {
  const _ = _properties.get(this) || {}
  var _$this = _.$this
  if (arguments.length) {
    _$this.prop('contenteditable', toggle).toggleClass('edit', toggle)
    toggle && _$this.focus()
  } else {
    return _$this.prop('contenteditable') === 'true'
  }
}

prototype.ghost = function ghost (toggle) {
  const _ = _properties.get(this) || {}
  if (arguments.length === 0) return _.$this.hasClass('ghost')
  _.$this.toggleClass('ghost', (toggle !== false))
  return this
}

prototype.select = function select (toggle) {
  const _ = _properties.get(this) || {}
  if (arguments.length === 0) return _.$this.hasClass('selected')
  if (toggle !== this.select()) {
    _.$this.toggleClass('selected', toggle)
    this.editor().triggerEvent(C`ADLEVENT_BLOCK_SELECT_CHANGED`)
  }
  return this
}

prototype.deselect = function deselect () {
  this.select(false)
}

prototype.pos = function pos (x, y) {
  const _ = _properties.get(this) || {}
  var _$this = _.$this
  // var p = _$this.position() ;
  var p = { left: _$this[0].style.left, top: _$this[0].style.top }
  if (arguments.length === 0) {
    return { x: Unit(p.left), y: Unit(p.top) }
  } else if (arguments.length === 1) {
    y = x
  }
  if (x instanceof Unit) {
    _$this[0].style.left = x.PX
  } else if (typeof x === 'number') {
    _$this[0].style.left = x + 'px'
  } else if (typeof x === 'string') {
    const offset = (x.charAt(0) === '+' || x.charAt(0) === '-') ? p.left : 0
    _$this[0].style.left = (offset + Unit(x).px) + 'px'
  } else {
    makeError('ERR_BAD_PARAMETERS', L`_STR_ERR_BAD_PARAMETERS`)
    return false
  }
  if (y instanceof Unit) {
    _$this[0].style.top = y.PX
  } else if (typeof y === 'number') {
    _$this[0].style.top = y + 'px'
  } else if (typeof y === 'string') {
    const offset = (y.charAt(0) === '+' || y.charAt(0) === '-') ? p.top : 0
    _$this[0].style.top = (offset + Unit(y).px) + 'px'
  } else {
    makeError('ERR_BAD_PARAMETERS', L`_STR_ERR_BAD_PARAMETERS`)
    return false
  }
  return this
}

prototype.size = function size (a, b, c, d) {
  var w = this.width().px
  var h = this.height().px
  var originX, originY

  function _translate (val, ref) {
    if (val instanceof Unit) {
      return val.px
    } else if (typeof val === 'string') {
      var offset = false
      if (val.charAt(0) === '+') {
        val = val.substr(1)
        offset = true
      }
      if (/^-?\d+\.?\d*$/.test(val)) {
        return parseFloat(val) + (offset ? ref : 0)
      } else if (/^-?\d+\.?\d*%$/.test(val)) {
        return ref * ((parseFloat(val) / 100) + (offset ? 1 : 0))
      } else if (/^-?\d+\.?\d*\w+$/.test(val)) {
        return Unit(val).px + (offset ? ref : 0)
      }
    } else if (typeof val === 'number') {
      return val
    } else {
      return false
    }
  }

  switch (arguments.length) {
    case 0:
      return { width: this.width(), height: this.height() }
    case 1:
      if (a.width !== undefined && a.height !== undefined) {
        return this.size(a.width, a.height)
      } else if (/^[a-z]+$/i.test(a)) {
        return { width: Unit(w).to(a), height: Unit(h).to(a) }
      } else {
        return this.size(a, a)
      }
    case 2:
      if (a.width !== undefined && a.height !== undefined && b.x !== undefined && b.y !== undefined) {
        return this.size(a.width, a.height, b.x, b.y)
      } else if (b.x !== undefined && b.y !== undefined) {
        return this.size(a, a, b.x, b.y)
      } else {
        w = _translate(a, w)
        h = _translate(b, h)
      }
      break
    case 3:
      if (c.x !== undefined && c.y !== undefined) {
        w = _translate(a, w)
        h = _translate(b, h)
        originX = c.x
        originY = c.y
      } else {
        w = _translate(a, w)
        h = _translate(a, h)
        originX = b
        originY = c
      }
      break
    case 4:
      w = _translate(a, w)
      h = _translate(b, h)
      originX = c
      originY = d
  }

  if (originX !== undefined && originY !== undefined) {
    var scaleX = w / this.width().px
    var scaleY = h / this.height().px
    var pos = this.pos()
    var newX = originX - ((originX - pos.x.px) * scaleX)
    var newY = originY - ((originY - pos.y.px) * scaleY)
    if (scaleX < 0) {
      newX -= Math.abs(w)
    }
    if (scaleY < 0) {
      newY -= Math.abs(h)
    }
    this.pos(newX, newY)
  }
  this.width(Math.abs(w))
  this.height(Math.abs(h))
  return this
}

prototype.bound = function bound (a, b, c, d) {
  switch (arguments.length) {
    case 0:
      var p = this.pos()
      var s = this.size()
      return { x: p.x, y: p.y, width: s.width, height: s.height }
    case 1:
      return this.pos(a.x, a.y) && this.size(a.width, a.height) && this
    case 2:
      return this.pos(a.x, a.y) && this.size(b.width, b.height) && this
    case 4:
      return this.pos(a, b) && this.size(c, d) && this
    default:
      makeError('ERR_BAD_PARAMETERS', L`_STR_ERR_BAD_PARAMETERS`)
      return false
  }
}

prototype.width = function width (val) {
  const _ = _properties.get(this) || {}
  if (arguments.length === 0) {
    return Unit(_.$this.width())
  } else {
    return _.$this.width(val)
  }
}

prototype.height = function height (val) {
  const _ = _properties.get(this) || {}
  if (arguments.length === 0) {
    return Unit(_.$this.height())
  } else {
    return _.$this.height(val)
  }
}

prototype.rebuild = function rebuild () {
  const _ = _properties.get(this) || {}
  var $this = _.$this
  var nodes = $this[0].childNodes
  function unwrap (offset) {
    offset = offset || 0
    var node = nodes[offset]
    var childNodes = node.childNodes
    if (childNodes.length > 0) {
      var range = document.createRange()
      range.setStartBefore(node)
      for (var i = 0; i < childNodes.length; i++) {
        var nextChildNode = childNodes[i]
        if (nextChildNode.nodeType !== 3) break
      }
      range.setEndBefore(nextChildNode)
      var fragment = range.extractContents()
      range.insertNode(fragment)
    }
    if (nodes.length - 1 > offset) unwrap(offset + 1)
  }
  unwrap(0)
}

prototype.direction = function direction (val) {
  const _ = _properties.get(this) || {}
  if (arguments.length === 0) return _('vertical')
  switch (val) {
    case 'vertical':
      _.$this.addClass('text_vertical')
      _.vertical = val
      break
    case 'horizontal':
      _.$this.removeClass('text_vertical')
      _.vertical = val
      break
  }
}

prototype.toJSON = function toJSON (stringify) {
  const _ = _properties.get(this) || {}
  var obj = {}
  var _$this = _.$this[0]
  var _style = _$this.style
  obj.id = this.id()
  obj.x = _style.left
  obj.y = _style.top
  obj.w = _style.width
  obj.h = _style.height
  obj.backgroundColor = _style['background-color']
  obj.borderWidths = _style['border-width']
  obj.fontSize = _style['font-size']
  obj.content = (function (nodes) {
    var res = []; var len = nodes.length
    for (let c = 0; c < len; c++) {
      var node = nodes[c]
      res.push({
        text: node.innerText,
        style: node.style.cssText
      })
    }
    return res
  })(_$this.children)
  obj.direction = _('vertical')
  return stringify === false ? obj : JSON.stringify(obj)
}

export default Block
