###
 * coffeescript-ui - Coffeescript User Interface System (CUI)
 * Copyright (c) 2013 - 2016 Programmfabrik GmbH
 * MIT Licence
 * https://github.com/programmfabrik/coffeescript-ui, http://www.coffeescript-ui.org
###

class CUI.Droppable extends CUI.DragDropSelect
	@cls = "droppable"

	initOpts: ->
		super()
		@addOpts
			accept:
				check: Function

			drop:
				default: (ev, info) =>
					pos = info.dropTargetPos or "on"
					CUI.alert(
						markdown: true,
						text: "You dropped me **"+pos+"**: " + CUI.dom.getAttribute(info.dropTarget, "class")
					)
				check: Function

			hoverClass:
				default: "cui-droppable"
				check: String

			dropHelper:
				mandatory: true
				default: false
				check: Boolean

			targetHelper:
				mandatory: true
				default: false
				check: Boolean

			selector:
				check: (v) =>
					CUI.util.isString(v) or CUI.util.isFunction(v)

	accept: (ev, info) ->
		@_accept?(ev, info)

	destroy: ->
		@removeHelper()
		super()

	readOpts: ->
		super()
		if @_targetHelper
			CUI.util.assert(@_selector, "new CUI.Droppable", "opts.targetHelper needs opts.selector to be set.", opts: @opts)

		if @_dropHelper
			CUI.util.assert(not @_selector or @_targetHelper, "new CUI.Droppable", "opts.dropHelper does only work without opts.selector or with opts.targetHelper and opts.selector. needs opts.selector to be set.", opts: @opts)
			@__dropHelper = CUI.dom.element("DIV", class: "cui-droppable-drop-helper")

		return

	removeHelper: ->
		@resetMargin()
		if @__selectedTarget
			CUI.dom.removeClass(@__selectedTarget, @_hoverClass)
			@__selectedTarget = null

		if @__dropHelper
			CUI.dom.remove(@__dropHelper)

		if @_targetHelper
			for el in CUI.dom.findElements(@_element, @_selector)
				el.classList.remove("cui-droppable-target-helper")


		@__dropTarget = undefined
		@__dropTargetPos = undefined

	resetMargin: ->
		if not @__resetMargin
			return

		@__resetMargin.classList.remove(@__resetMargin.__target_helper_class)
		delete(@__resetMargin.__target_helper_class)
		delete(@__resetMargin)
		delete(@__saveZoneDims)

	insideSaveZone: (coord) ->
		if not @__saveZoneDims
			return false

		buf = 5 # add extra pixels
		for zone in @__saveZoneDims
			if (zone.viewportTopMargin - buf) <= coord.pageY <= (zone.viewportBottomMargin + buf) and
				(zone.viewportLeftMargin - buf) <= coord.pageX <= (zone.viewportRightMargin + buf)
					return true

		return false

	syncDropHelper: ->
		# drop helper goes on top
		dim = CUI.dom.getDimensions(@_element)

		document.body.appendChild(@__dropHelper)

		CUI.dom.setDimensions @__dropHelper,
			contentBoxWidth: dim.borderBoxWidth
			contentBoxHeight: dim.borderBoxHeight

		drop_helper_dim = CUI.dom.getDimensions(@__dropHelper)

		CUI.dom.setStyle @__dropHelper,
			position: "absolute"
			top: dim.viewportTop - drop_helper_dim.borderTopWidth - drop_helper_dim.marginTop
			left: dim.viewportLeft - drop_helper_dim.borderLeftWidth - drop_helper_dim.marginLeft


	syncTargetHelper: (ev, info) ->
		target = ev.getTarget()
		coord = CUI.util.getCoordinatesFromEvent(info.originalEvent)

		if ev.getType() == "cui-dragleave"
			new_target = info.originalEvent.getTarget()
			if CUI.dom.closest(new_target, ".cui-drag-drop-select-droppable") != @_element
				# outside us
				@removeHelper()
				return true

			if @_targetHelper or not @_selector
				# ignore the event
				return

		acceptable = (dropTarget, dropTargetPos) =>
			info.dropTarget = dropTarget
			if @_targetHelper
				info.dropTargetPos = dropTargetPos

			if @accept(ev, info) == false
				@removeHelper()
				return false
			else
				return true

		if not @__initialized
			# check axis
			last_dim = null
			@__axis = undefined

			for el in CUI.dom.findElements(@_element, @_selector)
				if @_targetHelper
					el.classList.add("cui-droppable-target-helper")

				# we only need to check the first two elements
				# if we only have one we cannot determine the "axis" anyway
				if @__axis != undefined
					continue

				dim = CUI.dom.getDimensions(el)
				if last_dim

					@__axis = null
					if last_dim.viewportLeft == dim.viewportLeft
						@__axis = "y"

					if last_dim.viewportTop == dim.viewportTop
						@__axis = "x"

				last_dim = dim

			if not @__axis
				@__axis = "x"

			@__dropTargetPos = null
			@__dropTarget = null

			@__initialized = true

		CUI.dom.removeClass(@__selectedTarget, @_hoverClass)
		if @_selector
			@__selectedTarget = CUI.dom.closest(target, @_selector)
		else
			@__selectedTarget = @_element

		if not @_targetHelper

			if not acceptable(@__selectedTarget)
				@removeHelper()
				if @_selector and not @__selectedTarget
					# bubble
					return true
				return

			@__dropTarget = @__selectedTarget
			@__dropTargetPos = null

			CUI.dom.addClass(@__selectedTarget, @_hoverClass)
			if @__dropHelper
				@syncDropHelper()

			return

		if not @__selectedTarget
			if @insideSaveZone(coord)
				console.info("Inside save zone...")
				return

			@resetMargin()
			if @__dropHelper
				if not acceptable(@_element)
					return

				@__dropTarget = @_element
				@__dropTargetPos = null
				@syncDropHelper()
			else
				console.info("No selected target, no dropHelper...")
				@removeHelper()
				# bubble
				return true
			return

		CUI.dom.remove(@__dropHelper)

		dim = CUI.dom.getDimensions(@__selectedTarget)

		if (@__axis == "x" and coord.pageX > dim.viewportCenterLeft) or
			(@__axis == "y" and coord.pageY > dim.viewportCenterTop)
				dropTargetPos = "after"
		else
			dropTargetPos = "before"

		dropTarget = @__selectedTarget

		if not acceptable(dropTarget, dropTargetPos)
			@removeHelper()
			return

		@__dropTarget = dropTarget
		@__dropTargetPos = dropTargetPos

		helper_cls = "cui-droppable-target-helper-"+@__axis+"--"+@__dropTargetPos

		if @__resetMargin == @__selectedTarget and @__selectedTarget.__target_helper_class == helper_cls
			; # target helper is still ok
		else
			@resetMargin()
			@__saveZoneDims = [ CUI.dom.getDimensions(@__selectedTarget) ]
			@__selectedTarget.__target_helper_class = helper_cls
			CUI.dom.addClass(@__selectedTarget, @__selectedTarget.__target_helper_class)
			@__saveZoneDims.push(CUI.dom.getDimensions(@__selectedTarget))
			@__resetMargin = @__selectedTarget

		return

	init: ->
		CUI.Events.listen
			node: @element
			type: "cui-dragend"
			instance: @
			call: (ev, info) =>
				@removeHelper()

		CUI.Events.listen
			node: @element
			type: "cui-drop"
			instance: @
			call: (ev, info) =>
				if not @__dropTarget
					return
				info.dropTarget = @__dropTarget
				if @_targetHelper
					info.dropTargetPos = @__dropTargetPos

				if @accept(ev, info) != false
					ev.stopPropagation()
					CUI.setTimeout
						call: =>
							@_drop(ev, info)

				return

		CUI.Events.listen
			node: @element
			type: ["cui-dragover", "cui-dragenter", "cui-dragleave"]
			instance: @
			call: (ev, info) =>

				@syncTargetHelper(ev, info)
				ev.stopPropagation()
				return
