// Contract: DelegationManagementContract
// Target function: function setCollectionLock(address _collectionAddress, bool _status) public
2 collectionLockHash;
        bytes32 collectionUsecaseLockHash;
        bytes32 collectionUsecaseLockHashAll;
        // Locks
        collectionLockHash = keccak256(abi.encodePacked(_collectionAddress, _delegationAddress));
        collectionUsecaseLockHash = keccak256(abi.encodePacked(_collectionAddress, _delegationAddress, _useCase));
        collectionUsecaseLockHashAll = keccak256(abi.encodePacked(ALL_COLLECTIONS, _delegationAddress, _useCase));
        require(globalLock[_delegationAddress] == false);
        require(collectionLock[collectionLockHash] == false);
        require(collectionUsecaseLock[collectionUsecaseLockHash] == false);
        require(collectionUsecaseLock[collectionUsecaseLockHashAll] == false);
        // Push data to mappings
        globalHash = keccak256(abi.encodePacked(msg.sender, _collectionAddress, _delegationAddress, _useCase));
        delegatorHash = keccak256(abi.encodePacked(msg.sender, _collectionAddress, _useCase));
        // Stores delegation addresses on a delegator hash
        delegationAddressHash = keccak256(abi.encodePacked(_delegationAddress, _collectionAddress, _useCase));
        delegatorHashes[delegatorHash].push(_delegationAddress);
        // Stores delegators addresses on a delegation address hash
        delegationAddressHashes[delegationAddressHash].push(msg.sender);
        // Push additional data to the globalDelegationHashes mapping
        if (_allTokens == true) {
            GlobalData memory newdelegationGlobalData = GlobalData(msg.sender, _delegationAddress, block.timestamp, _expiryDate, true, 0);
            globalDelegationHashes[globalHash].push(newdelegationGlobalData);
        } else {
            GlobalData memory newdelegationGlobalData = GlobalData(msg.sender, _delegationAddress, block.timestamp, _expiryDate, false, _tokenId);
            globalDelegationHashes[globalHash].push(newdelegationGlobalData);
        }
        emit RegisterDelegation(msg.sender, _collectionAddress, _delegationAddress, _useCase, _allTokens, _tokenId);
    }

    /**
     * @notice Function to support subDelegation rights
     * @notice A delegation Address that has subDelegation rights given by a Delegator can register Delegations on behalf of Delegator
     */

    function registerDelegationAddressUsingSubDelegation(address _delegatorAddress, address _collectionAddress, address _delegationAddress, uint256 _expiryDate, uint256 _useCase, bool _allTokens, uint256 _tokenId) public {
        // Check subdelegation rights for the specific collection
        {
            bool subdelegationRightsCol;
            address[] memory allDelegators = retrieveDelegators(msg.sender, _collectionAddress, USE_CASE_SUB_DELEGATION);
            if (allDelegators.length > 0) {
                for (uint i = 0; i < allDelegators.length; ) {
                    if (_delegatorAddress == allDelegators[i]) {
                        subdelegationRightsCol = true;
                        break;
                    }

                    unchecked {
                        ++i;
                    }
                }
            }
            // Check subdelegation rights for All collections
            allDelegators = retrieveDelegators(msg.sender, ALL_COLLECTIONS, USE_CASE_SUB_DELEGATION);
            if (allDelegators.length > 0) {
                if (subdelegationRightsCol != true) {
                    for (uint i = 0; i < allDelegators.length; ) {
                        if (_delegatorAddress == allDelegators[i]) {
                            subdelegationRightsCol = true;
                            break;
                        }

                        unchecked {
                            ++i;
                        }
                    }
                }
            }
            // Allow to register
            require((subdelegationRightsCol == true));
        }
        // If check passed then register delegation address for Delegator
        require((_useCase > 0 && _useCase <= useCaseCounter));
        bytes32 delegatorHash;
        bytes32 delegationAddressHash;
        bytes32 globalHash;
        bytes32 collectionLockHash;
        bytes32 collectionUsecaseLockHash;
        bytes32 collectionUsecaseLockHashAll;
        // Locks
        collectionLockHash = keccak256(abi.encodePacked(_collectionAddress, _delegationAddress));
        collectionUsecaseLockHash = keccak256(abi.encodePacked(_collectionAddress, _delegationAddress, _useCase));
        collectionUsecaseLockHashAll = keccak256(abi.encodePacked(ALL_COLLECTIONS, _delegationAddress, _useCase));
        require(globalLock[_delegationAddress] == false);
        require(collectionLock[collectionLockHash] == false);
        require(collectionUsecaseLock[collectionUsecaseLockHash] == false);
        require(collectionUsecaseLock[collectionUsecaseLockHashAll] == false);
        // Push data to mappings
        globalHash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _delegationAddress, _useCase));
        delegatorHash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _useCase));
        // Stores delegation addresses on a delegator hash
        delegationAddressHash = keccak256(abi.encodePacked(_delegationAddress, _collectionAddress, _useCase));
        delegatorHashes[delegatorHash].push(_delegationAddress);
        // Stores delegators addresses on a delegation address hash
        delegationAddressHashes[delegationAddressHash].push(_delegatorAddress);
        // Push additional data to the globalDelegationHashes mapping
        if (_allTokens == true) {
            GlobalData memory newdelegationGlobalData = GlobalData(_delegatorAddress, _delegationAddress, block.timestamp, _expiryDate, true, 0);
            globalDelegationHashes[globalHash].push(newdelegationGlobalData);
        } else {
            GlobalData memory newdelegationGlobalData = GlobalData(_delegatorAddress, _delegationAddress, block.timestamp, _expiryDate, false, _tokenId);
            globalDelegationHashes[globalHash].push(newdelegationGlobalData);
        }
        emit RegisterDelegationUsingSubDelegation(_delegatorAddress, msg.sender, _collectionAddress, _delegationAddress, _useCase, _allTokens, _tokenId);
    }

    /**
     * @notice Delegator revokes delegation rights given to a delegation address on a specific use case on a specific NFT collection
     * @notice This function does not remove the delegation from the collectionsRegistered or useCaseRegistered as we want to track delegations history
     */

    function revokeDelegationAddress(address _collectionAddress, address _delegationAddress, uint256 _useCase) public {
        bytes32 delegatorHash;
        bytes32 delegationAddressHash;
        bytes32 globalHash;
        uint256 count;
        globalHash = keccak256(abi.encodePacked(msg.sender, _collectionAddress, _delegationAddress, _useCase));
        delegatorHash = keccak256(abi.encodePacked(msg.sender, _collectionAddress, _useCase));
        delegationAddressHash = keccak256(abi.encodePacked(_delegationAddress, _collectionAddress, _useCase));
        // Revoke delegation Address from the delegatorHashes mapping
        count = 0;
        if (delegatorHashes[delegatorHash].length > 0) {
            for (uint256 i = 0; i < delegatorHashes[delegatorHash].length; ) {
                if (_delegationAddress == delegatorHashes[delegatorHash][i]) {
                    count = count + 1;
                }

                unchecked {
                    ++i;
                }
            }
            uint256[] memory delegationsPerUser = new uint256[](count);
            uint256 count1 = 0;
            for (uint256 i = 0; i < delegatorHashes[delegatorHash].length; ) {
                if (_delegationAddress == delegatorHashes[delegatorHash][i]) {
                    delegationsPerUser[count1] = i;
                    count1 = count1 + 1;
                }

                unchecked {
                    ++i;
                }
            }

            if (count1 > 0) {
                for (uint256 j = 0; j < delegationsPerUser.length; ) {
                    uint256 temp1;
                    uint256 temp2;
                    temp1 = delegationsPerUser[delegationsPerUser.length - 1 - j];
                    temp2 = delegatorHashes[delegatorHash].length - 1;
                    delegatorHashes[delegatorHash][temp1] = delegatorHashes[delegatorHash][temp2];
                    delegatorHashes[delegatorHash].pop();

                    unchecked {
                        ++j;
                    }
                }                
            }
            // Revoke delegator Address from the delegationAddressHashes mapping
            uint256 countDA = 0;
            for (uint256 i = 0; i < delegationAddressHashes[delegationAddressHash].length; ) {
                if (msg.sender == delegationAddressHashes[delegationAddressHash][i]) {
                    countDA = countDA + 1;
                }

                unchecked {
                    ++i;
                }
            }
            uint256[] memory delegatorsPerUser = new uint256[](countDA);
            uint256 countDA1 = 0;
            for (uint256 i = 0; i < delegationAddressHashes[delegationAddressHash].length; ) {
                if (msg.sender == delegationAddressHashes[delegationAddressHash][i]) {
                    delegatorsPerUser[countDA1] = i;
                    countDA1 = countDA1 + 1;
                }

                unchecked {
                    ++i;
                }
            }
            if (countDA1 > 0) {
                for (uint256 j = 0; j < delegatorsPerUser.length; ) {
                    uint256 temp1;
                    uint256 temp2;
                    temp1 = delegatorsPerUser[delegatorsPerUser.length - 1 - j];
                    temp2 = delegationAddressHashes[delegationAddressHash].length - 1;
                    delegationAddressHashes[delegationAddressHash][temp1] = delegationAddressHashes[delegationAddressHash][temp2];
                    delegationAddressHashes[delegationAddressHash].pop();

                    unchecked {
                        ++j;
                    }
                }
            }
            // Delete global delegation data and emit event
            delete globalDelegationHashes[globalHash];
            emit RevokeDelegation(msg.sender, _collectionAddress, _delegationAddress, _useCase);
        }
    }

    /**
     * @notice This function supports the revoking of a Delegation Address using an address with Subdelegation rights
     */

    function revokeDelegationAddressUsingSubdelegation(address _delegatorAddress, address _collectionAddress, address _delegationAddress, uint256 _useCase) public {
        // Check subdelegation rights for the specific collection
        {
            bool subdelegationRightsCol;
            address[] memory allDelegators = retrieveDelegators(msg.sender, _collectionAddress, USE_CASE_SUB_DELEGATION);
            if (allDelegators.length > 0) {
                for (uint i = 0; i < allDelegators.length; ) {
                    if (_delegatorAddress == allDelegators[i]) {
                        subdelegationRightsCol = true;
                        break;
                    }

                    unchecked {
                        ++i;
                    }
                }     
            }
            // Check subdelegation rights for All collections
            allDelegators = retrieveDelegators(msg.sender, ALL_COLLECTIONS, USE_CASE_SUB_DELEGATION);
            if (allDelegators.length > 0) {
                if (subdelegationRightsCol != true) {
                    for (uint i = 0; i < allDelegators.length; ) {
                        if (_delegatorAddress == allDelegators[i]) {
                            subdelegationRightsCol = true;
                            break;
                        }

                        unchecked {
                            ++i;
                        }
                    }
                }
            }
            // Allow to revoke
            require((subdelegationRightsCol == true));
        }
        // If check passed then revoke delegation address for Delegator
        bytes32 delegatorHash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _useCase));
        bytes32 delegationAddressHash = keccak256(abi.encodePacked(_delegationAddress, _collectionAddress, _useCase));
        bytes32 globalHash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _delegationAddress, _useCase));
        uint256 count;
        count = 0;
        if (delegatorHashes[delegatorHash].length > 0) {
            for (uint256 i = 0; i < delegatorHashes[delegatorHash].length; ) {
                if (_delegationAddress == delegatorHashes[delegatorHash][i]) {
                    count = count + 1;
                }

                unchecked {
                    ++i;
                }
            }
            uint256[] memory delegationsPerUser = new uint256[](count);
            uint256 count1 = 0;
            for (uint256 i = 0; i < delegatorHashes[delegatorHash].length; ) {
                if (_delegationAddress == delegatorHashes[delegatorHash][i]) {
                    delegationsPerUser[count1] = i;
                    count1 = count1 + 1;
                }

                unchecked {
                    ++i;
                }
            }
            if (count1 > 0) {
                for (uint256 j = 0; j < delegationsPerUser.length; ) {
                    uint256 temp1;
                    uint256 temp2;
                    temp1 = delegationsPerUser[delegationsPerUser.length - 1 - j];
                    temp2 = delegatorHashes[delegatorHash].length - 1;
                    delegatorHashes[delegatorHash][temp1] = delegatorHashes[delegatorHash][temp2];
                    delegatorHashes[delegatorHash].pop();

                    unchecked {
                        ++j;
                    }
                }
            }
            // Revoke delegator Address from the delegationAddressHashes mapping
            uint256 countDA = 0;
            for (uint256 i = 0; i < delegationAddressHashes[delegationAddressHash].length; ) {
                if (_delegatorAddress == delegationAddressHashes[delegationAddressHash][i]) {
                    countDA = countDA + 1;
                }

                unchecked {
                    ++i;
                }
            }
            uint256[] memory delegatorsPerUser = new uint256[](countDA);
            uint256 countDA1 = 0;
            for (uint256 i = 0; i < delegationAddressHashes[delegationAddressHash].length; ) {
                if (_delegatorAddress == delegationAddressHashes[delegationAddressHash][i]) {
                    delegatorsPerUser[countDA1] = i;
                    countDA1 = countDA1 + 1;
                }

                unchecked {
                    ++i;
                }
            }
            if (countDA1 > 0) {
                for (uint256 j = 0; j < delegatorsPerUser.length; ) {
                    uint256 temp1;
                    uint256 temp2;
                    temp1 = delegatorsPerUser[delegatorsPerUser.length - 1 - j];
                    temp2 = delegationAddressHashes[delegationAddressHash].length - 1;
                    delegationAddressHashes[delegationAddressHash][temp1] = delegationAddressHashes[delegationAddressHash][temp2];
                    delegationAddressHashes[delegationAddressHash].pop();

                    unchecked {
                        ++j;
                    }
                }
            }
            // Delete global delegation data and emit event
            delete globalDelegationHashes[globalHash];
            emit RevokeDelegationUsingSubDelegation(_delegatorAddress, msg.sender, _collectionAddress, _delegationAddress, _useCase);
        }
    }

    /**
     * @notice Batch revoking (up to 5 delegation addresses)
     */

    function batchRevocations(address[] memory _collectionAddresses, address[] memory _delegationAddresses, uint256[] memory _useCases) public {
        require(_collectionAddresses.length < 6);
        for (uint256 i = 0; i < _collectionAddresses.length; ) {
            revokeDelegationAddress(_collectionAddresses[i], _delegationAddresses[i], _useCases[i]);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Delegator updates a delegation address for a specific use case on a specific NFT collection for a certain duration
     */

    function updateDelegationAddress(address _collectionAddress, address _olddelegationAddress, address _newdelegationAddress, uint256 _expiryDate, uint256 _useCase, bool _allTokens, uint256 _tokenId) public {
        revokeDelegationAddress(_collectionAddress, _olddelegationAddress, _useCase);
        registerDelegationAddress(_collectionAddress, _newdelegationAddress, _expiryDate, _useCase, _allTokens, _tokenId);
        emit UpdateDelegation(msg.sender, _collectionAddress, _olddelegationAddress, _newdelegationAddress, _useCase, _allTokens, _tokenId);
    }

    /**
     * @notice Batch registrations function (up to 5 delegation addresses)
     */

    function batchDelegations(address[] memory _collectionAddresses, address[] memory _delegationAddresses, uint256[] memory _expiryDates, uint256[] memory _useCases, bool[] memory _allTokens, uint256[] memory _tokenIds) public {
        require(_collectionAddresses.length < 6);
        for (uint256 i = 0; i < _collectionAddresses.length; ) {
            registerDelegationAddress(_collectionAddresses[i], _delegationAddresses[i], _expiryDates[i], _useCases[i], _allTokens[i], _tokenIds[i]);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Set global Lock status (hot wallet)
     */

    function setGlobalLock(bool _status) public {
        globalLock[msg.sender] = _status;
    }