contract SupplyCapAllowedCallerIssuanceHook is Ownable, IManagerIssuanceHook {
    using SafeMath for uint256;
    using AddressArrayUtils for address[];

    /* ============ Events ============ */

    event SupplyCapUpdated(uint256 _newCap);
    event CallerStatusUpdated(address indexed _caller, bool _status);
    event AnyoneCallableUpdated(bool indexed _status);
    
    /* ============ State Variables ============ */

    // Cap on totalSupply of Sets
    uint256 public supplyCap;

    // Boolean indicating if anyone can call function
    bool public anyoneCallable;

    // Mapping of contract addresses allowed to call function
    mapping(address => bool) public callAllowList;

    /* ============ Constructor ============ */

    /**
     * Constructor, overwrites owner and original supply cap.
     *
     * @param _initialOwner      Owner address, overwrites Ownable logic which sets to deployer as default
     * @param _supplyCap         Supply cap for Set (in wei of Set)
     */
    constructor(
        address _initialOwner,
        uint256 _supplyCap
    )
        public
    {
        supplyCap = _supplyCap;

        // Overwrite _owner param of Ownable contract
        transferOwnership(_initialOwner);
    }

    /* ============ External Functions ============ */

    /**
     * Adheres to IManagerIssuanceHook interface, and checks to make sure the current issue call won't push total supply over cap.
     */
    function invokePreIssueHook(
        ISetToken _setToken,
        uint256 _issueQuantity,
        address _sender,
        address /*_to*/
    )
        external
        override
    {
        _validateAllowedContractCaller(_sender);
        
        uint256 totalSupply = _setToken.totalSupply();
        require(totalSupply.add(_issueQuantity) <= supplyCap, "Supply cap exceeded");
    }

    /**
     * Adheres to IManagerIssuanceHook interface
     */
    function invokePreRedeemHook(
        ISetToken _setToken,
        uint256 _redeemQuantity,
        address _sender,
        address _to
    )
        external
        override
    {}

    /**
     * ONLY OWNER: Updates supply cap
     */
    function updateSupplyCap(uint256 _newCap) external onlyOwner {
        supplyCap = _newCap;
        SupplyCapUpdated(_newCap);
    }

    /**
     * ONLY OWNER: Toggle ability for passed addresses to call only allowed caller functions
     *
     * @param _callers           Array of caller addresses to toggle status
     * @param _statuses          Array of statuses for each caller
     */
    function updateCallerStatus(address[] calldata _callers, bool[] calldata _statuses) external onlyOwner {
        _callers.validatePairsWithArray(_statuses);

        for (uint256 i = 0; i < _callers.length; i++) {
            address caller = _callers[i];
            bool status = _statuses[i];
            callAllowList[caller] = status;
            emit CallerStatusUpdated(caller, status);
        }
    }

    /**
     * ONLY OWNER: Toggle whether anyone can call function, bypassing the callAllowlist 
     *
     * @param _status           Boolean indicating whether to allow anyone call
     */
    function updateAnyoneCallable(bool _status) external onlyOwner {
        anyoneCallable = _status;
        emit AnyoneCallableUpdated(_status);
    }

    /* ============ Internal Functions ============ */

    /**
     * Validate if passed address is allowed to call function. If anyoneCallable is set to true, anyone can call otherwise needs to be an EOA or 
     * approved contract address.
     */
    function _validateAllowedContractCaller(address _caller) internal view {
        require(
            _caller == tx.origin || anyoneCallable || callAllowList[_caller],
            "Contract not permitted to call"
        );
    }
}
/**
 * @title SupplyCapAllowedCallerIssuanceHook
 * @author Set Protocol
 *
 * Issuance hook that checks
 * 1) New issuances won't push SetToken totalSupply over supply cap
 * 2) A contract address is allowed to call the module. This does not apply if caller is an EOA
 */
function invokePreIssueHook(
    ISetToken _setToken,
    uint256 _issueQuantity,
    address _sender,
    address /*_to*/
)
    external
    override
{
    _validateAllowedContractCaller(_sender);
        
    uint256 totalSupply = _setToken.totalSupply();
    require(totalSupply.add(_issueQuantity) <= supplyCap, "Supply cap exceeded");
}

/**
 * Adheres to IManagerIssuanceHook interface, and checks to make sure the current issue call won't push total supply over cap.
 */