// Contract: DelegationManagementContract
// Target function: function retrieveSubDelegationStatus(address _delegatorAddress, address _collectionAddress, address _delegationAddress) public view returns (bool)
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;
    }

    /**
     * @notice Set collection Lock status (hot wallet)
     */

    function setCollectionLock(address _collectionAddress, bool _status) public {
        if (_collectionAddress == ALL_COLLECTIONS) {
            setGlobalLock(_status);
        } else {
            bytes32 collectionLockHash = keccak256(abi.encodePacked(_collectionAddress, msg.sender));
            collectionLock[collectionLockHash] = _status;
        }
    }

    /**
     * @notice Set collection usecase Lock status (hot wallet)
     */

    function setCollectionUsecaseLock(address _collectionAddress, uint256 _useCase, bool _status) public {
        if (_useCase==1) {
            setCollectionLock(_collectionAddress, _status);
        } else {
            bytes32 collectionUsecaseLockHash = keccak256(abi.encodePacked(_collectionAddress, msg.sender, _useCase));
            collectionUsecaseLock[collectionUsecaseLockHash] = _status;
        }
    }

    /**
     * @notice This function updates the number of Use Cases in case more usecases are needed
     */

    function updateUseCaseCounter() public {
        useCaseCounter = useCaseCounter + 1;
    }

    // A full list of Available Getter functions

    /**
     * @notice Retrieve Global Lock Status
     */

    function retrieveGlobalLockStatus(address _delegationAddress) public view returns (bool) {
        return globalLock[_delegationAddress];
    }

    /**
     * @notice Retrieve Collection Lock Status
     */

    function retrieveCollectionLockStatus(address _collectionAddress, address _delegationAddress) public view returns (bool) {
        if (_collectionAddress == ALL_COLLECTIONS) {
            return retrieveGlobalLockStatus(_delegationAddress);
        } else {
            bytes32 collectionLockHash;
            collectionLockHash = keccak256(abi.encodePacked(_collectionAddress, _delegationAddress));
            return collectionLock[collectionLockHash];
        }
    }

    /**
     * @notice Retrieve Collection Use Case Lock Status
     */

    function retrieveCollectionUseCaseLockStatus(address _collectionAddress, address _delegationAddress, uint256 _useCase) public view returns (bool) {
        if (_useCase == 1) {
            return retrieveCollectionLockStatus(_collectionAddress, _delegationAddress);
        } else {
            bytes32 collectionUsecaseLockHash;
            collectionUsecaseLockHash = keccak256(abi.encodePacked(_collectionAddress, _delegationAddress, _useCase));
            return collectionUsecaseLock[collectionUsecaseLockHash];
        }
    }

    /**
     * @notice Retrieve Collection Use Case Lock Status for both specific colleciton and ALL_COLLECTIONS
     */

    function retrieveCollectionUseCaseLockStatusOneCall(address _collectionAddress, address _delegationAddress, uint256 _useCase) public view returns (bool) {
        if (_useCase == 1) {
            return retrieveCollectionLockStatus(_collectionAddress, _delegationAddress);
        } else {
            return retrieveCollectionUseCaseLockStatus(_collectionAddress, _delegationAddress, _useCase) || retrieveCollectionUseCaseLockStatus(ALL_COLLECTIONS, _delegationAddress, _useCase);
        }
    }

    /**
     * @notice Support function to retrieve the hash given specific parameters
     */

    function retrieveLocalHash(address _walletAddress, address _collectionAddress, uint256 _useCase) public pure returns (bytes32) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_walletAddress, _collectionAddress, _useCase));
        return (hash);
    }

    /**
     * @notice Support function to retrieve the global hash given specific parameters
     */

    function retrieveGlobalHash(address _delegatorAddress, address _collectionAddress, address _delegationAddress, uint256 _useCase) public pure returns (bytes32) {
        bytes32 globalHash;
        globalHash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _delegationAddress, _useCase));
        return (globalHash);
    }

    /**
     * @notice Returns an array of all delegation addresses (active AND inactive) assigned by a delegator for a specific use case on a specific NFT collection
     */

    function retrieveDelegationAddresses(address _delegatorAddress, address _collectionAddress, uint256 _useCase) public view returns (address[] memory) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _useCase));
        return (delegatorHashes[hash]);
    }

    /**
     * @notice Returns an array of all delegators (active AND inactive) that delegated to a delegationAddress for a specific use case on a specific NFT collection
     */

    function retrieveDelegators(address _delegationAddress, address _collectionAddress, uint256 _useCase) public view returns (address[] memory) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_delegationAddress, _collectionAddress, _useCase));
        return (delegationAddressHashes[hash]);
    }

    /**
     * @notice Returns the status of a collection/delegation for a delegator (cold wallet)
     * @notice false means that the cold wallet did not register a delegation or the delegation was revoked from the delegatorHashes mapping
     */

    function retrieveDelegatorStatusOfDelegation(address _delegatorAddress, address _collectionAddress, uint256 _useCase) public view returns (bool) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _useCase));
        return delegatorHashes[hash].length > 0;
    }

    /**
     * @notice Returns the status of a collection/delegation given a delegation address (hot wallet)
     * @notice false means that a delegation address is not registered or it was revoked from the delegationAddressHashes mapping
     */

    function retrieveDelegationAddressStatusOfDelegation(address _delegationAddress, address _collectionAddress, uint256 _useCase) public view returns (bool) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_delegationAddress, _collectionAddress, _useCase));
        return delegationAddressHashes[hash].length > 0;
    }

    /**
     * @notice Returns the status of a delegation given the delegator address as well as the delegation address
     */

    function retrieveGlobalStatusOfDelegation(address _delegatorAddress, address _collectionAddress, address _delegationAddress, uint256 _useCase) public view returns (bool) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _delegationAddress, _useCase));
        return globalDelegationHashes[hash].length > 0;
    }

    /**
     * @notice Returns the status of a delegation given the delegator address, the collection address, the delegation address as well as a specific token id
     */

    function retrieveTokenStatus(address _delegatorAddress, address _collectionAddress, address _delegationAddress, uint256 _useCase, uint256 _tokenId) public view returns (bool) {
        bytes32 hash;
        hash = keccak256(abi.encodePacked(_delegatorAddress, _collectionAddress, _delegationAddress, _useCase));
        bool status;
        if (globalDelegationHashes[hash].length > 0) {
            for (uint256 i = 0; i < globalDelegationHashes[hash].length; ) {
                if ((globalDelegationHashes[hash][i].allTokens == false) && (globalDelegationHashes[hash][i].tokens == _tokenId)) {
                    status = true;
                    break;
                } else {
                    status = false;
                }

                unchecked {
                    ++i;
                }
            }
            return status;
        } else {
            return false;
        }
    }

    /**
     * @notice Checks if the delegation address performing actions is the most recent delegated by the specific delegator
     */

    function retrieveStatusOfMostRecentDelegation(address _delegatorAddress, address _collectionAddress, address _delegationAddress, uint256 _useCase) public view returns (bool) {
        return _delegationAddress == retrieveMostRecentDelegation(_delegatorAddress, _collectionAddress, _useCase);
    }