/*
 *  Copyright 2016 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 helper provides methods to update local assignments (client-side) on records
 * @private
 */
Ext.define('Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.AssignmentHelper', {
	singleton: true,
	
	/**
	 * Compute and update the local assignments of a given column (=profile)
	 * @param {Ext.util.Collection} records The records to update.
	 * @param {String} profileId The id of profile which is the id of column
	 */
	computeAndUpdateLocalInducedAssignments: function (records, profileId)
	{
		// When iterating, we need to be sure anonymous and anyconnected are the two first records (for then computing the others)
        // and we need to be sure group records come before all user records, as user records can be computed from their groups values
        
        // First, update local values for Anonymous and "any connected user" records
        var anonymousRecord = this.findAnonymousRecord(records);
        
        if (anonymousRecord.get(profileId) == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW)
        {
        	// If Anonymous is allowed, all others records are allowed and disabled
        	records.each(function(record) {
        		if (anonymousRecord.getId() != record.getId())
        		{
        			record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW_BY_ANONYMOUS, {dirty: this.isCellDirty(record, profileId)}); 
        		}
            }, this);
        	
        	return;
        }
        
        this.computeAndUpdateLocalInducedAssignmentsForAnonymous (records, anonymousRecord, anonymousRecord.get(profileId), profileId);
        
        var anyConnectedRecord = this.findAnyConnectedRecord(records);
        this.computeAndUpdateLocalInducedAssignmentsForAnyconnected (records, anyConnectedRecord, anyConnectedRecord.get(profileId), profileId);
        
        // Then, do a first iteration to update group records
        records.each(function(record) {
            var type = record.get('targetType');
            if (type == Ametys.plugins.coreui.profiles.PermissionTargetStore.TARGET_TYPE_GROUP)
            {
            	this.computeAndUpdateLocalInducedAssignmentsForGroup(records, record, record.get(profileId), profileId);
            }
        }, this);
        
        // Finally, do a second iteration to update user records
        records.each(function(record) {
            var type = record.get('targetType');
            if (type == Ametys.plugins.coreui.profiles.PermissionTargetStore.TARGET_TYPE_USER)
            {
            	this.computeAndUpdateLocalInducedAssignmentsForUser(records, record, record.get(profileId), profileId);
            }
        }, this);
	},
	
	/**
	 * Update the local assignment for anonymous 
     * @param {Ext.util.Collection} records The records to update.
	 * @param {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} record The anonymous record
	 * @param {String} currentValue The current assignment value
	 * @param {String} profileId The id of concerned profile
	 * @return {Boolean} true if changes were made, false otherwise
	 */
	computeAndUpdateLocalInducedAssignmentsForAnonymous: function (records, record, currentValue, profileId)
	{
		var hasLocalAssignment = currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW || currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY;
		if (!hasLocalAssignment)
		{
			// Check the value for any connected users
			var anyconnectedRecord = this.findAnyConnectedRecord(records);
            var assignmentForAnyconnected = anyconnectedRecord.get(profileId);
			
            if (assignmentForAnyconnected == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY)
            {
            	// If there is no local assignment and any connected user is denied, so Anonymous will be denied
                record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY_BY_ANYCONNECTED, {dirty: this.isCellDirty(record, profileId)}); 
                return true;
            }
		}
		
		return false;
	},
	
	/**
	 * Update the local assignment for any connected user 
     * @param {Ext.util.Collection} records The records to update.
	 * @param {Ext.data.Model} record The any connected user record
	 * @param {String} currentValue The current assignment value
	 * @param {String} profileId The id of concerned profile
	 * @return {Boolean} true if changes were made, false otherwise
	 */
	computeAndUpdateLocalInducedAssignmentsForAnyconnected: function (records, record, currentValue, profileId)
	{
		var hasLocalAssignment = currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW || currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY;
		if (!hasLocalAssignment)
		{
			// Check the value for anonymous
			var anonymousRecord = this.findAnonymousRecord(records);
            var assignmentForAnonymous = anonymousRecord.get(profileId);
            
            if (assignmentForAnonymous == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW || assignmentForAnonymous == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW)
            {
            	// If there is no local assignment and anonymous user is allowed, so any connected user will be allowed
            	record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW_BY_ANONYMOUS, {dirty: this.isCellDirty(record, profileId)}); 
            }
		}
	},
	
	/**
	 * Update the local assignment for a group
     * @param {Ext.util.Collection} records The records to update.
	 * @param {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} record The group record
	 * @param {String} currentValue The current assignment value
	 * @param {String} profileId The id of concerned profile
	 * @return {Boolean} true if changes were made, false otherwise
	 */
	computeAndUpdateLocalInducedAssignmentsForGroup: function (records, record, currentValue, profileId)
	{
		var hasLocalAssignment = currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW || currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY;
		if (!hasLocalAssignment)
		{
			// Get the current assignment for anonymous
        	var anonymousRecord = this.findAnonymousRecord(records);
            var assignmentForAnonymous = anonymousRecord.get(profileId);
            
            // Get the current access type for any connected users
        	var anyconnectedRecord = this.findAnyConnectedRecord(records);
        	var assignmentForAnyconnected = anyconnectedRecord.get(profileId);
        	
            if (assignmentForAnonymous == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW && currentValue != Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW && currentValue != Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_DENY)
            {
            	// If Anonymous is allowed by inheritance, the user/group is allowed except if it is already allowed/denied itself by inheritance
                record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW_BY_ANONYMOUS, {dirty: this.isCellDirty(record, profileId)}); 
                return true;
            }
            else if (assignmentForAnyconnected == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY)
            {
            	// If any connected user is locally denied, the user/group is denied
            	record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY_BY_ANYCONNECTED, {dirty: this.isCellDirty(record, profileId)}); 
            	return true;
            }
            else if (assignmentForAnyconnected == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_DENY && currentValue != Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW && currentValue != Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_DENY)
            {
            	// If any connected user is denied by inheritance, the user/group is denied except if it is already allowed/denied itself by inheritance
            	record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY_BY_ANYCONNECTED, {dirty: this.isCellDirty(record, profileId)}); 
            	return true;
            }
            else if (assignmentForAnyconnected == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW)
            {
            	// If any connected user is locally allowed, the user/group is allowed
            	record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW_BY_ANYCONNECTED, {dirty: this.isCellDirty(record, profileId)}); 
            	return true;
            }
            else if (assignmentForAnyconnected == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW && currentValue != Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW && currentValue != Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_DENY)
            {
            	// If any connected user is locally by inheritance, the user/group is allowed except if it is already allowed/denied itself by inheritance
            	record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW_BY_ANYCONNECTED, {dirty: this.isCellDirty(record, profileId)}); 
            	return true;
            }
		}
		
		return false;
	},
	
	/**
	 * Update the local assignment for a group
     * @param {Ext.util.Collection} records The records to update.
	 * @param {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} record The user record
	 * @param {String} currentValue The current assignment value
	 * @param {String} profileId The id of concerned profile
	 * @return {Boolean} true if changes were made, false otherwise
	 */
	computeAndUpdateLocalInducedAssignmentsForUser: function (records, record, currentValue, profileId)
	{
		var hasLocalAssignment = currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW || currentValue == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY;
		if (!hasLocalAssignment)
		{
			// First check the access given by the user's groups
			var isAllowedByGroup = false;
			var groups = record.get('groups');
			
			for (var i=0; i < groups.length; i++)
			{
				var group = groups[i];
				var groupRecord = this.findGroupRecord(records, group.groupId, group.groupDirectory);
				
				if (groupRecord != null)
				{
					var assignmentForGroup = groupRecord.get(profileId);
					if (assignmentForGroup == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY || assignmentForGroup == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_DENY)
					{
						// If at least one user's group is denied, so the user is denied
						record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_DENY_BY_GROUP, {dirty: this.isCellDirty(record, profileId)}); 
		            	return true;
					}
					else if (assignmentForGroup == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW || assignmentForGroup == Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_INHERITED_ALLOW)
					{
						isAllowedByGroup = true;
					}
				}
			}
			
			// If at least one user's group is allowed, the user is allowed
			if (isAllowedByGroup)
			{
				record.set(profileId, Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.ACCESS_TYPE_ALLOW_BY_GROUP, {dirty: this.isCellDirty(record, profileId)}); 
            	return true;
			}
			
			// If no user's group has allowed to determine access, check anonymous and any connected user access as for a group 
			return this.computeAndUpdateLocalInducedAssignmentsForGroup(records, record, currentValue, profileId);
		}
		return false;
	},
	
	/**
     * Find the Anonynous record
     * @param {Ext.util.Collection} records The records to search into.
     * @return {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} the Anonymous record or null if not found
     */
    findAnonymousRecord: function(records)
    {
    	var anonymousRecord = null;
        records.each(function(record) {
            if (record.get('targetType') == Ametys.plugins.coreui.profiles.PermissionTargetStore.TARGET_TYPE_ANONYMOUS)
            {
            	anonymousRecord = record; // stop iteration
            	return false;
            }
        }, this);
        
        return anonymousRecord;
    },
    
    /**
     * Find the "any connected user" record
     * @param {Ext.util.Collection} records The records to search into.
     * @return {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} the "any connected user" record or null if not found
     */
    findAnyConnectedRecord: function (records)
    {
    	var anyconnectedRecord = null;
       records.each(function(record) {
            if (record.get('targetType') == Ametys.plugins.coreui.profiles.PermissionTargetStore.TARGET_TYPE_ANYCONNECTEDUSER)
            {
            	anyconnectedRecord = record;
            	return false; // stop iteration
            }
        }, this);
        
        return anyconnectedRecord;
    },
    
    /**
     * Find a group record
     * @param {Ext.util.Collection} records The records to search into
     * @param {String} groupId the id of the group
     * @param {String} groupDirectory the id of the group directory
     * @return {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} the group record or null if not found
     */
    findGroupRecord: function(records, groupId, groupDirectory)
    {
    	var groupRecord = null;
    	records.each(function(record) {
            if (record.get('groupId') == groupId && record.get('groupDirectory') == groupDirectory)
            {
            	groupRecord = record;
            	return false; // stop iteration
            }
        }, this);
    	
        return groupRecord;
    },
    
    /**
     * Find a user record
     * @param {Ext.util.Collection} records The records to search into
     * @param {String} login the login of user
     * @param {String} population the id of population
     * @return {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} the user record or null if not found
     */
    findUserRecord: function(records, login, population)
    {
    	var userRecord = null;
    	records.each(function(record) {
            if (record.get('login') == login && record.get('populationId') == population)
            {
            	userRecord = record;
            	return false; // stop iteration
            }
        }, this);
    	
        return userRecord;
    },
    
    /**
     * Returns true if the cell corresponding to the given record and given column (profile) is in a dirty state
     * @param {Ametys.plugins.coreui.profiles.ProfileAssignmentsTool.Entry} record The record
     * @param {String} profileId The id of the profile
     * @return {Boolean} true if the cell is in a dirty state
     */
    isCellDirty: function(record, profileId)
    {
        var modified = record.modified;
        return isCellDirty = Ext.isObject(modified) && modified.hasOwnProperty(profileId);
    }

});