pragma solidity ^0.4.25;

interface DSG {
    function gamingDividendsReception() payable external;
}

contract TurntableDSG{
    using SafeMath for uint256;
    
    address constant public DSG_ADDRESS = 0x696826C18A6Bc9Be4BBfe3c3A6BB9f5a69388687;
    uint256 public totalDividends;
    uint256 public totalWinnings;
    uint256 public totalTurnover;
    uint256 public maxBet;
    uint256 public minBet;
    uint256 public minContractBalance;
    uint256 public minBetForJackpot;
    uint256 public jackpotBalance;
    uint256 public nextPayout;
    uint256 public ownerDeposit;
    address[2] public owners;
    address[2] public candidates;
    bool public paused;
    
    mapping (address => Bet) public usersBets;
    
    struct Bet {
        uint256 blockNumber;
        uint256 bet;
    }
    
    modifier onlyOwners(){
        require(msg.sender == owners[0] || msg.sender == owners[1]);
        _;
    }
    modifier checkBlockNumber(){
        uint256 blockNumber = usersBets[msg.sender].blockNumber;
        if(block.number.sub(blockNumber) >= 250 && blockNumber > 0){
            emit Result(msg.sender, 1000, 0, jackpotBalance, usersBets[msg.sender].bet, 0);
            delete usersBets[msg.sender];
        }
        else{
            _;
        }
    }
    constructor(address secondOwner) public payable{
        owners[0]   = msg.sender;
        owners[1]   = secondOwner;
        ownerDeposit   = msg.value;
        jackpotBalance = jackpotBalance.add(ownerDeposit.div(1000));
    }
    function play() public payable checkBlockNumber{
        uint256 bet = msg.value;
        require(checkSolvency(bet), "Not enough ETH in contract");
        require(paused == false, "Game was stopped");
        require(bet >= minBet && bet <= maxBet, "Amount should be within range");
        require(usersBets[msg.sender].bet == 0, "You have already bet");
        usersBets[msg.sender].bet = bet;
        usersBets[msg.sender].blockNumber = block.number;
        totalTurnover = totalTurnover.add(bet);
        emit PlaceBet(msg.sender, bet, now);
    }
    function result() public checkBlockNumber{
        require(blockhash(usersBets[msg.sender].blockNumber) != 0, "Your time to determine the result has come out or not yet come");
        uint256 bet = usersBets[msg.sender].bet;
        uint256 totalWinAmount;
        uint256 r = _random(1000, 0);
        uint256 winRate = 0;
        if(_winChanceJ(r, bet)){
		    winRate = 1000;
            totalWinAmount = totalWinAmount.add(jackpotBalance);
            emit Jackpot(msg.sender, jackpotBalance, now);
            delete jackpotBalance;
		}
		if(_winChance1x(r)){
		    winRate = 100;
		    totalWinAmount = totalWinAmount.add(bet);
		}
		if(_winChance1_5x(r)){
		    winRate = 150;
		    totalWinAmount = totalWinAmount.add(bet.mul(winRate).div(100));
		}
		if(_winChance2x(r)){
		    winRate = 200;
		    totalWinAmount = totalWinAmount.add(bet.mul(winRate).div(100));
		}
		if(_winChance2_5x(r)){
		    winRate = 250;
		    totalWinAmount = totalWinAmount.add(bet.mul(winRate).div(100));
		}
		if(_winChance3x(r)){
		    winRate = 300;
		    totalWinAmount = totalWinAmount.add(bet.mul(winRate).div(100));
		}
		if(_winChance5x(r)){
		    winRate = 500;
		    totalWinAmount = totalWinAmount.add(bet.mul(winRate).div(100));
		}
		if(totalWinAmount > 0){
            msg.sender.transfer(totalWinAmount);
            totalWinnings = totalWinnings.add(totalWinAmount);
        }
        jackpotBalance = jackpotBalance.add(bet.div(1000));
        delete usersBets[msg.sender];
        emit Result(msg.sender, r, totalWinAmount, jackpotBalance, bet, winRate);
    }
    function _winChanceJ(uint r, uint bet) private view returns(bool){
		if(bet >= minBetForJackpot && r == 999 && jackpotBalance > 0) return true;
		else return false;
	}
    function _winChance5x(uint r) private pure returns(bool){
		if(r == 11 || r == 21 || r == 31 || r == 41 || r == 51 || r == 61 || r == 71 || r == 81 || r == 91 || r == 99) return true;
		else return false;
	}
	function _winChance3x(uint r) private pure returns(bool){
		if((r >= 80 && r < 83)    ||
			(r == 180 && r < 183) ||
			(r == 280 && r < 283) ||
			(r == 380 && r < 383) ||
			(r == 480 && r < 483) ||
			(r == 580 && r < 583) ||
			(r == 680 && r < 683) ||
			(r == 780 && r < 783) ||
			(r == 880 && r < 883) ||
			(r == 980 && r < 983))
		return true;
		else return false;
	}
	function _winChance2_5x(uint r) private pure returns(bool){
		if( (r >= 75 && r < 80)   ||
			(r >= 175 && r < 180) ||
			(r >= 275 && r < 280) ||
			(r >= 375 && r < 380) ||
			(r >= 475 && r < 480) ||
			(r >= 575 && r < 580) ||
			(r >= 675 && r < 680) ||
			(r >= 775 && r < 780)) {
			return true;
		}
		else{
			return false;
		}
	}
	function _winChance2x(uint r) private pure returns(bool){
		if( (r >= 50 && r < 75)   ||
			(r >= 350 && r < 375) ||
			(r >= 650 && r < 675) ||
			(r >= 950 && r < 975)) {
			return true;
		}
		else{
			return false;
		}
	}
	function _winChance1_5x(uint r) private pure returns(bool){
		if((r >= 25 && r < 50) || (r >= 125 && r < 150)){
			return true;
		}
		else if((r >= 225 && r < 250) || (r >= 325 && r < 350)){
			return true;
		}
		else if((r >= 425 && r < 450) || (r >= 525 && r < 550)){
			return true;
		}
		else if((r >= 625 && r < 650) || (r >= 725 && r < 750)){
			return true;
		}
		else{
			return false;
		}
	}
	function _winChance1x(uint r) private pure returns(bool){
		if((r >= 0 && r < 25) || (r >= 100 && r < 125)){
			return true;
		}
		else if((r >= 200 && r < 225) || (r >= 300 && r < 325)){
			return true;
		}
		else if((r >= 400 && r < 425) || (r >= 500 && r < 525)){
			return true;
		}
		else if((r >= 600 && r < 625) || (r >= 700 && r < 725)){
			return true;
		}
		else if((r >= 800 && r < 825) || (r >= 900 && r < 925)){
			return true;
		}
		else{
			return false;
		}
	}
    function checkSolvency(uint bet) view public returns(bool){
        if(getContractBalance() > bet.mul(500).div(100).add(jackpotBalance)) return true;
        else return false;
    }
    function sendDividends() public {
        require(getContractBalance() > minContractBalance && now > nextPayout, "You cannot send dividends");
        DSG DSG0 = DSG(DSG_ADDRESS);
        uint256 balance = getContractBalance();
        uint256 dividends  = balance.sub(minContractBalance);
        nextPayout         = now.add(7 days);
        totalDividends = totalDividends.add(dividends);
        DSG0.gamingDividendsReception.value(dividends)();
        emit Dividends(balance, dividends, now);
    }
     function getContractBalance() public view returns (uint256){
        return address(this).balance;
    }
    function _random(uint256 max, uint256 entropy) private view returns(uint256){
        bytes32 hash = blockhash(usersBets[msg.sender].blockNumber);
        return uint256(keccak256(abi.encode(hash, now, msg.sender, entropy))) % max;
    }
    function deposit() public payable onlyOwners{
        ownerDeposit = ownerDeposit.add(msg.value);
    }
    function sendOwnerDeposit(address recipient) public onlyOwners{
        require(paused == true, 'Game was not stopped');
        uint256 contractBalance = getContractBalance();
        if(contractBalance >= ownerDeposit){
            recipient.transfer(ownerDeposit);
        }
        else{
            recipient.transfer(contractBalance);
        }
        delete jackpotBalance;
        delete ownerDeposit;
    }
    function pauseGame(bool option) public onlyOwners{
        paused = option;
    }
    function setMinBet(uint256 eth) public onlyOwners{
        minBet = eth;
    }
    function setMaxBet(uint256 eth) public onlyOwners{
        maxBet = eth;
    }
    function setMinBetForJackpot(uint256 eth) public onlyOwners{
        minBetForJackpot = eth;
    }
    function setMinContractBalance(uint256 eth) public onlyOwners{
        minContractBalance = eth;
    }
    function transferOwnership(address newOwnerAddress, uint8 k) public onlyOwners{
        candidates[k] = newOwnerAddress;
    }
    function confirmOwner(uint8 k) public{
        require(msg.sender == candidates[k]);
        owners[k] = candidates[k];
    }
    event Dividends(
        uint256 balance,
        uint256 dividends,
        uint256 timestamp
    );
    event Jackpot(
        address indexed player,
        uint256 jackpot,
        uint256 timestamp
    );
    event PlaceBet(
        address indexed player,
        uint256 bet,
        uint256 timestamp
    );
    event Result(
        address indexed player,
        uint256 indexed random,
        uint256 totalWinAmount,
        uint256 jackpotBalance,
        uint256 bet,
        uint256 winRate
    );
}
library SafeMath {
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {  return 0; }
        uint256 c = a * b;
        require(c / a == b);
        return c;
    }
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0);
        uint256 c = a / b;
        return c;
    }
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a);
        uint256 c = a - b;
        return c;
    }
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a);
        return c;
    }
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0);
        return a % b;
    }
}