/**
 * @class IDU.cardoMap.BergPass.Web.Client.Controls.ApplicationForm.controller.Steps
 * @extends Ext.ux.app.control.Controller
 * Controller, der sich um die Navigation zwischen den Formularschritten kümmert.
 * @author Robin
 * @date 2014-03-11
 */
Ext4.define('IDU.cardoMap.BergPass.Web.Client.Controls.ApplicationForm.controller.Steps', {
	extend: 'Ext.ux.app.control.Controller',

	stores: [
		'Steps'
	],
	views: [
		'CardContainer'
	],

	refs: [{
		ref: 'mainContainer',
		selector: '#mainContainer'
	},{
		ref: 'cardContainer',
		selector: '#cardContainer'
	},{
		ref: 'stepGrid',
		selector: '#stepGrid'
	},{
		ref: 'prevStepButton',
		selector: '#buttonPrevStep'
	},{
		ref: 'finishButton',
		selector: '#buttonFinish'
	}],

	/**
	 * @inheritdoc
	 * @protected
	 */
	init: function()
	{
		this.listen({
			'component': {
				'#cardContainer': {
					// das passiert erst nach dem Hinzufügen, da noch nicht bekannt ist, dass es sich dabei um eine Application-Komponente handeld
					'add': this.onCardContainerAdd,
					'deactivate': this.onCardContainerDeactivate,
					'activate': this.onCardContainerActivate
				},
				'#cardContainer > component': {
					'activate': this.onCardContainerComponentActivate,
					'beforedeactivate': this.onCardContainerComponentBeforeDeactivate
				},
				'#stepGrid': {
					'render': this.onStepGridRender,
					'itemclick': this.onStepGridItemClick
				},
				'#buttonPrevStep': {
					'click': this.onButtonPrevStepClick
				},
				'#buttonNextStep': {
					'click': this.onButtonNextStepClick
				}
			}
		});
	},

	/**
	 * Wird aufgerufen, wenn eine "Seite" in den Card-Container eingefügt wird.
	 * @param {Ext.container.Container} contianer
	 * @param {Ext.Component} component
	 * @private
	 */
	onCardContainerAdd: function(container, component)
	{
		// Wir füllen hier unseren Store, da wir das Grid links für die Navigation
		// durch die einzelnen Seiten nutzen.
		this.getStore('Steps').add({
			title: component.title,
			cardId: component.getId()
		});
	},
	/**
	 * Initialisiert die Schritte. Das muss manuell gemacht werden, da das onAdd noch nicht greift.
	 * Das liegt daran, dass der Container noch nicht Teil des Anwendungs-Layouts ist.
	 * @private
	 */
	initSteps: function()
	{
		var container = this.getCardContainer(),
			store = this.getStore('Steps'),
			grid = this.getStepGrid(),
			items = container.items,
			active = container.getLayout().getActiveItem();

		items.each(function(component) {
			store.add({
				title: component.title,
				cardId: component.getId()
			});
		});
	},
	/**
	 * Wird aufgerufen, wenn die Bearbeitungsansicht aktiviert wurde.
	 * @private
	 */
	onCardContainerActivate: function()
	{
		var sm = this.getStepGrid().getSelectionModel(),
			finishButton = this.getFinishButton();

		finishButton.toggle(false, true);

		if (!this.lastSelectedStep)
			return;
		
		// letzten Step selektieren
		sm.setLocked(false);
		sm.select(this.lastSelectedStep, false, true);
		sm.setLocked(true);
		delete this.lastSelectedStep;

	},
	/**
	 * Wird aufgerufen, wenn die Bearbeitungsansicht verlassen
	 * (und die Prüfungs-Ansicht aktiviert) wurde.
	 * @private
	 */
	onCardContainerDeactivate: function()
	{
		// Wir merken uns den letzten Schritt und deselektieren alle Schritte.

		var sm = this.getStepGrid().getSelectionModel();

		this.lastSelectedStep = sm.getLastSelected();

		sm.setLocked(false);
		sm.deselectAll(true);
		sm.setLocked(true);
	},
	/**
	 * Wird nach dem Rendern des Schritte-Grids aufgerufen.
	 * @param {IDU.cardoMap.BergPass.Web.Client.Controls.ApplicationForm.view.StepGrid} grid
	 * @private
	 */
	onStepGridRender: function(grid)
	{
		// Wir nutzen das nur intern zum markieren, fangen aber das Klicken auf ein Item ab.
		// Wir fangen nicht das selectionchange-Event ab, da dann bereits der neue Schritt
		// selektiert ist. Das ist aber nicht immer korrekt, bspw. wenn man nach fehlerhafter
		// Validierung beim alten Schritt verbleibt.
		grid.getSelectionModel().setLocked(true);
	},
	/**
	 * Wird nach Änderung der Selektion im Schritte-Grid aufgerufen.
	 * @param {Ext.grid.View} view
	 * @param {IDU.cardoMap.BergPass.Web.Client.Controls.ApplicationForm.model.Step} record
	 * @param {HTMLElement} item
	 * @param {Number} index
	 * @private
	 */
	onStepGridItemClick: function(view, record, item, index)
	{
		var cardContainer = this.getCardContainer(),
			layout = cardContainer.getLayout(),
			activeCard = layout.getActiveItem(),
			cardId = record.get('cardId');

		// noch im Prüfungsmodus?
		if (this.lastSelectedStep)
		{
			delete this.lastSelectedStep;
			this.getMainContainer().getLayout().setActiveItem(cardContainer);
			this.skipValidation = true;
		}

		// activate kommt nicht, wenn schon aktiv ist, bspw. nach zurückspringen
		// von der "letzten Seite".
		if (activeCard && activeCard.getId() === cardId)
			this.selectStepByCard(activeCard);
		else
			layout.setActiveItem(cardId);
	},
	/**
	 * Wird vor dem Deaktivieren einer Card aufgerufen.
	 * @param {Ext.Component} currentCard
	 * @param {Ext.Component} newCard
	 * @private
	 */
	onCardContainerComponentBeforeDeactivate: function()
	{
		var _getEntityPath = function(cmp)
			{
				// nicht getEntityPath/getRootRecord, da es einfach auch nur als Properties bei speziellen Layout-Containern zugewiesen ist.
				var hasIndex = typeof cmp.entityPath.getIndex() === 'number',
					parent = hasIndex ? cmp.entityPath.copyWithIndex(null) : cmp.entityPath.getParent();
				return {
					record : cmp.rootRecord,
					o: cmp.entityPath,//.toString();
					asString: cmp.entityPath.toString(),
					parentString: parent ? parent.toString() : null
				};
			},
			_sortEntityPaths = function(a, b)
			{
				return a.asString < b.asString ? -1 : (a.asString > b.asString ? 1 : 0);
			};
		return function(currentCard, newCard)
		{
			// Schon abgeschickt, nur noch schauen.
			if (this.application.isFinished)
				return true;

			if (this.skipValidation)
			{
				delete this.skipValidation;
				return true;
			}

			// Validierung für die Daten der Card
			if (!this.isValidating)
			{
				this.isValidating = true;

				this.application.mask();

				var entityPaths = Ext4.Array.map(currentCard.query('[entityPath]'), _getEntityPath),
					i = 0,
					len = entityPaths.length,
					values = {},
					pathsProcessed = {},
					path;

				// Sortieren nach Pfad, das reicht schon. Uns geht es nur darum, die inneren Sachen nicht mitzuschicken,
				// da das dann doppelt wäre.
				entityPaths.sort(_sortEntityPaths);
				for (; i < len; i++)
				{
					path = entityPaths[i];
					if (!path.parentString || !pathsProcessed[path.parentString])
						values[path.asString] = path.record.getValuesByEntityPath(path.o, true);
					pathsProcessed[path.asString] = true;
				}

				IDU.cardoMap.BergPass.Web.Client.Controls.ApplicationForm.ApplicationFormControlRemote.AxValidateStep(
					this.application.puzzleCategoryId,
					values,
					Ext4.Function.bind(this.onValidateStep, this, [currentCard, newCard], 0));

				return false;
			}
		};
	}(),
	/**
	 * Callback für die Validierung.
	 * @param {Ext.Component} currentCard
	 * @param {Ext.Component} newCard
	 * @param {Object} result
	 * @private
	 */
	onValidateStep: function(currentCard, newCard, result)
	{
		this.application.unmask();

		// Exception beim Validieren
		if (result.error)
		{
			this.application.alertMessage(
				'Fehler bei der Validierung',
				result.error.Message,
				function() {
					this.isValidating = false;
					this.selectStepByCard(currentCard);
				},
				this);
			return;
		}

		this.application.updateValidation(result.value, currentCard);

		if (!result.value.isValid)
		{
			var messageBox = this.application.createMessageBox({
				title: 'Bitte beachten!',
				msg: 'Ihre Eingaben sind noch unvollständig oder fehlerhaft. Möchten Sie trotzdem fortfahren?',
				ui: 'bergpass-red',
				fbar: [{
					ui: 'bergpass-red',
					iconAlign: 'left',
					iconCls: 'idu-cardomap-bergpass-client-applicationform-icon-prev',
					text: 'Eingaben korrigieren',
					handler: function() {
						this.isValidating = false;
						this.selectStepByCard(currentCard);
						messageBox.close();
					},
					scope: this
				},'->',{
					ui: 'bergpass-green',
					iconAlign: 'right',
					iconCls: 'idu-cardomap-bergpass-client-applicationform-icon-next',
					text: 'Trotzdem weiter',
					handler: function() {
						this.getCardContainer().getLayout().setActiveItem(newCard);
						messageBox.close();
						this.isValidating = false;
					},
					scope: this
				}]
			});
		}
		else
		{
			this.getCardContainer().getLayout().setActiveItem(newCard);
			this.isValidating = false;
		}
	},
	/**
	 * Wird beim Aktivieren (Umschalten einer Card im CardContainer aufgerufen.
	 * @param {Ext.Component} component
	 * @private
	 */
	onCardContainerComponentActivate: function(component)
	{
		this.application.fireEvent('stepchange', component.title);
		this.selectStepByCard(component);
	},
	/**
	 * Selektiert einen Schritt im Grid anhand der Komponente.
	 * @param {Ext.Component} card
	 * @private
	 */
	selectStepByCard: function(card)
	{
		var store = this.getStore('Steps'),
			index = store.indexOfId(card.getId()),
			prevButton = this.getPrevStepButton(),
			sm = this.getStepGrid().getSelectionModel(),
			finishButton = this.getFinishButton();
		
		// aktiven Step selektieren
		sm.setLocked(false);
		sm.select(index, false, true);
		sm.setLocked(true);

		// Zurück-Button deaktivieren, wenn auf erster Seite
		prevButton.setDisabled(index === 0);

		finishButton.toggle(false, true);
	},
	/**
	 * Wird beim Klick auf den Button "Zurück" aufgerufen.
	 * @private
	 */
	onButtonPrevStepClick: function()
	{
		this.skipValidation = true;
		this.stepBy(-1);
	},
	/**
	 * Wird beim Klick auf den Button "Weiter" aufgerufen.
	 * @private
	 */
	onButtonNextStepClick: function()
	{
		if (!this.stepBy(1))
			this.getController('Finish').finishApplication();
	},
	/**
	 * Springt die angegebene Anzahl Schritte vor (positive Werte) oder zurück (negative Werte)
	 * @param {Number} steps Die Anzahl der Schritte
	 * @return {Boolean} Gibt an, ob weiter gesprungen wurde.
	 * @private
	 */
	stepBy: function(steps)
	{
		var store = this.getStore('Steps'),
			length = store.getCount(),
			layout = this.getCardContainer().getLayout(),
			active = layout.getActiveItem(),
			index = store.indexOfId(active.getId());

		index += steps;

		if (index >= 0 && index < length)
		{
			layout.setActiveItem(store.getAt(index).getId());
			return true;
		}

		return false;
	}
});