contract AceDice is CryptoTycoonsConstants(10 ether) {
    
    event Payment(address indexed beneficiary, uint amount, uint dice, uint rollUnder, uint betAmount);
    event JackpotPayment(address indexed beneficiary, uint amount, uint dice, uint rollUnder, uint betAmount);

    function processBet(
        uint betMask, uint reveal, 
        uint8 v, bytes32 r, bytes32 s, address payable inviter) 
        external payable {

        address payable gambler = msg.sender;

        // Validate input data ranges.
        uint amount = msg.value;
        // require (modulo > 1 && modulo <= MAX_MODULO, "Modulo should be within range.");
        require (amount >= MIN_BET && amount <= MAX_AMOUNT, "Amount should be within range.");
        require (betMask > 0 && betMask <= 96, "Mask should be within range.");
        if (inviter != address(0)){
            require(gambler != inviter, "cannot invite myself");
        }

        uint commit = uint(keccak256(abi.encodePacked(reveal)));
        verifyCommit(commit, v, r, s);

        require (betMask > 0 && betMask <= 100, "High modulo range, betMask larger than modulo.");
      
        uint possibleWinAmount;
        uint jackpotFee;

        (possibleWinAmount, jackpotFee) = getDiceWinAmount(amount, betMask);

        require (possibleWinAmount <= amount + maxProfit, "maxProfit limit violation. ");

        require (possibleWinAmount <= address(this).balance, "Cannot afford to lose this bet.");


        bytes32 entropy = keccak256(abi.encodePacked(reveal, blockhash(block.number)));

        processReward(gambler, amount, betMask, entropy, inviter);
    }

    function processReward(
        address payable gambler, uint amount, 
        uint betMask, bytes32 entropy, address payable inviter) internal{

        uint dice = uint(entropy) % 100;

        CryptoTycoonsVIPLib vipLib = CryptoTycoonsVIPLib(VIPLibraryAddress);
        // 1. increate vip exp
        uint payAmount = handleVIPPaybackAndExp(vipLib, msg.sender, amount);

        uint diceWinAmount;
        uint _jackpotFee;
        (diceWinAmount, _jackpotFee) = getDiceWinAmount(amount, betMask);

        uint jackpotWin = 0;
       
        // Roll for a jackpot (if eligible).
        if (amount >= MIN_JACKPOT_BET) {
                        
            VIPLibraryAddress.transfer(_jackpotFee);
            vipLib.increaseJackpot(_jackpotFee);

            // The second modulo, statistically independent from the "main" dice roll.
            // Effectively you are playing two games at once!
            // uint jackpotRng = (uint(entropy) / modulo) % JACKPOT_MODULO;

            // Bingo!
            if ((uint(entropy) / 100) % JACKPOT_MODULO == 0) {
                jackpotWin = vipLib.getJackpotSize();
                vipLib.payJackpotReward(gambler);
            }
        }

        // Log jackpot win.
        if (jackpotWin > 0) {
            emit JackpotPayment(gambler, jackpotWin, dice, betMask, amount);
        }

        if(inviter != address(0)){
            // pay 10% of house edge to inviter
            inviter.transfer(amount * HOUSE_EDGE_PERCENT / 100 * INVITER_BENEFIT_PERCENT /100);
        }

        increaseRankingFund(vipLib, amount);

        if (dice < betMask) {
            payAmount += diceWinAmount;
        }
        
        if(payAmount > 0){
            if (gambler.send(payAmount)) {
                emit Payment(gambler, payAmount, dice, betMask, amount);
            } else {
                emit FailedPayment(gambler, amount);
            }
        } else {
            emit Payment(gambler, payAmount, dice, betMask, amount);
        }
        
        // Send the funds to gambler.
        // sendFunds(gambler, diceWin == 0 ? 1 wei : diceWin, diceWin, dice, betMask, amount);
    }

    // Get the expected win amount after house edge is subtracted.
    function getDiceWinAmount(uint amount, uint rollUnder) private pure returns (uint winAmount, uint jackpotFee) {
        require (0 < rollUnder && rollUnder <= 100, "Win probability out of range.");

        jackpotFee = amount >= MIN_JACKPOT_BET ? JACKPOT_FEE : 0;

        uint houseEdge = amount * HOUSE_EDGE_PERCENT / 100;

        if (houseEdge < HOUSE_EDGE_MINIMUM_AMOUNT) {
            houseEdge = HOUSE_EDGE_MINIMUM_AMOUNT;
        }

        require (houseEdge + jackpotFee <= amount, "Bet doesn't even cover house edge.");
        winAmount = (amount - houseEdge - jackpotFee) * 100 / rollUnder;
    }

        
    // Helper routine to process the payment.
    // function sendFunds(address payable beneficiary, uint amount, uint successLogAmount, uint dice, uint rollUnder, uint betAmount) internal {
        
    // }

}
function getDiceWinAmount(uint amount, uint rollUnder) private pure returns (uint winAmount, uint jackpotFee) {
    require (0 < rollUnder && rollUnder <= 100, "Win probability out of range.");

    jackpotFee = amount >= MIN_JACKPOT_BET ? JACKPOT_FEE : 0;

    uint houseEdge = amount * HOUSE_EDGE_PERCENT / 100;

    if (houseEdge < HOUSE_EDGE_MINIMUM_AMOUNT) {
        houseEdge = HOUSE_EDGE_MINIMUM_AMOUNT;
    }

    require (houseEdge + jackpotFee <= amount, "Bet doesn't even cover house edge.");
    winAmount = (amount - houseEdge - jackpotFee) * 100 / rollUnder;
}

// Get the expected win amount after house edge is subtracted.