contract AlchemyMinting is AlchemySynthesize {

    // Limit the nubmer of zero order assets the owner can create every day
    uint256 public zoDailyLimit = 1000; // we can create 4 * 1000 = 4000 0-order asset each day
    uint256[4] public zoCreated;

    // Limit the number each account can buy every day
    mapping(address => bytes32) public accountsBoughtZoAsset;
    mapping(address => uint256) public accountsZoLastRefreshTime;

    // Price of zero order assets
    uint256 public zoPrice = 2500 szabo;

    // Last daily limit refresh time
    uint256 public zoLastRefreshTime = now;

    // Event
    event BuyZeroOrderAsset(address account, bytes32 values);

    // To ensure scarcity, we are unable to change the max numbers of zo assets every day.
    // We are only able to modify the price
    function setZoPrice(uint256 newPrice) external onlyCOO {
        zoPrice = newPrice;
    }

    // Buy zo assets from us
    function buyZoAssets(bytes32 values) external payable whenNotPaused {
        // Check whether we need to refresh the daily limit
        bytes32 history = accountsBoughtZoAsset[msg.sender];
        if (accountsZoLastRefreshTime[msg.sender] == uint256(0)) {
            // This account's first time to buy zo asset, we do not need to clear accountsBoughtZoAsset
            accountsZoLastRefreshTime[msg.sender] = zoLastRefreshTime;
        } else {
            if (accountsZoLastRefreshTime[msg.sender] < zoLastRefreshTime) {
                history = bytes32(0);
                accountsZoLastRefreshTime[msg.sender] = zoLastRefreshTime;
            }
        }

        uint256 currentCount = 0;
        uint256 count = 0;

        bytes32 mask = bytes32(255); // 0x11111111
        uint256 maskedValue;
        uint256 maskedResult;

        bytes32 asset = assets[msg.sender][0];

        for (uint256 i = 0; i < 4; i++) {
            if (i > 0) {
                mask = mask << 8;
            }
            maskedValue = uint256(values & mask);
            currentCount = maskedValue / 2 ** (8 * i);
            count += currentCount;

            // Check whether this account has bought too many assets
            maskedResult = uint256(history & mask);
            maskedResult += maskedValue;
            require(maskedResult < (2 ** (8 * (i + 1))));

            // Update account bought history
            history = ((history ^ mask) & history) | bytes32(maskedResult);

            // Check whether this account will have too many assets
            maskedResult = uint256(asset & mask);
            maskedResult += maskedValue;
            require(maskedResult < (2 ** (8 * (i + 1))));

            // Update user asset
            asset = ((asset ^ mask) & asset) | bytes32(maskedResult);

            // Check whether we have enough assets to sell
            require(zoCreated[i] + currentCount <= zoDailyLimit);

            // Update our creation history
            zoCreated[i] += currentCount;
        }

        // Ensure this account buy at least one zo asset
        require(count > 0);

        // Check whether there are enough money for payment
        require(msg.value >= count * zoPrice);

        // Write updated user asset
        assets[msg.sender][0] = asset;

        // Write updated history
        accountsBoughtZoAsset[msg.sender] = history;

        // Emit BuyZeroOrderAsset event
        emit BuyZeroOrderAsset(msg.sender, values);

    }

    // Our daemon will refresh daily limit
    function clearZoDailyLimit() external onlyCOO {
        uint256 nextDay = zoLastRefreshTime + 1 days;
        if (now > nextDay) {
            zoLastRefreshTime = nextDay;
            for (uint256 i = 0; i < 4; i++) {
                zoCreated[i] =0;
            }
        }
    }
}
function clearZoDailyLimit() external onlyCOO {
    uint256 nextDay = zoLastRefreshTime + 1 days;
    if (now > nextDay) {
        zoLastRefreshTime = nextDay;
        for (uint256 i = 0; i < 4; i++) {
            zoCreated[i] =0;
        }
    }
}

// Our daemon will refresh daily limit