contract ChronosCore is ChronosFinance {
    
    function ChronosCore(uint256 _price, uint256 _timeout, uint256 _minimumTimeout, uint256 _numberOfWagersToMinimumTimeout) public {
        require(_timeout >= _minimumTimeout);
        
        nextPrice = _price;
        nextTimeout = _timeout;
        nextMinimumTimeout = _minimumTimeout;
        nextNumberOfWagersToMinimumTimeout = _numberOfWagersToMinimumTimeout;
        NextGame(nextPrice, nextTimeout, nextMinimumTimeout, nextNumberOfWagersToMinimumTimeout);
    }
    
    event NextGame(uint256 price, uint256 timeout, uint256 minimumTimeout, uint256 numberOfWagersToMinimumTimeout);
    event Start(address indexed starter, uint256 timestamp, uint256 price, uint256 timeout, uint256 minimumTimeout, uint256 numberOfWagersToMinimumTimeout);
    event End(address indexed winner, uint256 timestamp, uint256 prize);
    event Play(address indexed player, uint256 timestamp, uint256 timeoutTimestamp, uint256 wagerIndex, uint256 newPrizePool);
    
    /// @notice Participate in the game.
    /// @param startNewGameIfIdle Start a new game if the current game is idle.
    function play(bool startNewGameIfIdle) external payable {
        // Check to see if the game should end. Process payment.
        _processGameEnd();
        
        if (!gameStarted) {
            // If the game is not started, the contract must not be paused.
            require(!paused);
            
            // If the game is not started, the player must be willing to start
            // a new game.
            require(startNewGameIfIdle);
            
            // Set the price and timeout.
            price = nextPrice;
            timeout = nextTimeout;
            minimumTimeout = nextMinimumTimeout;
            numberOfWagersToMinimumTimeout = nextNumberOfWagersToMinimumTimeout;
            
            // Start the game.
            gameStarted = true;
            
            // Set the game starter.
            gameStarter = msg.sender;
            
            // Emit start event.
            Start(msg.sender, block.timestamp, price, timeout, minimumTimeout, numberOfWagersToMinimumTimeout);
        }
        
        // Enough Ether must be supplied.
        require(msg.value >= price);
        
        // Calculate the fees and dividends.
        uint256 fee = price.mul(feePercentage).div(100000);
        uint256 dividend = price.mul(gameStarterDividendPercentage).div(100000);
        uint256 wagerPoolPart = price.mul(2).div(7);
        
        // Calculate the timeout.
        uint256 currentTimeout = calculateTimeout();
        
        // Set the last player, timestamp, timeout timestamp, and increase prize.
        lastPlayer = msg.sender;
        lastWagerTimeoutTimestamp = block.timestamp + currentTimeout;
        prizePool = prizePool.add(price.sub(fee).sub(dividend).sub(wagerPoolPart));
        
        // Emit event.
        Play(msg.sender, block.timestamp, lastWagerTimeoutTimestamp, wagerIndex, prizePool);
        
        // Send the game starter dividend.
        _sendFunds(gameStarter, dividend);
        
        // Give the wager price every 7th wager.
        if (wagerIndex > 0 && (wagerIndex % 7) == 0) {
            // Give the wager prize to the sender.
            msg.sender.transfer(wagerPool);
            
            // Reset the wager pool.
            wagerPool = 0;
        }
        
        // Add funds to the wager pool.
        wagerPool = wagerPool.add(wagerPoolPart);
        
        // Increment the wager index.
        wagerIndex = wagerIndex.add(1);
        
        // Refund any excess Ether sent.
        // This subtraction never underflows, as msg.value is guaranteed
        // to be greater than or equal to price.
        uint256 excess = msg.value - price;
        
        if (excess > 0) {
            msg.sender.transfer(excess);
        }
    }
    
    /// @notice Set the parameters for the next game.
    /// @param _price The price of wagers for the next game.
    /// @param _timeout The timeout in seconds for the next game.
    /// @param _minimumTimeout The minimum timeout in seconds for
    /// the next game.
    /// @param _numberOfWagersToMinimumTimeout The number of wagers
    /// required to move to the minimum timeout for the next game.
    function setNextGame(uint256 _price, uint256 _timeout, uint256 _minimumTimeout, uint256 _numberOfWagersToMinimumTimeout) external onlyCFO {
        require(_timeout >= _minimumTimeout);
    
        nextPrice = _price;
        nextTimeout = _timeout;
        nextMinimumTimeout = _minimumTimeout;
        nextNumberOfWagersToMinimumTimeout = _numberOfWagersToMinimumTimeout;
        NextGame(nextPrice, nextTimeout, nextMinimumTimeout, nextNumberOfWagersToMinimumTimeout);
    } 
    
    /// @notice End the game. Pay prize.
    function endGame() external {
        require(_processGameEnd());
    }
    
    /// @dev End the game. Pay prize.
    function _processGameEnd() internal returns(bool) {
        if (!gameStarted) {
            // No game is started.
            return false;
        }
    
        if (block.timestamp <= lastWagerTimeoutTimestamp) {
            // The game has not yet finished.
            return false;
        }
        
        // Calculate the prize. Any leftover funds for the
        // 7th wager prize is added to the prize pool.
        uint256 prize = prizePool.add(wagerPool);
        
        // The game has finished. Pay the prize to the last player.
        _sendFunds(lastPlayer, prize);
        
        // Emit event.
        End(lastPlayer, lastWagerTimeoutTimestamp, prize);
        
        // Reset the game.
        gameStarted = false;
        gameStarter = 0x0;
        lastPlayer = 0x0;
        lastWagerTimeoutTimestamp = 0;
        wagerIndex = 0;
        prizePool = 0;
        wagerPool = 0;
        
        // Indicate ending the game was successful.
        return true;
    }
}
/// @dev Defines core Chronos functionality.
function endGame() external {
    require(_processGameEnd());
}

/// @notice End the game. Pay prize.