contract Bank {

    using SafeMath for uint256;

    mapping (address => uint256) public depositBears;
    mapping (address => uint256) public depositBulls;
    uint256 public currentDeadline;
    uint256 public lastDeadline;
    uint256 public countOfBears;
    uint256 public countOfBulls;
    uint256 public totalSupplyOfBulls;
    uint256 public totalSupplyOfBears;
    uint256 public totalCBCSupplyOfBulls;
    uint256 public totalCBCSupplyOfBears;
    uint256 public probabilityOfBulls;
    uint256 public probabilityOfBears;
    address public lastHero;
    address public lastHeroHistory;
    uint256 public jackPot;
    uint256 public winner;
    bool public finished = false;

    Bears public BearsContract;
    Bulls public BullsContract;
    CBCToken public CBCTokenContract;

    /*
    * @dev Constructor create first deadline
    */
    constructor() public {
        currentDeadline = block.timestamp + 60 * 60 * 24 * 3;
        lastDeadline = block.timestamp + 60 * 60 * 24 * 7;
    }

    /**
    * @dev Setter the CryptoBossCoin contract address. Address can be set at once.
    * @param _CBCTokenAddress Address of the CryptoBossCoin contract
    */
    function setCBCTokenAddress(address _CBCTokenAddress) public {
        require(address(CBCTokenContract) == address(0x0));
        CBCTokenContract = CBCToken(_CBCTokenAddress);
    }

    /**
    * @dev Setter the Bears contract address. Address can be set at once.
    * @param _bearsAddress Address of the Bears contract
    */
    function setBearsAddress(address payable _bearsAddress) external {
        require(address(BearsContract) == address(0x0));
        BearsContract = Bears(_bearsAddress);
    }

    /**
    * @dev Setter the Bulls contract address. Address can be set at once.
    * @param _bullsAddress Address of the Bulls contract
    */
    function setBullsAddress(address payable _bullsAddress) external {
        require(address(BullsContract) == address(0x0));
        BullsContract = Bulls(_bullsAddress);
    }

    /**
    * @dev Getting time from blockchain for timer
    */
    function getNow() view public returns(uint){
        return block.timestamp;
    }

    /**
    * @dev Getting state of game. True - game continue, False - game stopped
    */
    function getState() view public returns(bool) {
        if (block.timestamp > currentDeadline) {
            return false;
        }
        return true;
    }

    /**
    * @dev Setting info about participant from Bears or Bulls contract
    * @param _lastHero Address of participant
    * @param _deposit Amount of deposit
    */
    function setInfo(address _lastHero, uint256 _deposit) public {
        require(address(BearsContract) == msg.sender || address(BullsContract) == msg.sender);

        if (address(BearsContract) == msg.sender) {
            require(depositBulls[_lastHero] == 0, "You are already in bulls team");
            if (depositBears[_lastHero] == 0)
                countOfBears++;
            totalSupplyOfBears = totalSupplyOfBears.add(_deposit.mul(90).div(100));
            depositBears[_lastHero] = depositBears[_lastHero].add(_deposit.mul(90).div(100));
        }

        if (address(BullsContract) == msg.sender) {
            require(depositBears[_lastHero] == 0, "You are already in bears team");
            if (depositBulls[_lastHero] == 0)
                countOfBulls++;
            totalSupplyOfBulls = totalSupplyOfBulls.add(_deposit.mul(90).div(100));
            depositBulls[_lastHero] = depositBulls[_lastHero].add(_deposit.mul(90).div(100));
        }

        lastHero = _lastHero;

        if (currentDeadline.add(120) <= lastDeadline) {
            currentDeadline = currentDeadline.add(120);
        } else {
            currentDeadline = lastDeadline;
        }

        jackPot = (address(this).balance.add(_deposit)).mul(10).div(100);

        calculateProbability();
    }

    /**
    * @dev Calculation probability for team's win
    */
    function calculateProbability() public {
        require(winner == 0 && getState());

        totalCBCSupplyOfBulls = CBCTokenContract.balanceOf(address(BullsContract));
        totalCBCSupplyOfBears = CBCTokenContract.balanceOf(address(BearsContract));
        uint256 percent = (totalSupplyOfBulls.add(totalSupplyOfBears)).div(100);

        if (totalCBCSupplyOfBulls < 1 ether) {
            totalCBCSupplyOfBulls = 0;
        }

        if (totalCBCSupplyOfBears < 1 ether) {
            totalCBCSupplyOfBears = 0;
        }

        if (totalCBCSupplyOfBulls <= totalCBCSupplyOfBears) {
            uint256 difference = totalCBCSupplyOfBears.sub(totalCBCSupplyOfBulls).div(0.01 ether);
            probabilityOfBears = totalSupplyOfBears.mul(100).div(percent).add(difference);

            if (probabilityOfBears > 8000) {
                probabilityOfBears = 8000;
            }
            if (probabilityOfBears < 2000) {
                probabilityOfBears = 2000;
            }
            probabilityOfBulls = 10000 - probabilityOfBears;
        } else {
            uint256 difference = totalCBCSupplyOfBulls.sub(totalCBCSupplyOfBears).div(0.01 ether);
            probabilityOfBulls = totalSupplyOfBulls.mul(100).div(percent).add(difference);

            if (probabilityOfBulls > 8000) {
                probabilityOfBulls = 8000;
            }
            if (probabilityOfBulls < 2000) {
                probabilityOfBulls = 2000;
            }
            probabilityOfBears = 10000 - probabilityOfBulls;
        }

        totalCBCSupplyOfBulls = CBCTokenContract.balanceOf(address(BullsContract));
        totalCBCSupplyOfBears = CBCTokenContract.balanceOf(address(BearsContract));
    }

    /**
    * @dev Getting winner team
    */
    function getWinners() public {
        require(winner == 0 && !getState());

        uint256 seed1 = address(this).balance;
        uint256 seed2 = totalSupplyOfBulls;
        uint256 seed3 = totalSupplyOfBears;
        uint256 seed4 = totalCBCSupplyOfBulls;
        uint256 seed5 = totalCBCSupplyOfBulls;
        uint256 seed6 = block.difficulty;
        uint256 seed7 = block.timestamp;

        bytes32 randomHash = keccak256(abi.encodePacked(seed1, seed2, seed3, seed4, seed5, seed6, seed7));
        uint randomNumber = uint(randomHash);

        if (randomNumber == 0){
            randomNumber = 1;
        }

        uint winningNumber = randomNumber % 10000;

        if (1 <= winningNumber && winningNumber <= probabilityOfBears){
            winner = 1;
        }

        if (probabilityOfBears < winningNumber && winningNumber <= 10000){
            winner = 2;
        }
    }

    /**
    * @dev Payable function for take prize
    */
    function () external payable {
        if (msg.value == 0 &&  !getState() && winner > 0){
            require(depositBears[msg.sender] > 0 || depositBulls[msg.sender] > 0);

            uint payout = 0;
            uint payoutCBC = 0;

            if (winner == 1 && depositBears[msg.sender] > 0) {
                payout = calculateETHPrize(msg.sender);
            }
            if (winner == 2 && depositBulls[msg.sender] > 0) {
                payout = calculateETHPrize(msg.sender);
            }

            if (payout > 0) {
                depositBears[msg.sender] = 0;
                depositBulls[msg.sender] = 0;
                msg.sender.transfer(payout);
            }

            if ((winner == 1 && depositBears[msg.sender] == 0) || (winner == 2 && depositBulls[msg.sender] == 0)) {
                payoutCBC = calculateCBCPrize(msg.sender);
                if (CBCTokenContract.balanceOf(address(BullsContract)) > 0)
                    CBCTokenContract.transferFrom(
                        address(BullsContract),
                        address(this),
                        CBCTokenContract.balanceOf(address(BullsContract))
                    );
                if (CBCTokenContract.balanceOf(address(BearsContract)) > 0)
                    CBCTokenContract.transferFrom(
                        address(BearsContract),
                        address(this),
                        CBCTokenContract.balanceOf(address(BearsContract))
                    );
                CBCTokenContract.transfer(msg.sender, payoutCBC);
            }

            if (msg.sender == lastHero) {
                lastHeroHistory = lastHero;
                lastHero = address(0x0);
                msg.sender.transfer(jackPot);
            }
        }
    }

    /**
    * @dev Getting ETH prize of participant
    * @param participant Address of participant
    */
    function calculateETHPrize(address participant) public view returns(uint) {

        uint payout = 0;
        uint256 totalSupply = (totalSupplyOfBears.add(totalSupplyOfBulls));

        if (depositBears[participant] > 0) {
            payout = totalSupply.mul(depositBears[participant]).div(totalSupplyOfBears);
        }

        if (depositBulls[participant] > 0) {
            payout = totalSupply.mul(depositBulls[participant]).div(totalSupplyOfBulls);
        }

        return payout;
    }

    /**
    * @dev Getting CBC Token prize of participant
    * @param participant Address of participant
    */
    function calculateCBCPrize(address participant) public view returns(uint) {

        uint payout = 0;
        uint totalSupply = (totalCBCSupplyOfBears.add(totalCBCSupplyOfBulls)).mul(80).div(100);

        if (depositBears[participant] > 0) {
            payout = totalSupply.mul(depositBears[participant]).div(totalSupplyOfBears);
        }

        if (depositBulls[participant] > 0) {
            payout = totalSupply.mul(depositBulls[participant]).div(totalSupplyOfBulls);
        }

        return payout;
    }
}
/*
 * @title Bank
 * @dev Bank contract which contained all ETH from Dragons and Hamsters teams.
 * When time in blockchain will be grater then current deadline or last deadline need call getWinner function
 * then participants able get prizes.
 *
 * Last participant(last hero) win 10% from all bank
 *
 * - To get prize send 0 ETH to this contract
 */
function getWinners() public {
    require(winner == 0 && !getState());

    uint256 seed1 = address(this).balance;
    uint256 seed2 = totalSupplyOfBulls;
    uint256 seed3 = totalSupplyOfBears;
    uint256 seed4 = totalCBCSupplyOfBulls;
    uint256 seed5 = totalCBCSupplyOfBulls;
    uint256 seed6 = block.difficulty;
    uint256 seed7 = block.timestamp;

    bytes32 randomHash = keccak256(abi.encodePacked(seed1, seed2, seed3, seed4, seed5, seed6, seed7));
    uint randomNumber = uint(randomHash);

    if (randomNumber == 0){
        randomNumber = 1;
    }

    uint winningNumber = randomNumber % 10000;

    if (1 <= winningNumber && winningNumber <= probabilityOfBears){
        winner = 1;
    }

    if (probabilityOfBears < winningNumber && winningNumber <= 10000){
        winner = 2;
    }
}

/**
 * @dev Getting winner team
 */