

Tabs  = window.require 'spine/controller/tabs'
Panel = require './panel'
Skin  = window.require 'core/models/skin'

Activator	 = window.require 'spine/controller/activator'
Activateable = window.require 'spine/controller/activateable'
Controller   = window.require 'spine/controller'

{ createPopup } = require 'Core/utils/popup'
shouldShow = require 'Core/utils/should-show'

###
Recursively find a visible spot for a SkinTab
###
makeVisible = (tab, els, i) ->
	return if i is -1
	els[i].before tab.el
	return if tab.isVisible()
	makeVisible(tab, els, i - 1)

###
The tab class used to render a skin tab.
###
class SkinTab extends Controller
	@include Activateable
	events:
		'click':'active'

	isVisible: => @el.position().top == 0

class SkinTabs extends Activator
	containerClass  : 'skin-editor-tabs-list'
	events:
		'click .multi-tabs-toggle':'toggleItem'
		'click .activateable':'hideList'

	hideList:(e)=>
		@popup?.hide()
		controller = $(e.target).controller()
		unless controller.el.hasClass('hidden')
		 	@list.children().removeClass('active')
		@list.children().each (i,el)=>
			$el = $(el)
			if $el.data('tab') is controller
				$el.addClass('active').siblings().removeClass('active')

	toggleItem:(e) =>
		@_createPopup().then => if @popup.visible then @popup.hide() else @popup.show()

	constructor: ->
		super
		@tabsByName = {}

		@listenTo $(document), 'click', (e) => @popup?.hide() unless $(e.target).is('.multi-tabs-toggle')
		@el.prepend @toggle = $("<div class='multi-tabs-toggle' data-icon='❯'></div>")
		@list = $("<div class='multi-tabs-list'></div>")
		@list.on('click', '.multi-tab-item', @tabClick)

	clear: =>
		@list.empty()
		@tabsByName = {}
		@children().forEach (child) => @stopListening(child)
		@toggle.hide()

	afterAdd:(child)=>
		super
		@listenTo child, 'activate', @_onChildActivate

		@list.append item = $("<div class='multi-tab-item'>#{child.panel.tabTitle}</div>")

		@tabsByName[child.panel.tabTitle] = child
		item.data 'tab', child
		child.el.data 'multi-tab-list-item', item

	tabClick:(e)=>
		@popup?.hide()
		target = $(e.target)

		target.data('tab').active()

	update: (tabsMeta, skin) ->
		# find out which tabs are empty (i.e. all attributes are hidden because of if conditions)
		emptyTabs = {}
		for tab, attributes of tabsMeta
			emptyTabs[tab] = true
			for attribute, settings of attributes
				if shouldShow(settings.if, skin)
					emptyTabs[tab] = false
					break

		# hide the empty tabs
		for tab, empty of emptyTabs
			@tabsByName[tab].el.toggleClass('empty', empty)
			@tabsByName[tab].el.data('multi-tab-list-item')?.toggleClass('empty', empty)

		# if the active tab is now empty, activate the first non-empty tab
		@children (i) -> i.el.is('.active.empty')
			.map => @children (i) -> i.el.is(':not(.empty)')
			.forEach (notEmptyTabs) -> notEmptyTabs[0].active()

		@_updateVisibleTabs()

		true

	_onChildActivate: (tab, event) =>
		# NOTE: activateable behaviour already toggles the `active` class, but the
		# event is processed in the order of the child elements, so if you toggle
		# the tabs from right to left, the `active` class will be present on both
		# the current and the previous active tab. This code ensures that the DOM is
		# in the final state and the position calculations are correct.
		@children().forEach (child) -> child.$removeClass('active')
		tab.$addClass('active')

		# if the active tab is not visible, find a place to move it to
		if !tab.isVisible()
			makeVisible(tab, @children().map((i) -> i.el), 2)

		@_updateVisibleTabs()

	_updateVisibleTabs: ->
		# compute the visible state
		tabs = @children()
			.map (tab) -> {tab, visible: tab.isVisible()}

		# update the extra tabs dropdown
		tabs.forEach (i) ->
			i.tab.el.data 'multi-tab-list-item'
				.toggle !i.visible

		# show the extra tabs button if needed
		allVisible = tabs.map (i) -> i.visible
			.reduce (acc, i) -> acc && i
		@toggle.toggle(!allVisible)
		@popup?.hide() if allVisible

	_createPopup: ->
		return @popupPromise if @popupPromise
		@popupPromise = createPopup
				topOffset: 25
				leftOffset: 23
				anchor: @toggle
			.then (@popup) =>
				@popup.append(@list)
			.catch (err) ->
				console.error err.stack

class SkinEditor extends Tabs
	tabRenderer 		: SkinTab
	className			: 'skin-editor'
	tabContainerClass	: 'skin-editor-tabs'
	tabContainer 		: SkinTabs
	containerClass 		: 'skin-editor-container'

	tabTemplate			: (i)->i.panel.tabTitle

	setAccessLevel: (@accessLevel) ->

	parseMeta: (@meta, composition) =>
		child.release() for child in @children()
		@tabs.clear()

		for tabTitle, meta of @meta.tabs
			@append new Panel({meta, tabTitle, composition, @accessLevel})

		@tabs.update @meta.tabs, composition.getSkin()

	display: (index = 0) ->
		@children()[index]?.active()

	# check if the skin has missing atts, and update it
	updateSkin: (skin) =>
		atts = false
		for _, meta of @meta.tabs
			(atts or= {})[attrName] = attrData.options.default for attrName, attrData of meta when !skin[attrName]?

		skin.updateAttributes atts, {from: @, ajax: false} if atts

	setSkin: (skin) ->
		@updateSkin skin
		@children (panel) ->
			panel.skinLoad(skin)

	skinChanged: (skin) ->
		@tabs.update @meta.tabs, skin

module.exports = SkinEditor
