contract ACOPoolFactory2V3 is ACOPoolFactory2V2 {

    /**
     * @dev Emitted when the operator address permission has been changed.
     * @param operator Address of the operator.
     * @param previousPermission Whether the operator was authorized.
     * @param newPermission Whether the operator will be authorized.
     */
    event SetOperator(address indexed operator, bool indexed previousPermission, bool indexed newPermission);
    
    /**
     * @dev Emitted when a ACO creator address permission has been changed.
     * @param acoCreator Address of the ACO creator.
     * @param previousPermission Whether the creator was authorized.
     * @param newPermission Whether the creator will be authorized.
     */
    event SetAuthorizedAcoCreator(address indexed acoCreator, bool indexed previousPermission, bool indexed newPermission);

    /**
     * @dev A map to register the ACO Fool Factory operators permissions.
     */
    mapping(address => bool) public operators;
    
    /**
     * @dev A map to register the ACO Pool creator.
     */
    mapping(address => address) public creators;
    
    /**
     * @dev Addresses of authorized ACO creators for a pool.
     */
    address[] internal acoAuthorizedCreators;
    
    /**
     * @dev Function to get the number of ACO creators authorized for a pool.
     * @return The number of ACO creators authorized.
     */
    function getNumberOfAcoCreatorsAuthorized() view external virtual returns(uint256) {
        return acoAuthorizedCreators.length;
    }
    
    /**
     * @dev Function to get the address of ACO creators authorized.
     * @param index The index of the ACO creator.
     * @return The address of ACO creators authorized.
     */
    function getAcoCreatorAuthorized(uint256 index) view external virtual returns(address) {
        return acoAuthorizedCreators[index];
    }
    
    /**
     * @dev Function to set the operator permission.
     * @param operator Address of the operator.
     * @param newPermission Whether the operator will be authorized.
     */
    function setOperator(address operator, bool newPermission) onlyFactoryAdmin external virtual {
        _setOperator(operator, newPermission);
    }
    
    /**
     * @dev Function to set the ACO creator permission.
     * @param acoCreator Address of the ACO creator.
     * @param newPermission Whether the creator will be authorized.
     */
    function setAuthorizedAcoCreator(address acoCreator, bool newPermission) onlyFactoryAdmin external virtual {
        _setAuthorizedAcoCreator(acoCreator, newPermission);
    }

    /**
     * @dev Function to change the ACO pools protocol config.
     * Only can be called by a pool admin.
     * @param lendingPoolReferral The value of the lending pool referral.
     * @param withdrawOpenPositionPenalty Value of the ACO Pool penalty percentage on withdrawing open positions. It is a percentage value (100000 is 100%).
     * @param underlyingPriceAdjustPercentage Value of the ACO Pool underlying price percentage adjust. It is a percentage value (100000 is 100%).
     * @param fee Value of the ACO Pool fee percentage. It is a percentage value (100000 is 100%).
     * @param maximumOpenAco Value of the ACO Pool maximum number of open ACOs allowed.
     * @param feeDestination Address of the ACO Pool fee destination.
     * @param assetConverter Address of the asset converter helper.
     */
    function setProtocolConfigOnAcoPool(
        uint16 lendingPoolReferral,
        uint256 withdrawOpenPositionPenalty,
        uint256 underlyingPriceAdjustPercentage,
        uint256 fee,
        uint256 maximumOpenAco,
        address feeDestination,
        address assetConverter, 
        address[] calldata acoPools
    ) onlyPoolAdmin external virtual {
        IACOPool2.PoolProtocolConfig memory config = IACOPool2.PoolProtocolConfig(lendingPoolReferral, withdrawOpenPositionPenalty, underlyingPriceAdjustPercentage, fee, maximumOpenAco, feeDestination, assetConverter);
        for (uint256 i = 0; i < acoPools.length; ++i) {
            IACOPool2(acoPools[i]).setProtocolConfig(config);
        }
    }
    
    /**
     * @dev Internal function to set the operator permission.
     * @param operator Address of the operator.
     * @param newPermission Whether the operator will be authorized.
     */
    function _setOperator(address operator, bool newPermission) internal virtual {
        emit SetOperator(operator, operators[operator], newPermission);
        operators[operator] = newPermission;
    }
    
    /**
     * @dev Internal function to set the ACO creator permission.
     * @param acoCreator Address of the ACO creator.
     * @param newPermission Whether the creator will be authorized.
     */
    function _setAuthorizedAcoCreator(address acoCreator, bool newPermission) internal virtual {
        bool previousPermission = false;
        uint256 size = acoAuthorizedCreators.length;
        for (uint256 i = size; i > 0; --i) {
            if (acoAuthorizedCreators[i - 1] == acoCreator) {
                previousPermission = true;
                if (!newPermission) {
                    if (i < size) {
                        acoAuthorizedCreators[i - 1] = acoAuthorizedCreators[(size - 1)];
                    }
                    acoAuthorizedCreators.pop();
                }
                break;
            }
        }
        if (newPermission && !previousPermission) {
            acoAuthorizedCreators.push(acoCreator);
        }
        emit SetAuthorizedAcoCreator(acoCreator, previousPermission, newPermission);
    }
}
function setOperator(address operator, bool newPermission) onlyFactoryAdmin external virtual {
    _setOperator(operator, newPermission);
}

/**
 * @dev Function to set the operator permission.
 * @param operator Address of the operator.
 * @param newPermission Whether the operator will be authorized.
 */