contract DiceOffline is Config,RoomManager,UserManager {
    // 事件
    event withdraw_failed();
    event withdraw_succeeded(address toUser,uint256 value);    
    event bet_failed(address indexed player,uint256 value,uint result,uint roomid,uint errorcode);
    event bet_succeeded(address indexed player,uint256 value,uint result,uint roomid,bytes32 serialNumber);    
    event evt_createRoomFailed(address indexed player);
    event evt_createRoomSucceeded(address indexed player,uint roomid);
    event evt_closeRoomFailed(address indexed player,uint roomid);
    event evt_closeRoomSucceeded(address indexed player,uint roomid);

    // 下注信息
    struct BetInfo{
        address player;
        uint result;
        uint256 value;  
        uint roomid;       
    }

    mapping (bytes32 => BetInfo) rollingBet;
    uint256 public allWagered;
    uint256 public allWon;
    uint    public allPlayCount;

    function DiceOffline() public{        
    }  
   
    
    // 销毁合约
    function destroy() onlyOwner public{     
        returnAllRoomsBalance();
        selfdestruct(owner);
    }

    // 充值
    function () public payable {        
    }

    // 提现
    function withdraw(uint256 value) public onlyOwner{
        if(getAvailableBalance() < value){
            emit withdraw_failed();
            return;
        }
        owner.transfer(value);  
        emit withdraw_succeeded(owner,value);
    }

    // 获取可提现额度
    function getAvailableBalance() public view returns (uint256){
        return SafeMath.sub(getBalance(),getAllBalance());
    }

    function rollSystem (uint result,address referral) public payable returns(bool) {
        if(msg.value == 0){
            return;
        }
        BetInfo memory bet = BetInfo(msg.sender,result,msg.value,0);
       
        if(bet.value < minStake){
            bet.player.transfer(bet.value);
            emit bet_failed(bet.player,bet.value,result,0,0);
            return false;
        }

        uint256 maxBet = getAvailableBalance() / 10;
        if(maxBet > maxStake){
            maxBet = maxStake;
        }

        if(bet.value > maxBet){
            bet.player.transfer(SafeMath.sub(bet.value,maxBet));
            bet.value = maxBet;
        }
      
        allWagered += bet.value;
        allPlayCount++;

        addBet(msg.sender,bet.value);
        setReferral(msg.sender,referral);        
        // 生成随机数
        bytes32 serialNumber = doOraclize(true);
        rollingBet[serialNumber] = bet;
        emit bet_succeeded(bet.player,bet.value,result,0,serialNumber);        
        return true;
    }   

    // 如果setCount为0，表示大小
    function openRoom(uint setCount,uint roomData,address referral) public payable returns(bool) {
        if(setCount > 0 && (setCount > maxSet || setCount < minSet)){
            emit evt_createRoomFailed(msg.sender);
            msg.sender.transfer(msg.value);
            return false;
        }
        uint256 minValue = normalRoomMin;
        uint256 maxValue = normalRoomMax;
        if(setCount > 0){
            minValue = tripleRoomMin;
            maxValue = tripleRoomMax;
        }

        if(msg.value < minValue || msg.value > maxValue){
            emit evt_createRoomFailed(msg.sender);
            msg.sender.transfer(msg.value);
            return false;
        }

        allWagered += msg.value;

        uint roomid = tryOpenRoom(msg.sender,msg.value,setCount,roomData);
        setReferral(msg.sender,referral);
        addOpenRoomCount(msg.sender);

        emit evt_createRoomSucceeded(msg.sender,roomid); 
    }

    function closeRoom(uint roomid) public returns(bool) {        
        bool ret = false;
        bool taxPayed = false;        
        (ret,taxPayed) = tryCloseRoom(msg.sender,roomid,taxRate);
        if(!ret){
            emit evt_closeRoomFailed(msg.sender,roomid);
            return false;
        }
        
        emit evt_closeRoomSucceeded(msg.sender,roomid);

        if(!taxPayed){
            subOpenRoomCount(msg.sender);
        }
        
        return true;
    }    

    function rollRoom(uint roomid,address referral) public payable returns(bool) {
        bool ret = tryRollRoom(msg.sender,msg.value,roomid);
        if(!ret){
            emit bet_failed(msg.sender,msg.value,0,roomid,0);
            msg.sender.transfer(msg.value);
            return false;
        }        
        
        BetInfo memory bet = BetInfo(msg.sender,0,msg.value,roomid);

        allWagered += bet.value;
        allPlayCount++;
       
        setReferral(msg.sender,referral);
        addBet(msg.sender,bet.value);
        // 生成随机数
        bytes32 serialNumber = doOraclize(false);
        rollingBet[serialNumber] = bet;
        emit bet_succeeded(msg.sender,msg.value,0,roomid,serialNumber);       
        return true;
    }

    function dismissRoom(uint roomid) public onlyOwner {
        tryDismissRoom(roomid);
    } 

    function doOraclize(bool isSystem) internal returns(bytes32) {        
        uint256 random = uint256(keccak256(block.difficulty,now));
        return bytes32(random);       
    }

    /*TLSNotary for oraclize call 
    function offlineCallback(bytes32 myid) internal {
        uint num = uint256(keccak256(block.difficulty,now)) & 216;
        uint num1 = num % 6 + 1;
        uint num2 = (num / 6) % 6 + 1;
        uint num3 = (num / 36) % 6 + 1;
        doCalculate(num1 * 100 + num2 * 10 + num3,myid);  
    }*/

    function doCalculate(uint num123,bytes32 myid) internal {
        BetInfo memory bet = rollingBet[myid];   
        if(bet.player == 0){            
            return;
        }       
        
        if(bet.roomid == 0){    // 普通房间
            // 进行结算
            int256 winAmount = -int256(bet.value);
            if(bet.result == getResult(num123)){
                uint256 tax = (bet.value + bet.value) * taxRate / 1000;                
                winAmount = int256(bet.value - tax);
                addWin(bet.player,uint256(winAmount));
                bet.player.transfer(bet.value + uint256(winAmount));
                fundReferrel(bet.player,tax * referrelFund / 1000);
                allWon += uint256(winAmount);
            }
            //addGameRecord(bet.player,bet.value,winAmount,bet.result,num123,0x0,0,0);
            emit evt_calculate(bet.player,0x0,num123,winAmount,0,now,myid);
            emit evt_gameRecord(bet.player,bet.value,winAmount,bet.result,now,num123,0x0,0,0);
            delete rollingBet[myid];
            return;
        }
        
        doCalculateRoom(num123,myid);
    }

    function doCalculateRoom(uint num123,bytes32 myid) internal {
        // 多人房间
        BetInfo memory bet = rollingBet[myid];         
       
        bool success;
        bool isend;
        address winer;
        uint256 tax;     

        (success,isend,winer,tax) = calculateRoom(bet.roomid,num123,taxRate,myid);
        delete rollingBet[myid];
        if(!success){            
            return;
        }

        if(isend){
            addWin(winer,tax * 1000 / taxRate);
            fundReferrel(winer,SafeMath.div(SafeMath.mul(tax,referrelFund),1000));            
        }        
    }
  
    function getBalance() public view returns(uint256){
        return address(this).balance;
    }
}
function getAvailableBalance() public view returns (uint256){
    return SafeMath.sub(getBalance(),getAllBalance());
}

// 获取可提现额度