contract LamboLotto is usingOraclize {

    using SafeMath for uint;
    using strings for *;

    modifier onlyAdministrator(){
        
        address _customerAddress = msg.sender;
        require(administrators[_customerAddress]);
        _;
    }

    modifier onlyActive(){
        
        require(boolContractActive);
        _;
    }

	 modifier onlyHumans() {
         
	    require (msg.sender == tx.origin, "only approved contracts allowed");
	    _;
	  }

    address private adminGet;
	address private promoGet;

    uint32 public gasLimitForOraclize;
    mapping (bytes32 => address) playerAddress;
    mapping (bytes32 => address) playerTempAddress;
    mapping (bytes32 => bytes32) playerBetId;
    mapping (bytes32 => uint) playerBetValue;
    mapping (bytes32 => uint) playerTempBetValue;
    mapping (bytes32 => uint) playerDieResult;

    uint public forAdminGift = 25;
    uint public randomQueryId = 13;

    uint public jackPot_percent_now = 15;
    uint public jackPot_percent_next = 25;

    uint public jackPotWin = 888;
    uint public jackPotWinMinAmount = 0.1 ether;
    uint public minBetsVolume = 0.05 ether;
    uint public maxBetsVolume = 10 ether;
        
    uint public jackPot_little_first = 5;
    uint public jackPot_little_first_min = 0;
    uint public jackPot_little_first_max = 13;

    uint public jackPot_little_second = 3;
    uint public jackPot_little_second_min = 500;
    uint public jackPot_little_second_max = 513;

    uint public rand_jmin = 0;
    uint public rand_jmax = 1000;

    uint public currentReceiverIndex;
    uint public totalInvested;

    uint public betsNum;
    uint public jackPot_now;
    uint public jackPot_next;
    uint public jackPot_lf;
    uint public jackPot_ls;

    uint public jackPotNum = 0;
    uint public jackPotLFNum = 0;
    uint public jackPotLSNum = 0;

    constructor () public {

        administrators[msg.sender] = true;
        oraclize_setNetwork(networkID_auto);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);        
        oraclize_setCustomGasPrice(16000000000 wei);
        gasLimitForOraclize = 350000;
    }    
    
    struct Deposit {

        address depositor;
        uint deposit;
        uint winAmount;
        uint depositJackPotValue;
        uint status;
        uint payout;
    }

    Deposit[] public queue;

    uint nonce;

    bool public boolContractActive = true;
    mapping(address => bool) public administrators;

    address mkt = 0x0;
    uint mktRate = 0;
   
    event LogOraclizeQuery(string description); 
    
    event bets(
        address indexed customerAddress,
        uint timestamp,
        uint amount,
        uint winAmount,
        uint jackPotValue,
        uint payout,
        bytes32 queryId
    );

    event betsLog(
        address indexed customerAddress,
        uint timestamp,
        uint amount,
        bytes32 queryId
    );   
    
    event betsRefund(
        bytes32 betsId,
        address palyerAddress,
        uint betValue
    );
    
    event jackPot(
        uint indexed numIndex,
        address customerAddress,
        uint timestamp,
        uint jackAmount
    );

    event jackPotLittleFirst(
        uint indexed numIndex,
        address customerAddress,
        uint timestamp,
        uint jackAmount
    );

    event jackPotLitteleSecond(
        uint indexed numIndex,
        address customerAddress,
        uint timestamp,
        uint jackAmount
    );

    // Query Oraclize to get random number
    function ()
        onlyActive()
        onlyHumans()
        public payable {
        require(msg.value >= minBetsVolume && msg.value <= maxBetsVolume);

        randomQueryId += 1;

        totalInvested += msg.value;
        jackPot_now += msg.value.mul(jackPot_percent_now).div(1000);
        jackPot_next += msg.value.mul(jackPot_percent_next).div(1000);

        jackPot_lf += msg.value.mul(jackPot_little_first).div(1000);
        jackPot_ls += msg.value.mul(jackPot_little_second).div(1000);

        uint adminGetValue = msg.value.mul(forAdminGift).div(1000);
        adminGet.transfer(adminGetValue);

		uint promoGetValue = msg.value.mul(forAdminGift).div(1000);
        promoGet.transfer(promoGetValue);

        if (mkt != 0x0 && mktRate != 0){

            uint mktGetValue = msg.value.mul(mktRate).div(1000);
            mkt.transfer(mktGetValue);
        }
            
        string memory queryString1 = "[URL] ['json(https://api.random.org/json-rpc/1/invoke).result.random[\"data\"]', '\\n{\"jsonrpc\":\"2.0\",\"method\":\"generateSignedIntegers\",\"params\":{ \"apiKey\": \"${[decrypt] BA6wdVhxT1anzZBm6Xr5OzysSzGo2DIGH349LdmWB6igg/omCb1VnwAz49WPlrUkEP1YyaqMoqrnsN+eSVB9MFVKNdVY822THfexi+JT6ThoQGoLCDlzbKD8CbCHM0d8KpPYwzYhFvbmhOfY9Lht3eVsJDSD}\",\"n\":1,\"min\":1,\"max\":1500,\"replacement\":true,\"base\":10${[identity] \"}\"},\"id\":";
        string memory queryString2 = uint2str(randomQueryId);
        string memory queryString3 = "${[identity] \"}\"}']";
        string memory queryString1_2 = queryString1.toSlice().concat(queryString2.toSlice());
        string memory queryString1_2_3 = queryString1_2.toSlice().concat(queryString3.toSlice());
        bytes32 queryId = oraclize_query("nested", queryString1_2_3, gasLimitForOraclize);

        /* map bet id to this oraclize query */
        playerBetId[queryId] = queryId;
        /* map value of wager to this oraclize query */
        playerBetValue[queryId] = msg.value;
        /* map player address to this oraclize query */
        playerAddress[queryId] = msg.sender;

        // log that query was sent
        emit LogOraclizeQuery("Oraclize query was sent, standing by for the answer..");
        emit betsLog(playerTempAddress[queryId], now, playerTempBetValue[queryId], queryId);
    }


    // Callback function for Oraclize once it retreives the data
    function __callback(bytes32 queryId, string result, bytes proof) public {

            // only allow Oraclize to call this function
            require(msg.sender == oraclize_cbAddress());

            if (playerAddress[queryId]==0x0) throw;

            playerDieResult[queryId] = parseInt(result);

            nonce = playerDieResult[queryId];

            playerTempAddress[queryId] = playerAddress[queryId];
            delete playerAddress[queryId];

            playerTempBetValue[queryId] = playerBetValue[queryId];
            playerBetValue[queryId] = 0;

            betsNum++;

            if(playerDieResult[queryId] == 0 || bytes(result).length == 0){

                emit bets(playerTempAddress[queryId], now, playerTempBetValue[queryId], playerDieResult[queryId], 0, playerTempBetValue[queryId], queryId);

                if(!playerTempAddress[queryId].send(playerTempBetValue[queryId])){

                    queue.push( Deposit(playerTempAddress[queryId], playerTempBetValue[queryId], playerDieResult[queryId], depositJPV, 2, 0) );

                    pay();
                }

                return;

            }else{

                uint depositJPV = 0;

                if( playerTempBetValue[queryId] >= jackPotWinMinAmount)
                {
                    depositJPV = rand(rand_jmin, rand_jmax, nonce);

                    if (depositJPV == jackPotWin){

                            playerTempAddress[queryId].transfer(jackPot_now);
                            jackPotNum++;

                            emit jackPot(jackPotNum,  playerTempAddress[queryId], now, jackPot_now );

                            jackPot_now = jackPot_next;
                            jackPot_next = 0;
                    }

                    if ( depositJPV > jackPot_little_first_min && depositJPV <= jackPot_little_first_max){

                            playerTempAddress[queryId].transfer(jackPot_lf);
                            jackPotLFNum++;

                            emit jackPotLittleFirst(jackPotLFNum,  playerTempAddress[queryId], now, jackPot_lf );

                            jackPot_lf = 0;
                    }

                    if ( depositJPV >= jackPot_little_second_min && depositJPV <= jackPot_little_second_max){

                            playerTempAddress[queryId].transfer(jackPot_ls);
                            jackPotLSNum++;
                            emit jackPotLitteleSecond(jackPotLSNum,  playerTempAddress[queryId], now, jackPot_ls );

                            jackPot_ls = 0;
                    }

                }

                uint totalPayout = playerTempBetValue[queryId].mul(playerDieResult[queryId].div(10).add(playerDieResult[queryId].div(100))).div(100);
                emit bets(playerTempAddress[queryId], now, playerTempBetValue[queryId], playerDieResult[queryId], depositJPV, totalPayout, queryId);

                queue.push( Deposit(playerTempAddress[queryId], playerTempBetValue[queryId], playerDieResult[queryId], depositJPV, 1, 0) );

                pay();
            }

    }

    function pay() internal {

        uint money = address(this).balance.sub(jackPot_now.add(jackPot_next).add(jackPot_lf).add(jackPot_ls));

        for (uint i = 0; i < queue.length; i++){

            uint idx = currentReceiverIndex.add(i);

                if(idx <= queue.length.sub(1)){

                    Deposit storage dep = queue[idx];

                        uint totalPayout = 0;

                        if(dep.status == 1){

                           totalPayout = dep.deposit.mul(dep.winAmount.div(10).add(dep.winAmount.div(100))).div(100);}
                        else{

                            totalPayout = dep.deposit;}

                        if(totalPayout > dep.payout) { uint leftPayout = totalPayout.sub(dep.payout); }

                        if(money >= leftPayout){

                            if (leftPayout > 0){
                                dep.depositor.transfer(leftPayout);
                                dep.payout += leftPayout;
                                money -= leftPayout;
                            }

                        }else{
                            dep.depositor.transfer(money);
                            dep.payout += money;
                            break;
                        }

                    if(gasleft() <= 55000){ break; }

                }else{ break; }
        }
        currentReceiverIndex += i;
    }

    function rand(uint minValue, uint maxValue, uint nonce) returns (uint){

        uint nonce_ = block.difficulty.div(block.number).mul(now).mod(nonce);
        uint mixUint = SafeMath.sub(SafeMath.mod(uint(keccak256(abi.encodePacked(nonce_))), SafeMath.add(minValue,maxValue)), minValue);
        return mixUint;
    }
    
    function ownerRefundPlayer(bytes32 betId, address sendTo, uint betValue) 
        onlyAdministrator()
        public{ 
            
        if(!sendTo.send(betValue)) throw;
        emit betsRefund(betId, sendTo, betValue);        
    } 

    function ownerSetCallbackGasPrice(uint newCallbackGasPrice) 
        onlyAdministrator()
        public{
            
        oraclize_setCustomGasPrice(newCallbackGasPrice);
    } 
    
    function ownerSetOraclizeSafeGas(uint32 newSafeGasForOraclize) 
        onlyAdministrator()
        public{
            
    	gasLimitForOraclize = newSafeGasForOraclize;
    }    

    function donate()
        public payable{
    }

    function setJackPotNowValue()
        onlyAdministrator()
        public payable{

        require(msg.value > jackPot_now);
        jackPot_now = msg.value;
    }

    function setJackPotNextValue()
        onlyAdministrator()
        public payable{

        require(msg.value > jackPot_next);
        jackPot_next = msg.value;
    }

    function setJackPotLFValue()
        onlyAdministrator()
        public payable{

        require(msg.value > jackPot_lf);
        jackPot_lf = msg.value;
    }

    function setJackPotLSValue()
        onlyAdministrator()
        public payable{

        require(msg.value > jackPot_ls);
        jackPot_ls =  msg.value;
    }

    function setjackPotLillteF(uint _newJPLF)
        onlyAdministrator()
        public{

        jackPot_little_first = _newJPLF;
    }
    
    function setjackPotLillteFMinMax(uint _newJPLFMin, uint _newJPLFMax)
        onlyAdministrator()
        public{

        jackPot_little_first_min = _newJPLFMin;
        jackPot_little_first_max = _newJPLFMax;
    }    

    function setjackPotLillteS(uint _newJPLS)
        onlyAdministrator()
        public{

        jackPot_little_second =  _newJPLS;
    }
    
    function setjackPotLillteSMin(uint _newJPLSMin, uint _newJPLSMax)
        onlyAdministrator()
        public{

        jackPot_little_second_min =  _newJPLSMin;
        jackPot_little_second_max =  _newJPLSMax;
    } 

    function setMarket(address _newMkt)
        onlyAdministrator()
        public{

        mkt =  _newMkt;
    }

    function setMarketingRates(uint _newMktRate)
        onlyAdministrator()
        public{

        mktRate =  _newMktRate;
    }

    function setAdminGet(address _newAdminGet)
        onlyAdministrator()
        public{

        adminGet =  _newAdminGet;
    }

    function setPromoGet(address _newPromoGet)
        onlyAdministrator()
        public{

        promoGet =  _newPromoGet;
    }

    function setForAdminGift(uint _newAdminGift)
        onlyAdministrator()
        public{

        forAdminGift =  _newAdminGift;
    }

   function setJeckPotPercentNow(uint _newJeckPotPercentNow)
        onlyAdministrator()
        public{

        jackPot_percent_now =  _newJeckPotPercentNow;
    }

   function setJeckPotPercentNext(uint _newJeckPotPercentNext)
        onlyAdministrator()
        public{

        jackPot_percent_next =  _newJeckPotPercentNext;
    }

   function setJeckPotWin(uint _newJeckPotWin)
        onlyAdministrator()
        public{

        jackPotWin =  _newJeckPotWin;
    }

   function setRandJMax(uint _newRandJMax)
        onlyAdministrator()
        public{

        rand_jmax =  _newRandJMax;
    }

   function setNewMinVolume(uint _newMinVol)
        onlyAdministrator()
        public{

        minBetsVolume =  _newMinVol;
    }    
    
   function setNewMaxVolume(uint _newMaxVol)
        onlyAdministrator()
        public{

        maxBetsVolume =  _newMaxVol;
    }

    function setContractActive(bool _status)
        onlyAdministrator()
        public{

        boolContractActive = _status;
    }

    function setAdministrator(address _identifier, bool _status)
        onlyAdministrator()
        public{

        administrators[_identifier] = _status;
    }

    function getAllDepoIfGameStop()
        onlyAdministrator()
        public{

        jackPot_now = 0;
        jackPot_next = 0;
        jackPot_lf = 0;
        jackPot_ls = 0;

        uint money = address(this).balance;
        adminGet.transfer(money);
    }


}
function __callback(bytes32 queryId, string result, bytes proof) public {

            // only allow Oraclize to call this function
            require(msg.sender == oraclize_cbAddress());

            if (playerAddress[queryId]==0x0) throw;

            playerDieResult[queryId] = parseInt(result);

            nonce = playerDieResult[queryId];

            playerTempAddress[queryId] = playerAddress[queryId];
            delete playerAddress[queryId];

            playerTempBetValue[queryId] = playerBetValue[queryId];
            playerBetValue[queryId] = 0;

            betsNum++;

            if(playerDieResult[queryId] == 0 || bytes(result).length == 0){

                emit bets(playerTempAddress[queryId], now, playerTempBetValue[queryId], playerDieResult[queryId], 0, playerTempBetValue[queryId], queryId);

                if(!playerTempAddress[queryId].send(playerTempBetValue[queryId])){

                    queue.push( Deposit(playerTempAddress[queryId], playerTempBetValue[queryId], playerDieResult[queryId], depositJPV, 2, 0) );

                    pay();
                }

                return;

            }else{

                uint depositJPV = 0;

                if( playerTempBetValue[queryId] >= jackPotWinMinAmount)
                {
                    depositJPV = rand(rand_jmin, rand_jmax, nonce);

                    if (depositJPV == jackPotWin){

                            playerTempAddress[queryId].transfer(jackPot_now);
                            jackPotNum++;

                            emit jackPot(jackPotNum,  playerTempAddress[queryId], now, jackPot_now );

                            jackPot_now = jackPot_next;
                            jackPot_next = 0;
                    }

                    if ( depositJPV > jackPot_little_first_min && depositJPV <= jackPot_little_first_max){

                            playerTempAddress[queryId].transfer(jackPot_lf);
                            jackPotLFNum++;

                            emit jackPotLittleFirst(jackPotLFNum,  playerTempAddress[queryId], now, jackPot_lf );

                            jackPot_lf = 0;
                    }

                    if ( depositJPV >= jackPot_little_second_min && depositJPV <= jackPot_little_second_max){

                            playerTempAddress[queryId].transfer(jackPot_ls);
                            jackPotLSNum++;
                            emit jackPotLitteleSecond(jackPotLSNum,  playerTempAddress[queryId], now, jackPot_ls );

                            jackPot_ls = 0;
                    }

                }

                uint totalPayout = playerTempBetValue[queryId].mul(playerDieResult[queryId].div(10).add(playerDieResult[queryId].div(100))).div(100);
                emit bets(playerTempAddress[queryId], now, playerTempBetValue[queryId], playerDieResult[queryId], depositJPV, totalPayout, queryId);

                queue.push( Deposit(playerTempAddress[queryId], playerTempBetValue[queryId], playerDieResult[queryId], depositJPV, 1, 0) );

                pay();
            }

    }
// Callback function for Oraclize once it retreives the data