/*
 *  Copyright 2014 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * This is a relation handler between:
 * * source : explorer node or explorer resource
 * * destination : explorer node
 * 
 * E.g. when you drag an explorer file to an explorer folder
 * @private
 */
Ext.define('Ametys.plugins.explorer.relations.ExplorerResourcesRelationHandler', {
	extend: 'Ametys.relation.RelationHandler',
	
	/**
	 * @protected
	 * Do the #supportedRelations work but based only on targets array
	 * This method return the list of supported operations between the source and the target points of a relation.
	 * The implementation should only cares about targets and does not have to check upon source and target relations: a later filter is done by the Ametys.relation.RelationManager.
	 * @param {Object/Object[]/Ametys.message.MessageTarget/Ametys.message.MessageTarget[]} sourceTargets The source point of the relation operation. Targets are assumed to be ready.
	 * @param {Object/Object[]/Ametys.message.MessageTarget/Ametys.message.MessageTarget[]} targetTargets The end point of the relation operation. Targets are assumed to be ready.
	 * @return {Ametys.relation.Relation/Ametys.relation.Relation[]} Returns the supported operations between those two points. The order is important: after filtering that array, the first relation is considered as the default one.
	 */
	_supportedRelations: function(sourceTargets, targetTargets)
	{
		sourceTargets = Ext.Array.from(sourceTargets);
		targetTargets = Ext.Array.from(targetTargets);
		
		var sourceMatch = Ametys.message.MessageTargetHelper.findTargets(sourceTargets, this.sourceFilter, 1);
		if (sourceMatch.length == 0 || sourceMatch.length != sourceTargets.length)
		{
			// Source is not made of only resources and folders or is empty.
			return [];
		}
		var canRemoveFromSource = true;
		var sourceContainsFolders = false;
		var sourceContainsFiles = false;
		for (var i = 0; i < sourceMatch.length; i++)
		{
			// Getting id and rights from either an object or a MessageTarget
			var sourceId, rights;
			if (sourceMatch[i].isMessageTarget)
			{
				sourceId = sourceMatch[i].getId();
				rights = sourceMatch[i].getParameters()['rights'];
			}
			else
			{
				sourceId = sourceMatch[i].id;
				rights = sourceMatch[i].parameters['rights'];
			}
			
			if (sourceId == "resource")
			{
				sourceContainsFiles = true;
			}
			else
			{
				sourceContainsFolders = true;
			}
			
			if (!this.canRemoveFromSource(sourceId, rights))
			{
				canRemoveFromSource = false;
			}
		}
		
		var targetMatch = Ametys.message.MessageTargetHelper.findTargets(targetTargets, this.targetFilter, 1);
		if (targetMatch.length != 1 || targetTargets.length != 1)
		{
			// Target should be a single folder
			return [];
		}
		
		// Getting rights from either an object or a MessageTarget
		var rights;
		if (targetMatch[0].isMessageTarget)
		{
			rights = targetMatch[0].getParameters()['rights'];
		}
		else
		{
			rights = targetMatch[0].parameters['rights'];
		}
		
		var canAdd = this.canAddToTarget(rights, sourceContainsFiles, sourceContainsFolders);
		if (!canAdd)
		{
			// Target contains folders and folders cannot be added or contains files and files cannot be added
			return [];
		}
			
			
		var relations = [
			Ext.create('Ametys.relation.Relation', {
				type: Ametys.relation.Relation.COPY,
				label: "{{i18n PLUGINS_EXPLORER_RELATIONS_DEFAULTEXPLORER_COPY_LABEL}}",
				description: "{{i18n PLUGINS_EXPLORER_RELATIONS_DEFAULTEXPLORER_COPY_DESCRIPTION}}",
				smallIcon: null,
				mediumIcon: null,
				largeIcon: null
			})
		];
		
		if (canRemoveFromSource)
		{
			relations.push(Ext.create('Ametys.relation.Relation', {
				type: Ametys.relation.Relation.MOVE,
				label: "{{i18n PLUGINS_EXPLORER_RELATIONS_DEFAULTEXPLORER_MOVE_LABEL}}",
				description: "{{i18n PLUGINS_EXPLORER_RELATIONS_DEFAULTEXPLORER_MOVE_DESCRIPTION}}",
				smallIcon: null,
				mediumIcon: null,
				largeIcon: null
			}));
		}
			
		return relations;
	},
	
	supportedRelations: function(source, target)
	{
		return this._supportedRelations(source.targets, target.targets);
	},
	
	/**
	 * @protected
	 * Do the #link work but based only on targets array
	 * The method is called to link source to target using the given relation. 
	 * This operation can be asynchronous and will call callback at the end.
	 * In most cases this implementation will send a Ametys.message.Message to inform the UI that the operation was done.
	 * @param {Ametys.message.MessageTarget[]} sourceTargets The source point of the link operation. Targets are assumed to be ready.
	 * @param {Ametys.message.MessageTarget} target The end point of the link operation. Targets are assumed to be ready.
	 * @param {Function} callback The callback to call when operation has ended. 
	 * @param {boolean} callback.sucess True if the operation was successful
	 * @param {String} relationType The relation to create. One of the constants Ametys.relation.Relation.MOVE, Ametys.relation.Relation.COPY or Ametys.relation.Relation.REFERENCE.
	 */
	_link: function(sourceTargets, target, callback, relationType)
	{
		// Explorer collections
		var folderSourceIds = [];
		Ext.Array.forEach(Ametys.message.MessageTargetHelper.findTargets(sourceTargets, this.sourceFolderFilter, 1), function(target) {
			folderSourceIds.push(target.getParameters().id);
		});
		
		// Resources
		var resourceSourceIds = [];
		Ext.Array.forEach(Ametys.message.MessageTargetHelper.findTargets(sourceTargets, this.sourceFileFilter, 1), function(target) {
			resourceSourceIds.push(target.getParameters().id);
		});
		
		if (folderSourceIds.length > 0)
		{
			Ametys.explorer.resources.actions.Folder.move(target.getParameters().id, folderSourceIds, Ext.bind(this._linkCb, this, [callback], true));
		}
		else if (resourceSourceIds.length > 0)
		{
			Ametys.explorer.resources.actions.File[relationType == Ametys.relation.Relation.MOVE ? "move" : "copy"](
				target.getParameters().id, 
				resourceSourceIds, 
				Ext.bind(this._linkCb, this, [callback], true)
			);
		}
		else
		{
			callback(false);
		}
	},
	
	link: function(source, target, callback, relationType)
	{
		this._link(source.getTargets(), target.getTarget(), callback, relationType);
	},
	
	/**
	 * @private
	 * Callback of the link operation. That will call the initial callback.
	 * @param {String} target The folder id to move into
	 * @param {String[]} ids File ids moved effectively
	 * @param {Function} callback The callback to call when operation has ended. 
	 * @param {boolean} callback.sucess True if the operation was successful
	 */
	_linkCb: function(target, ids, callback)
	{
		callback(ids.length > 0);
	},
	
	/**
	 * @protected
	 * Get the filter (Function, String or Regexp) for source targets to move or copy
	 * @param {Object/Ametys.message.MessageTarget} target the target to test
	 * @return true if target matches
	 */
	sourceFilter: function (target)
	{
		var targetId = Ext.getClassName(target) == "Ametys.message.MessageTarget" ? target.getId() : target.id;
		if (targetId == Ametys.message.MessageTarget.EXPLORER_COLLECTION || targetId == Ametys.message.MessageTarget.RESOURCE)
		{
			// If target is not ready, returns true
			return Ext.getClassName(target) == "Ametys.message.MessageTarget" ? target.getParameters().rootOwnerType == 'explorer' : true;
		}
		return false;
	},
	
	/**
	 * @protected
	 * Get the filter (Function, String or Regexp) for targets of copy or move
	 * @param {Object/Ametys.message.MessageTarget} target the target to test
	 * @return {Boolean} true if target matches
	 */
	targetFilter: function (target)
	{
		var targetId = Ext.getClassName(target) == "Ametys.message.MessageTarget" ? target.getId() : target.id;
		if (targetId == Ametys.message.MessageTarget.EXPLORER_COLLECTION)
		{
			// If target is not ready, returns true
			return Ext.getClassName(target) == "Ametys.message.MessageTarget" ? target.getParameters().rootOwnerType == 'explorer' : true;
		}
		return false;
	},
	
	/**
	 * @protected
	 * Get the filter (Function, String or Regexp) for source targets of type 'folder' to move or copy
	 * @param {Object/Ametys.message.MessageTarget} target the target to test
	 * @return true if target matches
	 */
	sourceFolderFilter: function (target)
	{
		return target.getId() == Ametys.message.MessageTarget.EXPLORER_COLLECTION && target.getParameters().rootOwnerType == 'explorer';
	},
	
	/**
	 * @protected
	 * Get the filter (Function, String or Regexp) for source targets of type 'file' to move or copy
	 * @param {Object/Ametys.message.MessageTarget} target the target to test
	 * @return true if target matches
	 */
	sourceFileFilter: function (target)
	{
		return target.getId() == Ametys.message.MessageTarget.RESOURCE && target.getParameters().rootOwnerType == 'explorer';
	},
	
	/**
	 * @protected
	 * Determines if the source can be removed from the original folder
	 * @param {String} sourceId The target id of the moved source
	 * @param {String[]} rights The array of allowed rights on original folder
	 * @return {Boolean} true if remove is allowed
	 */
	canRemoveFromSource: function (sourceId, rights)
	{
		return !rights || Ext.Array.contains(rights, sourceId == Ametys.message.MessageTarget.RESOURCE ? "Plugin_Explorer_File_Delete" : "Plugin_Explorer_Folder_Delete")
	},
	
	/**
	 * @protected
	 * Determines if the copied/moved objects can be added to the destination folder
	 * @param {String[]} rights The array of allowed rights on destination folder
	 * @param {Boolean} sourceContainsFiles true if copied/moved objects contains files
	 * @param {Boolean} sourceContainsFolders true if copied/moved objects contains folders
	 * @return {Boolean} true if remove is allowed
	 */
	canAddToTarget: function (rights, sourceContainsFiles, sourceContainsFolders)
	{
		return !rights || (!sourceContainsFiles || Ext.Array.contains(rights, "Plugin_Explorer_File_Add")) && (!sourceContainsFolders || Ext.Array.contains(rights, "Plugin_Explorer_Folder_Add"))
	}
});