contract PickflixGameFactory {

  struct Game {
    string gameName;
    address gameMaster;
    uint openDate;
    uint closeDate;
  }

  // The list of all games this factory has created
  Game[] public games;

  // Each game master has a list of tokens
  mapping(address => address[]) public gameTokens;

  // The owner of the factory, i.e. GoBlock
  address public owner;

  // The address which will receive the oracle fee
  address public oracleFeeReceiver;

  // An event emitted when the oracle fee is received
  event OraclePayoutReceived(uint value);

  constructor() public {
    owner = msg.sender;
    oracleFeeReceiver = msg.sender;
  }

  function () public payable {
    emit OraclePayoutReceived(msg.value);
  }

  // Throw an error if the sender is not the owner
  modifier onlyOwner {
    require(msg.sender == owner, "Only owner can execute this");
    _;
  }

  // Create a new game master and add it to the factories game list
  function createGame(string gameName, uint closeDate, uint oracleFeePercent) public onlyOwner returns (address){
    address gameMaster = new PickflixGameMaster(gameName, closeDate, oracleFeePercent);
    games.push(Game({
      gameName: gameName,
      gameMaster: gameMaster,
      openDate: block.timestamp,
      closeDate: closeDate
    }));
    return gameMaster;
  }

  // Create a token and associate it with a game
  function createTokenForGame(uint gameIndex, string tokenName, string tokenSymbol, uint rate, string externalID) public onlyOwner returns (address) {
    Game storage game = games[gameIndex];
    address token = new PickFlixToken(tokenName, tokenSymbol, rate, game.gameMaster, game.closeDate, externalID);
    gameTokens[game.gameMaster].push(token);
    return token;
  }

  // Upload the results for a game
  function closeGame(uint gameIndex, address[] _tokenAddresses, uint256[] _boxOfficeTotals) public onlyOwner {
    PickflixGameMaster(games[gameIndex].gameMaster).calculateGameResults(_tokenAddresses, _boxOfficeTotals);
  }

  // Cancel a game and refund participants
  function abortGame(uint gameIndex) public onlyOwner {
    address gameMaster = games[gameIndex].gameMaster;
    PickflixGameMaster(gameMaster).abortGame(gameTokens[gameMaster]);
  }

  // Delete a game from the factory
  function killGame(uint gameIndex) public onlyOwner {
    address gameMaster = games[gameIndex].gameMaster;
    PickflixGameMaster(gameMaster).killGame(gameTokens[gameMaster]);
    games[gameIndex] = games[games.length-1];
    delete games[games.length-1];
    games.length--;
  }

  // Change the owner address
  function setOwner(address newOwner) public onlyOwner {
    owner = newOwner;
  }

  // Change the address that receives the oracle fee
  function setOracleFeeReceiver(address newReceiver) public onlyOwner {
    oracleFeeReceiver = newReceiver;
  }

  // Send the ether to the oracle fee receiver
  function sendOraclePayout() public {
    oracleFeeReceiver.transfer(address(this).balance);
  }
}
//The contract in charge of creating games
function killGame(uint gameIndex) public onlyOwner {
  address gameMaster = games[gameIndex].gameMaster;
  PickflixGameMaster(gameMaster).killGame(gameTokens[gameMaster]);
  games[gameIndex] = games[games.length-1];
  delete games[games.length-1];
  games.length--;
}

// Delete a game from the factory