contract ZethrGame {
  using SafeMath for uint;
  using SafeMath for uint56;

  // Default events:
  event Result (address player, uint amountWagered, int amountOffset);
  event Wager (address player, uint amount, bytes data);

  // Queue of pending/unresolved bets
  address[] pendingBetsQueue;
  uint queueHead = 0;
  uint queueTail = 0;

  // Store each player's latest bet via mapping
  mapping(address => BetBase) bets;

  // Bet structures must start with this layout
  struct BetBase {
    // Must contain these in this order
    uint56 tokenValue;    // Multiply by 1e14 to get tokens
    uint48 blockNumber;
    uint8 tier;
    // Game specific structures can add more after this
  }

  // Mapping of addresses to their *position* in the queue
  // Zero = they aren't in the queue
  mapping(address => uint) pendingBetsMapping;

  // Holds the bankroll controller info
  ZethrBankrollControllerInterface controller;

  // Is the game paused?
  bool paused;

  // Minimum bet should always be >= 1
  uint minBet = 1e18;

  // Percentage that a resolver gets when he resolves bets for the house
  uint resolverPercentage;

  // Every game has a name
  string gameName;

  constructor (address _controllerAddress, uint _resolverPercentage, string _name) public {
    controller = ZethrBankrollControllerInterface(_controllerAddress);
    resolverPercentage = _resolverPercentage;
    gameName = _name;
  }

  /** @dev Gets the max profit of this game as decided by the token bankroll
    * @return uint The maximum profit
    */
  function getMaxProfit()
  public view
  returns (uint)
  {
    return ZethrTokenBankrollInterface(msg.sender).getMaxProfit(address(this));
  }

  /** @dev Pauses the game, preventing anyone from placing bets
    */
  function ownerPauseGame()
  public
  ownerOnly
  {
    paused = true;
  }

  /** @dev Resumes the game, allowing bets
    */
  function ownerResumeGame()
  public
  ownerOnly
  {
    paused = false;
  }

  /** @dev Sets the percentage of the bets that a resolver gets when resolving tokens.
    * @param _percentage The percentage as x/1,000,000 that the resolver gets
    */
  function ownerSetResolverPercentage(uint _percentage)
  public
  ownerOnly
  {
    require(_percentage <= 1000000);
    resolverPercentage = _percentage;
  }

  /** @dev Sets the address of the game controller
    * @param _controllerAddress The new address of the controller
    */
  function ownerSetControllerAddress(address _controllerAddress)
  public
  ownerOnly
  {
    controller = ZethrBankrollControllerInterface(_controllerAddress);
  }

  // Every game should have a name
  /** @dev Sets the name of the game
    * @param _name The name of the game
    */
  function ownerSetGameName(string _name)
  ownerOnly
  public
  {
    gameName = _name;
  }

  /** @dev Gets the game name
    * @return The name of the game
    */
  function getGameName()
  public view
  returns (string)
  {
    return gameName;
  }

  /** @dev Resolve expired bets in the queue. Gives a percentage of the house edge to the resolver as ZTH
    * @param _numToResolve The number of bets to resolve.
    * @return tokensEarned The number of tokens earned
    * @return queueHead The new head of the queue
    */
  function resolveExpiredBets(uint _numToResolve)
  public
  returns (uint tokensEarned_, uint queueHead_)
  {
    uint mQueue = queueHead;
    uint head;
    uint tail = (mQueue + _numToResolve) > pendingBetsQueue.length ? pendingBetsQueue.length : (mQueue + _numToResolve);
    uint tokensEarned = 0;

    for (head = mQueue; head < tail; head++) {
      // Check the head of the queue to see if there is a resolvable bet
      // This means the bet at the queue head is older than 255 blocks AND is not 0
      // (However, if the address at the head is null, skip it, it's already been resolved)
      if (pendingBetsQueue[head] == address(0x0)) {
        continue;
      }

      if (bets[pendingBetsQueue[head]].blockNumber != 0 && block.number > 256 + bets[pendingBetsQueue[head]].blockNumber) {
        // Resolve the bet
        // finishBetfrom returns the *player* profit
        // this will be negative if the player lost and the house won
        // so flip it to get the house profit, if any
        int sum = - finishBetFrom(pendingBetsQueue[head]);

        // Tokens earned is a percentage of the loss
        if (sum > 0) {
          tokensEarned += (uint(sum).mul(resolverPercentage)).div(1000000);
        }

        // Queue-tail is always the "next" open spot, so queue head and tail will never overlap
      } else {
        // If we can't resolve a bet, stop going down the queue
        break;
      }
    }

    queueHead = head;

    // Send the earned tokens to the resolver
    if (tokensEarned >= 1e14) {
      controller.gamePayoutResolver(msg.sender, tokensEarned);
    }

    return (tokensEarned, head);
  }

  /** @dev Finishes the bet of the sender, if it exists.
    * @return int The total profit (positive or negative) earned by the sender
    */
  function finishBet()
  public
  hasNotBetThisBlock(msg.sender)
  returns (int)
  {
    return finishBetFrom(msg.sender);
  }

  /** @dev Resturns a random number
    * @param _blockn The block number to base the random number off of
    * @param _entropy Data to use in the random generation
    * @param _index Data to use in the random generation
    * @return randomNumber The random number to return
    */
  function maxRandom(uint _blockn, address _entropy, uint _index)
  private view
  returns (uint256 randomNumber)
  {
    return uint256(keccak256(
        abi.encodePacked(
          blockhash(_blockn),
          _entropy,
          _index
        )));
  }

  /** @dev Returns a random number
    * @param _upper The upper end of the range, exclusive
    * @param _blockn The block number to use for the random number
    * @param _entropy An address to be used for entropy
    * @param _index A number to get the next random number
    * @return randomNumber The random number
    */
  function random(uint256 _upper, uint256 _blockn, address _entropy, uint _index)
  internal view
  returns (uint256 randomNumber)
  {
    return maxRandom(_blockn, _entropy, _index) % _upper;
  }

  // Prevents the user from placing two bets in one block
  modifier hasNotBetThisBlock(address _sender)
  {
    require(bets[_sender].blockNumber != block.number);
    _;
  }

  // Requires that msg.sender is one of the token bankrolls
  modifier bankrollOnly {
    require(controller.isTokenBankroll(msg.sender));
    _;
  }

  // Requires that the game is not paused
  modifier isNotPaused {
    require(!paused);
    _;
  }

  // Requires that the bet given has max profit low enough
  modifier betIsValid(uint _betSize, uint _tier, bytes _data) {
    uint divRate = ZethrTierLibrary.getDivRate(_tier);
    require(isBetValid(_betSize, divRate, _data));
    _;
  }

  // Only an owner can call this method (controller is always an owner)
  modifier ownerOnly()
  {
    require(msg.sender == address(controller) || controller.multiSigWallet().isOwner(msg.sender));
    _;
  }

  /** @dev Places a bet. Callable only by token bankrolls
    * @param _player The player that is placing the bet
    * @param _tokenCount The total number of tokens bet
    * @param _divRate The dividend rate of the player
    * @param _data The game-specific data, encoded in bytes-form
    */
  function execute(address _player, uint _tokenCount, uint _divRate, bytes _data) public;

  /** @dev Resolves the bet of the supplied player.
    * @param _playerAddress The address of the player whos bet we are resolving
    * @return int The total profit the player earned, positive or negative
    */
  function finishBetFrom(address _playerAddress) internal returns (int);

  /** @dev Determines if a supplied bet is valid
    * @param _tokenCount The total number of tokens bet
    * @param _divRate The dividend rate of the bet
    * @param _data The game-specific bet data
    * @return bool Whether or not the bet is valid
    */
  function isBetValid(uint _tokenCount, uint _divRate, bytes _data) public view returns (bool);
}
/* Zethr Game Interface
 *
 * Contains the necessary functions to integrate with
 * the Zethr Token bankrolls & the Zethr game ecosystem.
 *
 * Token Bankroll Functions:
 *  - execute
 *
 * Player Functions:
 *  - finish
 *
 * Bankroll Controller / Owner Functions:
 *  - pauseGame
 *  - resumeGame
 *  - set resolver percentage
 *  - set controller address
 *
 * Player/Token Bankroll Functions:
 *  - resolvePendingBets
 */
function getMaxProfit()
public view
returns (uint)
{
  return ZethrTokenBankrollInterface(msg.sender).getMaxProfit(address(this));
}

/** @dev Gets the max profit of this game as decided by the token bankroll
 * @return uint The maximum profit
 */