contract CryptoTreasure is Pausable, Destructible, usingOraclize {

	uint currentGame = 1;
	uint playPrice = 0.05 ether;
	uint numberOfSpots = 107;
	uint public currentTreasure;
	uint initialTreasure = 2.5 ether;
	uint totalPaidOut;
	uint totalPlayers;
	uint totalGamesEnded;
	uint totalGamesWon;
	uint totalGamesLost;
	uint totalWagered;
	uint totalPendingWithdrawals;
	uint gasForOraclize = 200000;
	uint restakePercent = 1027;

	struct Player {
		uint lastGamePlayed;
		uint gamesPlayed;
		uint gamesWon;
		uint gamesLost;
		uint amountWon;
		uint pendingWithdrawals;
		uint lastGameStatus;
	}

	struct Game {
		uint Id;
		uint Date;
		uint Spot;
		bool Ended;
		bool Won;
		address Player;
	}

	mapping (uint => Game) games;
	mapping (address => Player) players;
	mapping (bytes32 => uint) oraclizeQueries;

	event GameWon(uint indexed game, address indexed player, uint amount, uint date);
	event GameEnded(uint indexed game, address indexed player, uint date, uint8 number);

	function CryptoTreasure() public {
		oraclize_setNetwork(networkID_consensys);
	}

	function getVariables() public view returns(uint[11]) {
		uint[11] memory array;
		array[0] = (paused == true ? 1 : 0);
		array[1] = playPrice;
		array[2] = currentTreasure;
		array[3] = players[msg.sender].lastGamePlayed;
		array[4] = players[msg.sender].gamesPlayed;
		array[5] = players[msg.sender].gamesWon;
		array[6] = players[msg.sender].gamesLost;
		array[7] = players[msg.sender].amountWon;
		array[8] = players[msg.sender].pendingWithdrawals;
		array[9] = players[msg.sender].lastGameStatus;
		array[10] = totalPaidOut;
		return array;
	}

	function initializeTreasure(uint _amount) internal {
		if (_amount > 0) {
			if (this.balance > _amount + totalPendingWithdrawals)
				currentTreasure = _amount;
			else
				paused = true;
		}
	}

	function participate(uint8 _spot) public payable whenNotPaused {
    require(_spot >= 1 && _spot <= numberOfSpots);
    require(msg.value == playPrice);

		uint _restake = msg.value / 100 / 100 * restakePercent;
    currentTreasure += _restake;
    totalWagered += msg.value;

		if (players[msg.sender].gamesPlayed == 0)
			totalPlayers++;
		players[msg.sender].gamesPlayed++;

	  games[currentGame].Id = currentGame;
		games[currentGame].Date = now;
		games[currentGame].Spot = _spot;
		games[currentGame].Player = msg.sender;

	  finishGame(currentGame);
		currentGame++;
	}

  function __callback(bytes32 queryId, string result) public {
  	assert(msg.sender == oraclize_cbAddress());
		uint gameId = oraclizeQueries[queryId];
		uint8 winningNumber = uint8(parseInt(result));
		distributeGame(winningNumber, gameId);
  }

  function distributeGame(uint8 winningNumber, uint gameId) internal {
  	require(games[gameId].Ended == false);
  	require(games[gameId].Player != 0x0);
		games[gameId].Ended = true;
		address _address = games[gameId].Player;

		if (games[gameId].Spot == winningNumber) {

			uint winningAmount = currentTreasure;
			currentTreasure = 0;
			games[gameId].Won = true;
			players[_address].gamesWon++;
			players[_address].amountWon += winningAmount;
			players[_address].lastGameStatus = 1;
			GameWon(gameId, _address, winningAmount, now);
			if (!_address.send(winningAmount)) {
				players[_address].pendingWithdrawals += winningAmount;
				totalPendingWithdrawals += winningAmount;
			}
			totalPaidOut += winningAmount;
			totalGamesWon++;
			initializeTreasure(initialTreasure);

		} else {
			games[gameId].Won = false;
			players[_address].gamesLost++;
			players[_address].lastGameStatus = 2;
			totalGamesLost++;
		}

		players[_address].lastGamePlayed = gameId;
		GameEnded(gameId, _address, now, winningNumber);
		totalGamesEnded++;
  }

	function finishGame(uint gameId) internal {
		require(games[gameId].Ended == false);
		bytes32 queryId = oraclize_query("nested", "[URL] ['json(https://api.random.org/json-rpc/1/invoke).result.random.data.0', '\\n{\"jsonrpc\":\"2.0\",\"method\":\"generateIntegers\",\"params\":{\"apiKey\":\"${[decrypt] BFa/zWup+t1GegCMaZO/Tx433Go5RruGsY5Kikog/3HpGfQRkxEaIzcLlY28YgCiNsR/FdeHyQFqAKiPrRvLSGqL4tPFla1p71fEwxFYqhiu2Yrxqlk8vz//fA87oJ6eu+aL6ze3HihJpi3LQ+7j4k5EnoEq}\",\"n\":1,\"min\":1,\"max\":107,\"replacement\":true${[identity] \"}\"},\"id\":1${[identity] \"}\"}']");
		oraclizeQueries[queryId] = gameId;
	}

	function withdraw() public whenNotPaused returns (bool) {
		uint withdrawAmount = players[msg.sender].pendingWithdrawals;
		if (withdrawAmount > 0) {
			players[msg.sender].pendingWithdrawals = 0;
			totalPendingWithdrawals -= withdrawAmount;
			if (msg.sender.send(withdrawAmount)) {
				return true;
			} else {
				players[msg.sender].pendingWithdrawals = withdrawAmount;
				totalPendingWithdrawals += withdrawAmount;
				return false;
			}
		}
	}

	/* Admin functions below */

	function withdraw(uint _amount) public onlyOwner {
		require(_amount <= this.balance - currentTreasure - totalPendingWithdrawals);
    owner.transfer(_amount);
	}

	function deposit() public payable onlyOwner {
	}

	function getAdminVariables() public view onlyOwner returns(uint[14]) {
		uint[14] memory array;
		array[0] = this.balance;
		array[1] = playPrice;
		array[2] = initialTreasure;
		array[3] = currentGame;
		array[4] = totalPaidOut;
		array[5] = totalPlayers;
		array[6] = currentTreasure;
		array[7] = totalGamesEnded;
		array[8] = totalGamesWon;
		array[9] = totalGamesLost;
		array[10] = totalWagered;
		array[11] = totalPendingWithdrawals;
		array[12] = gasForOraclize;
		array[13] = restakePercent;
		return array;
	}

	function setAdminVariables(uint _playPrice,
														 uint _initialTreasure,
														 uint _restakePercent,
														 uint _gasForOraclize) public onlyOwner {
		if (_playPrice > 0) playPrice = _playPrice;
		if (_initialTreasure > 0) initialTreasure = _initialTreasure;
		if (_restakePercent > 0) restakePercent = _restakePercent;
		if (_gasForOraclize > 0) gasForOraclize = _gasForOraclize;
	}

	function getUnfinishedGames() public view onlyOwner returns (uint[], uint[], uint[], bool[], bool[], address[]) {
		uint totalUnfinished = 0;
		uint i = 0;
		for (i = 0; i < currentGame; i++) {
			if (games[i].Ended == false && games[i].Id > 0)
				totalUnfinished++;
		}
		uint[] memory arrId = new uint[](totalUnfinished);
		uint[] memory arrDate = new uint[](totalUnfinished);
		uint[] memory arrSpot = new uint[](totalUnfinished);
		bool[] memory arrEnded = new bool[](totalUnfinished);
		bool[] memory arrWon = new bool[](totalUnfinished);
		address[] memory arrPlayer = new address[](totalUnfinished);
		uint g = 0;
		for (i = 0; i < currentGame; i++) {
			if (games[i].Ended == false && games[i].Id > 0) {
				arrId[g] = games[i].Id;
				arrDate[g] = games[i].Date;
				arrSpot[g] = games[i].Spot;
				arrEnded[g] = games[i].Ended;
				arrWon[g] = games[i].Won;
				arrPlayer[g] = games[i].Player;
				g++;
			}
		}
		return (arrId, arrDate, arrSpot, arrEnded, arrWon, arrPlayer);
	}

	function manualFinishGame(uint gameId) public onlyOwner {
		finishGame(gameId);
	}

	function manualInitializeTreasure(uint _amount) public onlyOwner {
		require(this.balance > _amount + totalPendingWithdrawals);
		initializeTreasure(_amount);
	}

}
// </ORACLIZE_API>
function withdraw(uint _amount) public onlyOwner {
	require(_amount <= this.balance - currentTreasure - totalPendingWithdrawals);
   owner.transfer(_amount);
}

/* Admin functions below */