/*
 *  Copyright 2015 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.
 */

/**
 * <p>This tool displays a tree of sites
 * @private
 */
Ext.define('Ametys.plugins.web.site.SitesTool', {
	extend: 'Ametys.tool.Tool',
	
	/**
	 * @private
	 * @property {Ametys.web.site.SitesTree} _tree The sites tree panel
	 */
    /**
     * @private
     * @property {String} _autoSelectId The site to select on next refresh
     */
	
	constructor: function(config)
	{
		this.callParent(arguments);
		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._onCreated, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDeleted, this);
	},
		
	getMBSelectionInteraction: function() 
	{
		return Ametys.tool.Tool.MB_TYPE_ACTIVE;
	},
	
	createPanel: function ()
	{
		this._tree = this._createSitesTreePanel();
		
		this._tree.getSelectionModel().select(0);
		return this._tree;
	},
	
	sendCurrentSelection: function()
	{
		this._onSelectSite();
	},
	
	setParams: function (params)
	{
		this.callParent(arguments);
        this._autoSelectId = params ? params.id : null;
    	this.refresh();
	},
	
	/**
	 * Refreshes the tool
	 */
	refresh: function ()
	{
		this.showRefreshing();
		
		Ametys.data.ServerComm.callMethod({
			role: "org.ametys.web.repository.site.SiteDAO",
			methodName: "getRootId",
			parameters: [],
			callback: {
				scope: this,
				handler: this._getRootCb
			},
			errorMessage: {
				category: this.self.getName(),
				msg: "{{i18n PLUGINS_WEB_ADMINISTRATOR_SITES_HANDLE_GETROOT_ERROR}}"
			}
		});
	},
	
	/**
	 * @private
	 * Callback function invoked after retrieving the id of sites root node
	 * @param {String} rootId The id of sites root node
	 */
	_getRootCb: function (rootId)
	{
		var newRoot = Ext.clone(this._tree.getRootNode());
        newRoot.setId(rootId);
        this._tree.setRootNode(newRoot); 
        
		this._tree.getStore().load({node: this._tree.getRootNode(), callback: this.showRefreshed, scope: this});
	},
    
    /**
     * @private
     * After store load
     */
    _onLoad: function()
    {
        if (this._autoSelectId)
        {
            var record = this._tree.getStore().findNode('id', this._autoSelectId);
            if (record)
            {
                this._tree.getSelectionModel().select([record]);
            }
            this._autoSelectId = null;
        }
    },

    /**
     * Get the tree of the tool
     * @return {Ext.tree.Panel} The main tree component of the tool
     */
	getTree: function()
	{
		return this._tree;
	},
	
	/**
	 * @private
	 * Draw the tree panel for the sites
	 */
	_createSitesTreePanel: function()
	{
		return Ext.create('Ametys.web.site.SitesTree', {
			showColumns: true,
			
			cls: 'uitool-sites',
			url: 'administrator/sites.json',
			plugin: 'web',
            
            stateId: this.$className,
            stateful: true,
			
			// handle drag & drop
			viewConfig: {
                plugins: {
                    ptype: 'ametystreeviewdragdrop',
                    containerScroll: true,
                    appendOnly: true,
                    sortOnDrop: true,
                    expandDelay: 500,
                    allowContainerDrops: true,
                    setAmetysDragInfos: Ext.bind(this.getDragInfo, this),
                    setAmetysDropZoneInfos: Ext.bind(this.getDropInfo, this)
                }
            },
		
			listeners: {
				'selectionchange': this.sendCurrentSelection,
                'load': this._onLoad, 
                scope: this
			}
		});
	},
	
	/**
     * @private
     * This event is thrown by the getDragData to add the 'source' of the drag.
     * @param {Object} item The default drag data that will be transmitted. You have to add a 'source' item in it: 
     * @param {Ametys.relation.RelationPoint} item.source The source (in the relation way) of the drag operation. 
     */
    getDragInfo: function (item)
    {
        var targets = [];
        
        for (var i = 0; i < item.records.length; i++)
        {
        	var node = item.records[i];
            targets.push({
            	id: node.isRoot() ? Ametys.message.MessageTarget.ROOT_SITES : Ametys.message.MessageTarget.SITE,
				parameters: {
					id: node.getId(), 
					name: node.get('name'),
					title: node.get('title')
				}
            });
        }
    
        if (targets.length > 0)
        {
            item.source = {
                relationTypes: [Ametys.relation.Relation.MOVE, Ametys.relation.Relation.REFERENCE], 
                targets: targets
            };
        }
    },
    
    /**
     * @private
     * This event is thrown before the beforeDrop event and create the target of the drop operation relation.
     * @param {Ext.data.Model[]} targetRecords The target records of the drop operation.
     * @param {Object} item The default drag data that will be transmitted. You have to add a 'target' item in it: 
     * @param {Object} item.target The target (in the relation way) of the drop operation. A Ametys.relation.RelationPoint config.   
     */ 
    getDropInfo: function(targetRecords, item)
    {
        var targets = [];
        
        for (var i = 0; i < targetRecords.length; i++)
        {
        	var node = targetRecords[i];
            targets.push({
            	id: node.isRoot() ? Ametys.message.MessageTarget.ROOT_SITES : Ametys.message.MessageTarget.SITE,
				parameters: {
					id: node.getId(), 
					name: node.get('name'),
					title: node.get('title')
				}
            });
        }

        if (targets.length > 0)
        {
            item.target = {
                relationTypes: [Ametys.relation.Relation.MOVE], 
                targets: targets
            };
        }
    },
	
    /**
     * @private
     * Send selection message
     */
	_onSelectSite: function()
	{
		var targets = [];
		
		var selection = this._tree.getSelectionModel().getSelection();
		Ext.Array.forEach(selection, function(node) {
			
			target = Ext.create('Ametys.message.MessageTarget', {
				id: node.isRoot() ? Ametys.message.MessageTarget.ROOT_SITES : Ametys.message.MessageTarget.SITE,
				parameters: {
					id: node.getId(), 
					name: node.get('name'),
					title: node.get('title')
				}
			});
			
			targets.push(target);
		});
		
		Ext.create('Ametys.message.Message', {
			type: Ametys.message.Message.SELECTION_CHANGED,
			targets: targets
		});
	},
    
	/**
     * @private
     * Listener on edition (site or population)
     * @param {Ametys.message.Message} message The edition message
     */
	_onModified: function(message)
	{
        // Site edition
		var targets = message.getTargets(Ametys.message.MessageTarget.SITE);
		if (targets.length > 0)
		{
			var store = this._tree.getStore();
			var me = this;
			
			Ext.Array.forEach(targets, function(target) {
				var node = me._tree.getStore().getNodeById(target.getParameters().id);
				if (node != null && !me.isRefreshing())
				{
					// FIXME only refresh the edited node
					me._tree.getStore().load({node: node.parentNode});
				}
			});
		}
        
        // Population edition
		targets = message.getTargets(Ametys.message.MessageTarget.USER_POPULATION);
		if (targets.length > 0)
		{
			var store = this._tree.getStore();
            var modifiedPopulationIds = Ext.Array.map(targets, function(target) {
                return target.getParameters().id;
            });
			var me = this;
			
            // Retrieve the sites to update (sites which have one of the modified population as context)
            var sitesToUpdate = Ext.Array.filter(store.getRange(), function(site) {
                var sitePopulations = site.get('populations');
                if (sitePopulations != null)
                {
                    var sitePopulationsIds = Ext.Array.map(sitePopulations, function(population) {
                        return population.id;
                    });
                    return containsAny(sitePopulationsIds, modifiedPopulationIds);
                }
                return false;
            });
            if (sitesToUpdate.length > 0 && !me.isRefreshing())
            {
                // need refresh
                me.showOutOfDate()
            }
            
            function containsAny(arr1, arr2)
            {
                var result = false;
                
                // Tests if one of the item in arr2 is in arr1
                Ext.Array.each(arr2, function(item) {
                    if (Ext.Array.contains(arr1, item))
                    {
                        result = true;
                        return true;
                    }
                });
                
                return result;
            }
		}
	},
	
	/**
     * @private
     * Listener on site creation
     * @param {Ametys.message.Message} message The creation message
     */
	_onCreated: function (message)
	{
		var target = message.getTarget(Ametys.message.MessageTarget.SITE);
		if (target != null)
		{
			var store = this._tree.getStore();
			var node = store.getNodeById(target.getParameters().parentId);
			if (node == null)
			{
				node = this._tree.getRootNode();
			}
			else
			{
				node.set('expanded', true)
            	node.commit();
			}
			
			store.load({node: node});
		}
	},
	
	/**
     * @private
     * Listener on site deletion
     * @param {Ametys.message.Message} message The deletion message
     */
	_onDeleted: function (message)
	{
		var targets = message.getTargets(Ametys.message.MessageTarget.SITE);
		if (targets.length > 0)
		{
			var store = this._tree.getStore();
			var me = this;
			
			Ext.Array.forEach(targets, function(target) {
				
				var node = store.getNodeById(target.getParameters().id);
				if (node != null)
				{
					var parentNode = node.parentNode;
					me._tree.getSelectionModel().select([parentNode]);
					node.remove();
				}
			});
		}
	}
});

Ext.define("Ametys.message.SiteMessageTarget",
	{
		override: "Ametys.message.MessageTarget",
		
		statics: 
		{
			/**
			 * @member Ametys.message.MessageTarget
			 * @readonly
			 * @property {String} SITE The target type is a site.
			 */
			SITE: "site",
			
			/**
			 * @member Ametys.message.MessageTarget
			 * @readonly
			 * @property {String} ROOT_SITES The target type is the root of sites.
			 */
			ROOT_SITES: "root-sites"
		}
	}
);