contract gameLotto is safeApi,usingOraclize{
    mapping(bytes32=>bool) private validQueryId;
    struct player
    {
        uint256 id;
        address addr;
        uint256 balance;//wei
        uint256 ticket;
        uint256 affNumLevel_1;
        uint256 affNumLevel_2;
        uint256 timeStamp;
        mapping(uint256=> playerRecord) index;
    }
    
    struct playerRecord{
        mapping(uint256=>uint256) numberOfTickets;
        uint8 betNumbers;
        uint32 betTickets;
        bool isReceive;
    }
    
    
    struct  gameConfig
    {
        uint256 unitPrice;
        uint256 singleLimit;
        uint256 ticketNum;
        uint256 dividendPct;
        uint256 lotteryPoolPct;
        uint256 prize_1;
        uint256 prize_2;
        uint256 prize_3;
        uint256 lotteryInterval;
        uint256 level1Rewards;
        uint256 level2Rewards;
        uint256 minBetNum;
        uint256 betMaxNumber;
        uint256 receiveAwardPct;
    }
    
    struct lotteryRecord{
        uint256 index;
        uint256 number;
        uint256 prize2number;
        uint256 prize3number;
        uint256 betOfNumber;
        uint256 prize1Wei;
        uint256 prize2Wei;
        uint256 prize3Wei;
        uint256 time;
    }
    
    struct gameInfo{
        uint256 nextLottery;
        uint256 totalTicket;
        uint256 useTicket;
        uint256 index;
        uint256 prizePool;
        mapping (uint256 => lotteryRecord) lotteryResult;
    }
    
    event Lottery(
      uint256 number,
      uint256 nextLottery,
      uint256 indexed index
   );
   
   event Bet(
      address indexed addr,
      uint256 indexed index,
      uint256 number,
      uint256 use
   );
   
    event Buy(
       address indexed addr,
       uint256 number,
       uint256 value
   );
   
    mapping (uint256 => player) public player_;
    mapping (address => uint256) public playAddr_;
    mapping (uint256 => uint256) public playAff_;

    mapping (uint256 => mapping(uint256 => uint256)) private indexNumberTicket_;
    mapping (uint256 => mapping(uint256 => uint256[])) private playIndexBetNumber_;

    
     gameConfig public gameConfig_;
     gameInfo public gameInfo_;
     address public admin__;
     uint256 adminPct__;
     uint256 private autoPlayId_=123456;
     uint256 constant CUSTOM_GASLIMIT = 250000;
     address shareContract=0x55b65bdbe29a1b2d29e28038b09075bac320d11d;
     bool betSwitch=true;
    constructor() public {
        admin__ = msg.sender;
        gameConfig_.unitPrice=10000000000000000;//0.01 eth
        gameConfig_.singleLimit=1000;//One-time ticket limit
        gameConfig_.ticketNum=3;//unitPrice Number of tickets
        gameConfig_.dividendPct=5;//%Buy tickets for the prize pool pct
        gameConfig_.lotteryPoolPct=50;//% Draw Prize Pool Use pct
        gameConfig_.prize_1=55;//%First prize pct
        gameConfig_.prize_2=30;//%Second prize pct
        gameConfig_.prize_3=15;//%Third prize pct
        gameConfig_.lotteryInterval=86400 seconds;//Draw Interval 1 day
        gameConfig_.level1Rewards=30;//%Inviter Level 1
        gameConfig_.level2Rewards=10;//%Inviter Level 2
        gameConfig_.minBetNum=10;//Minimum number of lottery tickets
        gameConfig_.betMaxNumber=20;//The number of bets per person per period
        gameConfig_.receiveAwardPct=5;//%
         adminPct__=5;//% admin Commission
        gameInfo_.nextLottery=1541336400;//Next draw time stamp
        gameInfo_.index=1;//Lottery Round
        getPlayId(admin__);
    }
   
    
    /* Buy lottery tickets */
    function buy(uint256 _number,uint256 _affCode,address level1,address level2)
    safe() 
    public
    payable {
        require(msg.value>=gameConfig_.unitPrice,'Please pay the correct eth');
        require(_number>0 && _number<=gameConfig_.singleLimit ,'limited single purchase');
        uint256 sendWei= gameConfig_.unitPrice * _number ;
        require(msg.value == sendWei ,'Please pay the correct eth!');
        uint256 pid=getPlayId(msg.sender);
        
        uint256 adminWei=safePercent(adminPct__,sendWei);
        uint256 lotteryPoolWei=safePercent(gameConfig_.dividendPct,sendWei);
        addAff(pid,_affCode,adminWei,level1,level2);//Inviter dividend
        addTicket(pid,_number);//Increase lottery
        gameInfo_.prizePool=add(gameInfo_.prizePool,lotteryPoolWei);//Partial access to the prize pool
        emit Buy(msg.sender,_number,sendWei);
    }
 
    /* Lottery bet */
    function bet(uint256 _number,uint16 _use) safe() external{
         require(_number >=12 && _number<=9876,'Please enter a correct number (2-4 digits)');
         require(_use >=1 && _use<=1000,'Please enter a correct integer (1-1000)');
         require(now<gameInfo_.nextLottery,'Please wait for the draw before you can continue to bet');
         require(betSwitch==true,'Error Action');
          uint256 pid=playAddr_[msg.sender];
          require(pid>0);
          player storage _p=player_[pid];
          uint256 _index=gameInfo_.index;
          playerRecord  storage _pIndex=_p.index[_index];
          require(_p.ticket>=_use,'No tickets available');
           _p.ticket-=_use;
          if(indexNumberTicket_[_index][_number]==0){
              require(_pIndex.betNumbers <= gameConfig_.betMaxNumber,'Limited number of bet numbers');
             _pIndex.betNumbers++;
          }
         _pIndex.betTickets +=_use;
         indexNumberTicket_[_index][_number]+=_use;
         //The number of times the player purchased this number in this period
          _pIndex.numberOfTickets[_number] +=_use;
          gameInfo_.lotteryResult[_index].betOfNumber+=_use;
           //0.003ETH
         gameInfo_.prizePool=add(gameInfo_.prizePool,mul(3000000000000000,_use));
         emit  Bet(msg.sender,_index,_number,_use);
    }
    
    
    
    
    function addAff(uint256 pid,uint256 _affCode,uint256 adminAmount,address level1,address level2) private{
         
        require(adminAmount>0);
        uint256 adminId=player_[playAddr_[admin__]].id;
        if(playAff_[pid]==0){
            
                if(_affCode==0){
                    _affCode=adminId;
                    level1=admin__;
                    level2=address(0);
                }
            
               uint256  level1Pid=adminId;
               uint256  level2Pid=0;
               bytes4 methodId = bytes4(keccak256("addOtherGameAff(uint256,address,address,address)"));
              if(shareContract.call(methodId,_affCode,msg.sender,level1,level2)){
                     level1Pid=getPlayId(level1);
                     level2Pid=getPlayId(level2);
              }
               playAff_[pid]=level1Pid;
               player_[level1Pid].affNumLevel_1++;
               if(level2Pid>0){
                  player_[level2Pid].affNumLevel_2++;
                  if( playAff_[level1Pid]==0)
                    playAff_[level1Pid]=level2Pid;
               }
        }
        
         if(playAff_[pid] != adminId)
        {
               uint256 level1Amount=safePercent(gameConfig_.level1Rewards,adminAmount);
                
               uint256 level1Id=playAff_[pid];
               player_[level1Id].balance = add(player_[level1Id].balance,level1Amount);
      
               uint256 level2Id=playAff_[level1Id];
               if(level2Id>0){
                     uint256 level2Amount=safePercent(gameConfig_.level2Rewards,adminAmount);
                    adminAmount=sub(adminAmount,level2Amount);
                    player_[level2Id].balance=add(player_[level2Id].balance,level2Amount);
               } 
                 adminAmount=sub(adminAmount,level1Amount);   
            require(adminAmount>0);
        }
        
      player_[adminId].balance=add(player_[adminId].balance,adminAmount);
  }
    
    
    function  addTicket(uint256 pid,uint256 _number) private{
         require(player_[pid].id > 0);
         uint256 addTicketNum=mul(_number,gameConfig_.ticketNum);
         require(addTicketNum > 0);
         uint256 ticket=player_[pid].ticket;
         player_[pid].ticket+=addTicketNum;
         require(sub(player_[pid].ticket,ticket) ==addTicketNum);
         gameInfo_.totalTicket=add(gameInfo_.totalTicket,addTicketNum);
    }
    
    
    function withdraw(uint256 pid) safe() external{
        require(playAddr_[msg.sender] == pid,'Error Action');
        require(player_[pid].addr == msg.sender,'Error Action');
        require(player_[pid].balance >0,'Insufficient balance');
        uint256 balance =player_[pid].balance;
        player_[pid].balance=0;
        return player_[pid].addr.transfer(balance);
    }
    
    function __callback(bytes32 myid, string result) public  {
            require (validQueryId[myid] == true);
            delete validQueryId[myid];
            require(msg.sender == oraclize_cbAddress(),'Error');
            require(now > gameInfo_.nextLottery,'Not yet in the draw time');
          __lottery(result);
    }
    
    /* The administrator opens the lottery program */
    function lottery(uint256 gwei,uint256 gasLimit) safe() external payable{
    
     require(msg.sender==admin__,'Only an admin can draw a lottery');
     require(now > gameInfo_.nextLottery,'Not yet in the draw time');
     require(gameInfo_.lotteryResult[gameInfo_.index].time==0);
     if(gameInfo_.lotteryResult[gameInfo_.index].betOfNumber<gameConfig_.minBetNum
     || gameInfo_.prizePool<=0
     ){
            gameInfo_.nextLottery=add(gameInfo_.nextLottery,gameConfig_.lotteryInterval);
           //  gameInfo_.nextLottery=now +600;
            emit Lottery(0,gameInfo_.nextLottery,gameInfo_.index);
            return;
     }
        uint256 _gasLimit=gasLimit;//CUSTOM_GASLIMIT;
        if(gasLimit==0 || gasLimit>3000000){
            _gasLimit=CUSTOM_GASLIMIT;//
         }
        uint256 _gwei;
       if(gwei==0 || gwei>50){
           _gwei=10100000000;//7.1GWEI
        }else{
           _gwei=mul(1000000000,gwei);
        }
      oraclize_setCustomGasPrice(_gwei);//Gwei
      uint256 pushPrice=oraclize.getPrice("URL",_gasLimit);
      require(address(this).balance>=pushPrice,'Oraclize query was NOT sent, please add some ETH to cover for the query fee');
       //Execute the lottery program
     bytes32 queryId =
        oraclize_query("URL", "html(https://www.random.org/integer-sets/?sets=1&num=4&min=1&max=9&order=random&format=plain&rnd=new).xpath(translate(normalize-space(text()),' ',''))",_gasLimit);
        gameInfo_.prizePool=sub(gameInfo_.prizePool,pushPrice);
       validQueryId[queryId]=true;
       betSwitch=false;//Close Bet
      
    }
    
   /* Lottery */
     function __lottery(string strNumber) private{
        
           uint256  _number=parseInt(strNumber);
           require(_number >=1234 && _number<=9876,'Error 11');
            
            uint256 _now=now;
            uint256 _index=gameInfo_.index;
            require(_now>gameInfo_.lotteryResult[_index-1].time,'Error 12');
          gameInfo_.nextLottery=add(gameInfo_.nextLottery,gameConfig_.lotteryInterval);
         // gameInfo_.nextLottery=_now + 600;
          lotteryRecord memory _gli=gameInfo_.lotteryResult[_index];
          _gli.number=_number;
          _gli.time=_now;
          _gli.index=gameInfo_.index;
          gameInfo_.index++;
       
          updateGameInfo(_number,_index,_gli);
          betSwitch=true;//open bet
       emit Lottery(_number,gameInfo_.nextLottery,gameInfo_.index);
    }
    
 function updateGameInfo(uint256 _number,uint256 index,lotteryRecord _gli) private{
        
        string memory  strNumber=uint2str(_number);
        string memory  secondPrize=subStr(strNumber,1,4);
        _gli.prize2number=parseInt(secondPrize);
        require(_gli.prize2number>100 && _gli.prize2number<999);
        string memory  thirdPrize=subStr(strNumber,2,4);
    
        _gli.prize3number=parseInt(thirdPrize);
        
        require( _gli.prize3number>10 &&  _gli.prize3number<99);
        //The prize pool of this Index
        uint256 indexPrizePool = safePercent(gameConfig_.lotteryPoolPct,gameInfo_.prizePool);
        require(indexPrizePool>0,'ERROR 1');
      
        uint256 prize1Pool=safePercent(gameConfig_.prize_1,indexPrizePool);
        uint256 prize2Pool=safePercent(gameConfig_.prize_2,indexPrizePool);
        uint256 prize3Pool=safePercent(gameConfig_.prize_3,indexPrizePool);
        
        require(add(add(prize1Pool,prize2Pool),prize3Pool)<=indexPrizePool,'ERROR 2');
        uint256 prize1Num=indexNumberTicket_[index][_number];
        uint256 prize2Num=indexNumberTicket_[index][_gli.prize2number];
        uint256 prize3Num=indexNumberTicket_[index][_gli.prize3number];
        uint256 actualCost=0;
        if(prize1Num>0){
          _gli.prize1Wei=div(prize1Pool,prize1Num);
          actualCost = prize1Pool;
        }
        if(prize2Num>0){
         _gli.prize2Wei=div(prize2Pool,prize2Num);
         actualCost = add(actualCost,prize2Pool);
        }
        if(prize3Num>0){
          _gli.prize3Wei=div(prize3Pool,prize3Num);
          actualCost = add(actualCost,prize3Pool);
        }
        //Bonus deduction
        gameInfo_.prizePool=sub(gameInfo_.prizePool,actualCost);
        gameInfo_.lotteryResult[index]=_gli;
    }
    
    function viewAwardInfo(uint256 _index) safe() external view 
    returns(uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool){
        
        uint256 pid=playAddr_[msg.sender];
        require(pid>0,'Error Action 2');
         uint256 index=_index;
         uint256 prize1Num=gameInfo_.lotteryResult[index].number;
         uint256 prize2Num= gameInfo_.lotteryResult[index].prize2number;
         uint256 prize3Num= gameInfo_.lotteryResult[index].prize3number;
        
        return(
              player_[pid].index[index].numberOfTickets[prize1Num],
              gameInfo_.lotteryResult[index].prize1Wei,
             player_[pid].index[index].numberOfTickets[prize2Num],
             gameInfo_.lotteryResult[index].prize2Wei,
             player_[pid].index[index].numberOfTickets[prize3Num],
             gameInfo_.lotteryResult[index].prize3Wei,
             player_[pid].index[index].betTickets,
             player_[pid].index[index].isReceive
            );
    }
    
    //Receive your own bonus
    function receiveAward(uint256 index) safe() external{
        
        uint256 pid=playAddr_[msg.sender];
        require(pid>0,'Error Action 2');
        
        lotteryRecord storage _gli=gameInfo_.lotteryResult[index];
        
        require(_gli.time > 0,'Error Action 3');
         playerRecord storage _pi=player_[pid].index[index];
        require(_pi.isReceive==false,'Error Action 4');
        
        _pi.isReceive=true;
        
        
        uint256 prize1Num=_gli.number;
        uint256 sendWei=0;
        
        if(_pi.numberOfTickets[prize1Num] > 0){
            
          sendWei = mul(_gli.prize1Wei,_pi.numberOfTickets[prize1Num]);
        }
        
         uint256 prize2Num= _gli.prize2number;
        
        if(_pi.numberOfTickets[prize2Num]> 0){
            sendWei = add(sendWei,mul(_gli.prize2Wei,_pi.numberOfTickets[prize2Num]));
        }
        
        uint256 prize3Num= _gli.prize3number;
        
        if(player_[pid].index[index].numberOfTickets[prize3Num]> 0){
             sendWei = add(sendWei,mul(_gli.prize3Wei,_pi.numberOfTickets[prize3Num]));
         }
            require(sendWei>0,'Error Action 5');
            uint256 adminAmount=safePercent(gameConfig_.receiveAwardPct,sendWei);
            sendWei=sub(sendWei,adminAmount);
            uint256 adminId=player_[playAddr_[admin__]].id;
            player_[adminId].balance=add(player_[adminId].balance,adminAmount);
        player_[pid].addr.transfer(sendWei);
    }
 
    
    function getLotteryInfo(uint256 index)  external view 
    returns(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256){
              
                 uint256 showIndex=index;
              if(index<=0 || index > gameInfo_.index)
                 showIndex=gameInfo_.index;
                
                
             if(showIndex >1 && gameInfo_.lotteryResult[showIndex].time==0)
                showIndex--;
                
        return(
                gameInfo_.index,
                gameInfo_.nextLottery,
                gameInfo_.lotteryResult[showIndex].number,
                gameInfo_.lotteryResult[showIndex].betOfNumber,
                gameInfo_.prizePool,
                0,
                indexNumberTicket_[showIndex][gameInfo_.lotteryResult[showIndex].number],
                 gameInfo_.lotteryResult[showIndex].prize1Wei,
                indexNumberTicket_[showIndex][gameInfo_.lotteryResult[showIndex].prize2number],
                 gameInfo_.lotteryResult[showIndex].prize2Wei,
                 indexNumberTicket_[showIndex][gameInfo_.lotteryResult[showIndex].prize3number],
                 gameInfo_.lotteryResult[showIndex].prize3Wei,
                 gameInfo_.lotteryResult[showIndex].time
            );
}
    
  
   
     //2020.01.01 Close Game Used to update the game
   function closeGame() external safe() {
        uint256 closeTime=1577808000;
        require(now > closeTime,'Time has not arrived');
        require(msg.sender == admin__,'Error');
        selfdestruct(admin__);
    }
    
    
    function getPlayId(address addr) private returns(uint256){
        if(address(0) ==addr)
            return 0;
        if(playAddr_[addr] >0){
         return playAddr_[addr];
        }
              autoPlayId_++;
              playAddr_[addr]=autoPlayId_;
              player memory _p;
              _p.id=autoPlayId_;
              _p.addr=addr;
              _p.timeStamp=now;
              player_[autoPlayId_]=_p;
              return autoPlayId_;
   }

}
function closeGame() external safe() {
     uint256 closeTime=1577808000;
     require(now > closeTime,'Time has not arrived');
     require(msg.sender == admin__,'Error');
     selfdestruct(admin__);
 }

//2020.01.01 Close Game Used to update the game