contract GCCOracleProofConsumer is GCCOracleReader {
    using SafeMath for uint256;

    uint256 internal _fullAmount;
    uint256 internal _usedAmount;
    mapping(address => uint256) internal _consumedProofs;

    function initialize(address sender, uint256 maxAmount) public initializer {
        _fullAmount = maxAmount;
        GCCOracleReader.initialize(sender);
    }

    /**
     * @dev Proves amount for given address with all accessible proofs.
     */
    function consumeProofs(address addr) public returns (bool) {
        _consumeProofs(addr, 1000);
        return true;
    }

    /**
     * @dev Proves amount for given address with a limit of newly processed proofs.
     */
    function consumeLimitedProofs(address addr, uint256 limit) public returns (bool) {
        _consumeProofs(addr, limit);
        return true;
    }

    /**
     * @dev Returns full consumable amount.
     */
    function getConsumableFullAmount() public view returns (uint256) {
        return _fullAmount;
    }

    /**
     * @dev Returns already used consumable amount.
     */
    function getConsumableUsedAmount() public view returns (uint256) {
        return _usedAmount;
    }

    /**
     * @dev Returns list of all already consumed proofs
     */
    function getConsumedProofs(address addr) public view returns (address[] memory, bytes32[] memory, uint64[] memory) {
        uint256 consumedLimit = _consumedProofs[addr];
        return oracle.getProofs(addr, 0, consumedLimit);
    }

    /**
     * @dev Returns list of few already consumed proofs constrained by cursor and limit
     */
    function fewConsumedProofs(address addr, uint cursor, uint limit) public view returns (address[] memory, bytes32[] memory, uint64[] memory) {
        uint256 consumedLimit = _consumedProofs[addr].sub(cursor) < limit ? _consumedProofs[addr].sub(cursor) : limit;
        return oracle.getProofs(addr, cursor, consumedLimit);
    }

    /**
     * @dev Internal function for proving amount(s)
     */
    function _consumeProofs(address addr, uint256 limit) internal {
        require(getOracleAddress() != address(0), "GCCOracleProofConsumer: Cannot provably mint tokens while there is no oracle!");

        uint256 prevCursor = _consumedProofs[addr];

        address[] memory addrList;
        bytes32[] memory txidList;
        uint64 [] memory coinList;

        (addrList, txidList, coinList) = getProofs(addr, prevCursor, limit);
        uint256 size = addrList.length;
        if (size == 0) return;

        uint256 nextCursor = prevCursor.add(size);
        uint256 mintAmount = 0;

        for (uint256 i=0; i<size; i++) {
            mintAmount = mintAmount.add(coinList[i]);
        }

        uint256 freeAmount = _fullAmount.sub(_usedAmount);
        require(mintAmount <= freeAmount, "GCCOracleProofConsumer: Cannot mint more tokens, limit exhausted!");

        _usedAmount = _usedAmount.add(mintAmount);
        _consumedProofs[addr] = nextCursor;
        _afterConsumeProofs(addr, mintAmount);
    }

    /**
     * @dev Hook to execute when proving amount
     */
    function _afterConsumeProofs(address account, uint256 amount) internal {
    }
}
function getConsumableUsedAmount() public view returns (uint256) {
    return _usedAmount;
}

/**
 * @dev Returns already used consumable amount.
 */