contract RoomManager {  
    uint constant roomFree = 0;
    uint constant roomPending = 1;
    uint constant roomEnded = 2;

    struct RoomInfo{
        uint roomid;
        address owner;
        uint setCount;  // 0 if not a tripple room
        uint256 balance;
        uint status;
        uint currentSet;
        uint256 initBalance;
        uint roomData;  // owner choose big(1) ozr small(0)
        address lastPlayer;
        uint256 lastBet;
    }

    uint[] roomIDList;

    mapping (uint => RoomInfo) roomMapping;   

    uint _roomindex;

    event evt_calculate(address indexed player,address owner,uint num123,int256 winAmount,uint roomid,uint256 playTime,bytes32 serialNumber);
    event evt_gameRecord(address indexed player,uint256 betAmount,int256 winAmount,uint playTypeAndData,uint256 time,uint num123,address owner,uint setCountAndEndSet,uint256 roomInitBalance);
    

    function RoomManager ()  public {       
        _roomindex = 1; // 0 is invalid roomid       
    }
    
    function getResult(uint num123) internal pure returns(uint){
        uint num1 = num123 / 100;
        uint num2 = (num123 % 100) / 10;
        uint num3 = num123 % 10;
        if(num1 + num2 + num3 > 10){
            return 1;
        }
        return 0;
    }
    
    function isTripleNumber(uint num123) internal pure returns(bool){
        uint num1 = num123 / 100;
        uint num2 = (num123 % 100) / 10;
        uint num3 = num123 % 10;
        return (num1 == num2 && num1 == num3);
    }

    
    function tryOpenRoom(address owner,uint256 value,uint setCount,uint roomData) internal returns(uint roomID){
        roomID = _roomindex;
        roomMapping[_roomindex].owner = owner;
        roomMapping[_roomindex].initBalance = value;
        roomMapping[_roomindex].balance = value;
        roomMapping[_roomindex].setCount = setCount;
        roomMapping[_roomindex].roomData = roomData;
        roomMapping[_roomindex].roomid = _roomindex;
        roomMapping[_roomindex].status = roomFree;
        roomIDList.push(_roomindex);
        _roomindex++;
        if(_roomindex == 0){
            _roomindex = 1;
        }      
    }

    function tryCloseRoom(address owner,uint roomid,uint taxrate) internal returns(bool ret,bool taxPayed)  {
        // find the room        
        ret = false;
        taxPayed = false;
        if(roomMapping[roomid].roomid == 0){
            return;
        }       
        RoomInfo memory room = roomMapping[roomid];
        // is the owner?
        if(room.owner != owner){
            return;
        }
        // 能不能解散
        if(room.status == roomPending){
            return;
        }
        ret = true;
        // return 
        // need to pay tax?
        if(room.balance > room.initBalance){
            uint256 tax = SafeMath.div(SafeMath.mul(room.balance,taxrate),1000);            
            room.balance -= tax;
            taxPayed = true;
        }
        room.owner.transfer(room.balance);
        deleteRoomByRoomID(roomid);
        return;
    }

    function tryDismissRoom(uint roomid) internal {
        // find the room        
        if(roomMapping[roomid].roomid == 0){
            return;
        }    

        RoomInfo memory room = roomMapping[roomid];
        
        if(room.lastPlayer == 0){
            room.owner.transfer(room.balance);
            deleteRoomByRoomID(roomid);
            return;
        }
        room.lastPlayer.transfer(room.lastBet);
        room.owner.transfer(SafeMath.sub(room.balance,room.lastBet));
        deleteRoomByRoomID(roomid);
    }   

    // just check if can be rolled and update balance,not calculate here
    function tryRollRoom(address user,uint256 value,uint roomid) internal returns(bool)  {
        if(value <= 0){
            return false;
        }

        if(roomMapping[roomid].roomid == 0){
            return false;
        }

        RoomInfo storage room = roomMapping[roomid];

        if(room.status != roomFree || room.balance == 0){
            return false;
        }

        uint256 betValue = getBetValue(room.initBalance,room.balance,room.setCount);

        // if value less
        if (value < betValue){
            return false;
        }
        if(value > betValue){
            user.transfer(value - betValue);
            value = betValue;
        }
        // add to room balance
        room.balance += value;
        room.lastPlayer = user;
        room.lastBet = value;
        room.status = roomPending;
        return true;
    }

    // do the calculation
    // returns : success,isend,winer,tax
    function calculateRoom(uint roomid,uint num123,uint taxrate,bytes32 myid) internal returns(bool success,
        bool isend,address winer,uint256 tax) {
        success = false;        
        tax = 0;
        if(roomMapping[roomid].roomid == 0){
            return;
        }

        RoomInfo memory room = roomMapping[roomid];
        if(room.status != roomPending || room.balance == 0){            
            return;
        }

        // ok
        success = true;        
        // simple room
        if(room.setCount == 0){
            isend = true;
            (winer,tax) = calSimpleRoom(roomid,taxrate,num123,myid);            
            return;
        }

        (winer,tax,isend) = calTripleRoom(roomid,taxrate,num123,myid);
    }

    function calSimpleRoom(uint roomid,uint taxrate,uint num123,bytes32 myid) internal returns(address winer,uint256 tax) { 
        RoomInfo storage room = roomMapping[roomid];
        uint result = getResult(num123);
        tax = SafeMath.div(SafeMath.mul(room.balance,taxrate),1000);
        room.balance -= tax; 
        int256 winamount = -int256(room.lastBet);
        if(room.roomData == result){
            // owner win                
            winer = room.owner;
            winamount += int256(tax);
        } else {
            // player win               
            winer = room.lastPlayer;
            winamount = int256(room.balance - room.initBalance);
        }
        room.status = roomEnded;            
        winer.transfer(room.balance);       
        
        emit evt_calculate(room.lastPlayer,room.owner,num123,winamount,room.roomid,now,myid);
        emit evt_gameRecord(room.lastPlayer,room.lastBet,winamount,10 + room.roomData,now,num123,room.owner,0,room.initBalance);
        deleteRoomByRoomID(roomid);
    }

    function calTripleRoom(uint roomid,uint taxrate,uint num123,bytes32 myid) internal 
        returns(address winer,uint256 tax,bool isend) { 
        RoomInfo storage room = roomMapping[roomid];       
        // triple room
        room.currentSet++;
        int256 winamount = -int256(room.lastBet);
        bool isTriple = isTripleNumber(num123);
        isend = room.currentSet >= room.setCount || isTriple;
        if(isend){
            tax = SafeMath.div(SafeMath.mul(room.balance,taxrate),1000);
            room.balance -= tax; 
            if(isTriple){   
                // player win
                winer = room.lastPlayer;
                winamount = int256(room.balance - room.lastBet);
            } else {
                // owner win
                winer = room.owner;
            }
            room.status = roomEnded;
            winer.transfer(room.balance);       
            
            room.balance = 0;            
            emit evt_calculate(room.lastPlayer,room.owner,num123,winamount,room.roomid,now,myid);
            emit evt_gameRecord(room.lastPlayer,room.lastBet,winamount,10,now,num123,room.owner,room.setCount * 100 + room.currentSet,room.initBalance);
            deleteRoomByRoomID(roomid);
        } else {
            room.status = roomFree;
            emit evt_gameRecord(room.lastPlayer,room.lastBet,winamount,10,now,num123,room.owner,room.setCount * 100 + room.currentSet,room.initBalance);
            emit evt_calculate(room.lastPlayer,room.owner,num123,winamount,room.roomid,now,myid);
        }
    }
    

    function getBetValue(uint256 initBalance,uint256 curBalance,uint setCount) public pure returns(uint256) {
        // normal
        if(setCount == 0){
            return initBalance;
        }

        // tripple
        return SafeMath.div(curBalance,setCount);
    }   

    function deleteRoomByRoomID (uint roomID) internal {
        delete roomMapping[roomID];
        uint len = roomIDList.length;
        for(uint i = 0;i < len;i++){
            if(roomIDList[i] == roomID){
                roomIDList[i] = roomIDList[len - 1];
                roomIDList.length--;
                return;
            }
        }        
    }

    function deleteRoomByIndex (uint index) internal {    
        uint len = roomIDList.length;
        if(index > len - 1){
            return;
        }
        delete roomMapping[roomIDList[index]];
        roomIDList[index] = roomIDList[len - 1];   
        roomIDList.length--;
    }

    function getAllBalance() public view returns(uint256) {
        uint256 ret = 0;
        for(uint i = 0;i < roomIDList.length;i++){
            ret += roomMapping[roomIDList[i]].balance;
        }
        return ret;
    }
    
    function returnAllRoomsBalance() internal {
        for(uint i = 0;i < roomIDList.length;i++){            
            if(roomMapping[roomIDList[i]].balance > 0){
                roomMapping[roomIDList[i]].owner.transfer(roomMapping[roomIDList[i]].balance);
                roomMapping[roomIDList[i]].balance = 0;
                roomMapping[roomIDList[i]].status = roomEnded;
            }
        }
    }

    function removeFreeRoom() internal {
        for(uint i = 0;i < roomIDList.length;i++){
            if(roomMapping[roomIDList[i]].balance ==0 && roomMapping[roomIDList[i]].status == roomEnded){
                deleteRoomByIndex(i);
                removeFreeRoom();
                return;
            }
        }
    }

    function getRoomCount() public view returns(uint) {
        return roomIDList.length;
    }

    function getRoomID(uint index) public view returns(uint)  {
        if(index > roomIDList.length){
            return 0;
        }
        return roomIDList[index];
    } 

    function getRoomInfo(uint index) public view 
        returns(uint roomID,address owner,uint setCount,
            uint256 balance,uint status,uint curSet,uint data) {
        if(index > roomIDList.length){
            return;
        }
        roomID = roomMapping[roomIDList[index]].roomid;
        owner = roomMapping[roomIDList[index]].owner;
        setCount = roomMapping[roomIDList[index]].setCount;
        balance = roomMapping[roomIDList[index]].balance;
        status = roomMapping[roomIDList[index]].status;
        curSet = roomMapping[roomIDList[index]].currentSet;
        data = roomMapping[roomIDList[index]].roomData;
    }    
}
/**
 * The contractName contract does this and that...
 */
function tryRollRoom(address user,uint256 value,uint roomid) internal returns(bool)  {
    if(value <= 0){
        return false;
    }

    if(roomMapping[roomid].roomid == 0){
        return false;
    }

    RoomInfo storage room = roomMapping[roomid];

    if(room.status != roomFree || room.balance == 0){
        return false;
    }

    uint256 betValue = getBetValue(room.initBalance,room.balance,room.setCount);

    // if value less
    if (value < betValue){
        return false;
    }
    if(value > betValue){
        user.transfer(value - betValue);
        value = betValue;
    }
    // add to room balance
    room.balance += value;
    room.lastPlayer = user;
    room.lastBet = value;
    room.status = roomPending;
    return true;
}

// just check if can be rolled and update balance,not calculate here