

Tabs    = window.require 'spine/controller/tabs'

ContentModel = window.require 'core/models/content'
SkinModel    = window.require 'core/models/skin'
Composition  = window.require 'core/models/composition'
WidgetModel  = window.require 'core/models/widget'

Skin    = require './skin'
Content = require './content'
Details = require './details'
Save   	= require './save'
Footer  = require './footer'

timestamp = window.require 'spine/utils/timestamp'

LoginPopup = require './login-popup'

class WidgetEditor extends Tabs
	tabContainer 		: require './footer'
	tabRenderer			: require './footer/breadcrumb'
	className	 		: 'editor'
	tabContainerClass 	: 'editor-footer'
	containerClass 		: 'editor-container'

	###
	DOM Events and their handlers
	###
	events:
		'viewChange':                    '_onViewChange'
		'Footer:step':                   '_onStep'
		'Breadcrumb:step':               '_onStep'
		'Save:close':                    '_onSaveClose'
		'DetailsEditor:empty':           '_onDetailsEditorEmpty'
		'editor:content:notification':   '_onContentNotification'
		'editor:content:browserState':   '_onContentBrowserState'

	###
	A list of Controllers that should be created in this TabsController
	###
	controllers:
		'skin':    Skin
		'content': Content
		'details': Details
		'save':    Save

	###
	An object containing associations between the step name and the step
	controller reference
	###
	steps:
		'skin':     'skin'
		'edit_skin':'skin'
		'theme':    'skin'
		'content':  'content'
		'details':  'details'
		'done':     'save'

	###
	Collection of validation functions for displaying editors

	Each function is ran before trying to activate an editor.
	###
	stepValidators:
		'skin': ->
			return console.warn 'No composition set' unless @composition
			return true
		'edit_skin': -> @stepValidators['skin'].call(@) and !@canEditSkin? or @canEditSkin
		'theme': -> @stepValidators['skin'].call(@)
		'content': ->
			return false unless @stepValidators['skin'].call @
			if @hasPendingChange()
				@initSave()
				@step 'skin'
				return false
			return true
		'details': ->
			return false unless @stepValidators.content.call @
			unless @composition.hasEnoughContent()
				@content.notificator.notify @composition.validateLength().errorMsg
				@step 'content'
				return false
			return true
		'done': ->
			return false unless @stepValidators['details'].call @
			return true

	###
	Creates the embedded Editors, Navigation and binds events
	###
	constructor: ->
		timestamp 'WidgetEditor:constructor'
		super
		@children (child) => child.setPremium? @premium
		@refreshElements()
		@el.append @tabs.el
		LoginPopup.place @el
		Blogvio.pubsub.subscribe 'api/token/update', @_updateLoggedInStatus

		@el.attr('editorVersion', VERSION)

	###
	Initializes all embbedded Editors to work on a composition
	Starts waiting for all of the `initialized` events
	Navigates to the 'skin' step
	###
	for: (composition) ->
		timestamp 'WidgetEditor:for'
		@initialize(composition)
		@skin.for(@composition)
		@content.for(@composition)
		@details.for(@composition)
		@save.for(@composition)
		@

	initialize: (composition) ->
		timestamp 'WidgetEditor:initialize'
		@stopListening(@composition)
		@composition = composition

		@_waitForInit()

		@_updatePersistedStatus()
		@_updateLoggedInStatus()

		@listenTo @composition, 'validateLength', @_onValidateLength
		@composition.validateLength()

		$(window).on 'beforeunload', @onBeforeUnload

	###
	Tests if there are any pending (unsaved) changes in the editors
	###
	hasPendingChange: -> @skin.hasPendingChange()

	###
	Initializes the save dialog when there is a pending change
	###
	initSave: -> @skin.initSave()

	###
	Jump to a step (one of the editors)
	###
	step: (step) ->
		return console.warn "The editor does not have the #{step} step." unless @steps[step]
		stepController = @[@steps[step]]
		stepController.active step if @stepValidators[step].call @
		@_updatePersistedStatus()

	selectSkin: (skin, force = true) -> @skin.select(skin, force)

	###
	Event Handler for step navigation events
	###
	_onStep: (ev, step) ->
		@step(step)
		@$trigger 'step:intent', {step, skin_pending: @hasPendingChange(), content: @composition.hasEnoughContent()}

	###
	Event Handler for the Composition `validateLength` event

	Updates the `minValid` and `maxValid` attributes on the editor DOM element
	Used for styling based on the validation state
	###
	_onValidateLength: (_, valid, error) =>
		return unless @composition
		@el.attr minValid: valid or error isnt 'min', maxValid: valid or error isnt 'max'

	_updatePersistedStatus: =>
		@el.attr 'persisted-comp', @composition.isPersisted()

	_updateLoggedInStatus: =>
		# timestamp 'WidgetEditor:_updateLoggedInStatus'

		status = Blogvio.auth.status()
		isConnected = status.status is 'connected'

		@el.toggleClass('anonymous-user', !isConnected)

	###
	Event Handler for the `viewChange` events launched by the editors

	Updates the `current-view` attribute on the editor DOM element
	Used for styling based on the current view
	###
	_onViewChange: (ev, view) =>
		@el.attr 'current-view', view

	###
	Binds event listeners for the Editors' `initialized` event
	###
	_waitForInit: =>
		@initCount = 0
		@listenToOnce @skin,    'initialized', @_onInitialized
		@listenToOnce @content, 'initialized', @_onInitialized
		@listenToOnce @details, 'initialized', @_onInitialized

	###
	Event Handler for the Editors `initialized` event

	Triggers the WidgetEditor `initialized` event when all of
	the embbedded editors have finised initalization.
	TODO: check if this still works
	###
	_onInitialized: =>
		return if ++@initCount isnt 3

		@trigger 'initialized'

	###
	Event Handler for the `editor:content:notification` event
	###
	_onContentNotification: (ev, options) ->
		@el.attr 'notification-visible', options.show

	###
	Event Handler for the `editor:content:browserState` event
	###
	_onContentBrowserState: (ev, options) ->
		@el.attr 'content-browser-service', options.service
		@el.attr 'content-browser-auth', options.authenticated

	###
	Event Handler for the `DetailsEditor:empty` event

	Navigate to the content editor when there is no content to
	display in the details editor
	###
	_onDetailsEditorEmpty: -> @step 'content'

	###
	Event Handler for the `Save:close` event

	Navigate to the details editor when the save editor's login
	popup is closed
	###
	_onSaveClose: -> @step 'details'

	setOptions: do ->
		_methods = {
			'skin_editor', 'step', 'navigation'
			'skin':'selectSkin'
			'composition':'setCompositionName'
			'sources': 'addContentSources'
			'active_source': 'activateSource'
		}
		(opts = {}) -> @[_fn]? params for prop, _fn of _methods when params = opts[prop]

	navigation: (opts) =>
		@el.attr 'data-steps', opts.steps.join ' ' if opts.steps?
		@tabs.setOptions arguments...

	skin_editor: (opts = {}) =>
		@canEditSkin = opts.enabled if opts.enabled?
		@skin.setOptions opts

	addContentSources: (sources = {}) ->
		@content.addBrowser source, sourceName for sourceName, source of sources

	setCompositionName: (opts = {}) =>
		@composition.name = opts.name if opts.name?

	activateSource: (source) =>
		@content.focusBrowser source

	release: ->
		@_release @
		super

	onBeforeUnload: =>
		return 'There are some unsaved changes!' if @hasPendingChange()

module.exports = WidgetEditor
