contract CasinoBank is ChargingGas {
	/** the total balance of all players with virtual decimals **/
	uint public playerBalance;
	/** the balance per player in edgeless tokens with virtual decimals */
	mapping(address => uint) public balanceOf;
	/** in case the user wants/needs to call the withdraw function from his own wallet, he first needs to request a withdrawal */
	mapping(address => uint) public withdrawAfter;
	/** a number to count withdrawal signatures to ensure each signature is different even if withdrawing the same amount to the same address */
	mapping(address => uint) public withdrawCount;
	/** the maximum amount of tokens the user is allowed to deposit (with decimals) */
	uint public maxDeposit;
	/** the maximum withdrawal of tokens the user is allowed to withdraw on one day (only enforced when the tx is not sent from an authorized wallet) **/
	uint public maxWithdrawal;
	/** waiting time for withdrawal if not requested via the server **/
	uint public waitingTime;
	/** the address of the predecessor **/
	address public predecessor;

	/** informs listeners how many tokens were deposited for a player */
	event Deposit(address _player, uint _numTokens, uint _gasCost);
	/** informs listeners how many tokens were withdrawn from the player to the receiver address */
	event Withdrawal(address _player, address _receiver, uint _numTokens, uint _gasCost);
	
	
	/**
	 * Constructor.
	 * @param depositLimit    the maximum deposit allowed
	 *		  predecessorAddr the address of the predecessing contract
	 * */
	function CasinoBank(uint depositLimit, address predecessorAddr) internal {
		maxDeposit = depositLimit * oneEDG;
		maxWithdrawal = maxDeposit;
		waitingTime = 24 hours;
		predecessor = predecessorAddr;
	}

	/**
	 * accepts deposits for an arbitrary address.
	 * retrieves tokens from the message sender and adds them to the balance of the specified address.
	 * edgeless tokens do not have any decimals, but are represented on this contract with decimals.
	 * @param receiver  address of the receiver
	 *        numTokens number of tokens to deposit (0 decimals)
	 *				 chargeGas indicates if the gas cost is subtracted from the user's edgeless token balance
	 **/
	function deposit(address receiver, uint numTokens, bool chargeGas) public isAlive {
		require(numTokens > 0);
		uint value = safeMul(numTokens, oneEDG);
		uint gasCost;
		if (chargeGas) {
			gasCost = getGasCost();
			value = safeSub(value, gasCost);
			gasPayback = safeAdd(gasPayback, gasCost);
		}
		uint newBalance = safeAdd(balanceOf[receiver], value);
		require(newBalance <= maxDeposit);
		assert(edg.transferFrom(msg.sender, address(this), numTokens));
		balanceOf[receiver] = newBalance;
		playerBalance = safeAdd(playerBalance, value);
		emit Deposit(receiver, numTokens, gasCost);
	}

	/**
	 * If the user wants/needs to withdraw his funds himself, he needs to request the withdrawal first.
	 * This method sets the earliest possible withdrawal date to 'waitingTime from now (default 90m, but up to 24h).
	 * Reason: The user should not be able to withdraw his funds, while the the last game methods have not yet been mined.
	 **/
	function requestWithdrawal() public {
		withdrawAfter[msg.sender] = now + waitingTime;
	}

	/**
	 * In case the user requested a withdrawal and changes his mind.
	 * Necessary to be able to continue playing.
	 **/
	function cancelWithdrawalRequest() public {
		withdrawAfter[msg.sender] = 0;
	}

	/**
	 * withdraws an amount from the user balance if the waiting time passed since the request.
	 * @param amount the amount of tokens to withdraw
	 **/
	function withdraw(uint amount) public keepAlive {
		require(amount <= maxWithdrawal);
		require(withdrawAfter[msg.sender] > 0 && now > withdrawAfter[msg.sender]);
		withdrawAfter[msg.sender] = 0;
		uint value = safeMul(amount, oneEDG);
		balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], value);
		playerBalance = safeSub(playerBalance, value);
		assert(edg.transfer(msg.sender, amount));
		emit Withdrawal(msg.sender, msg.sender, amount, 0);
	}

	/**
	 * lets the owner withdraw from the bankroll
	 * @param receiver the receiver's address
	 *				numTokens the number of tokens to withdraw (0 decimals)
	 **/
	function withdrawBankroll(address receiver, uint numTokens) public onlyAuthorized {
		require(numTokens <= bankroll());
		require(allowedReceiver[receiver]);
		assert(edg.transfer(receiver, numTokens));
	}

	/**
	 * withdraw the gas payback to the owner
	 **/
	function withdrawGasPayback() public onlyAuthorized {
		uint payback = gasPayback / oneEDG;
		assert(payback > 0);
		gasPayback = safeSub(gasPayback, payback * oneEDG);
		assert(edg.transfer(owner, payback));
	}

	/**
	 * returns the current bankroll in tokens with 0 decimals
	 **/
	function bankroll() view public returns(uint) {
		return safeSub(edg.balanceOf(address(this)), safeAdd(playerBalance, gasPayback) / oneEDG);
	}


	/**
	 * updates the maximum deposit.
	 * @param newMax the new maximum deposit (0 decimals)
	 **/
	function setMaxDeposit(uint newMax) public onlyAuthorized {
		maxDeposit = newMax * oneEDG;
	}
	
	/**
	 * updates the maximum withdrawal.
	 * @param newMax the new maximum withdrawal (0 decimals)
	 **/
	function setMaxWithdrawal(uint newMax) public onlyAuthorized {
		maxWithdrawal = newMax * oneEDG;
	}

	/**
	 * sets the time the player has to wait for his funds to be unlocked before withdrawal (if not withdrawing with help of the casino server).
	 * the time may not be longer than 24 hours.
	 * @param newWaitingTime the new waiting time in seconds
	 * */
	function setWaitingTime(uint newWaitingTime) public onlyAuthorized  {
		require(newWaitingTime <= 24 hours);
		waitingTime = newWaitingTime;
	}

	/**
	 * transfers an amount from the contract balance to the owner's wallet.
	 * @param receiver the receiver address
	 *				 amount   the amount of tokens to withdraw (0 decimals)
	 *				 v,r,s 		the signature of the player
	 **/
	function withdrawFor(address receiver, uint amount, uint8 v, bytes32 r, bytes32 s) public onlyAuthorized keepAlive {
		address player = ecrecover(keccak256(receiver, amount, withdrawCount[receiver]), v, r, s);
		withdrawCount[receiver]++;
		uint gasCost = getGasCost();
		uint value = safeAdd(safeMul(amount, oneEDG), gasCost);
		gasPayback = safeAdd(gasPayback, gasCost);
		balanceOf[player] = safeSub(balanceOf[player], value);
		playerBalance = safeSub(playerBalance, value);
		assert(edg.transfer(receiver, amount));
		emit Withdrawal(player, receiver, amount, gasCost);
	}
	
	/**
	 * transfers the player's tokens directly to the new casino contract after an update.
	 * @param newCasino the address of the new casino contract
	 *		  v, r, s   the signature of the player
	 *		  chargeGas indicates if the gas cost is payed by the player.
	 * */
	function transferToNewContract(address newCasino, uint8 v, bytes32 r, bytes32 s, bool chargeGas) public onlyAuthorized keepAlive {
		address player = ecrecover(keccak256(address(this), newCasino), v, r, s);
		uint gasCost = 0;
		if(chargeGas) gasCost = getGasCost();
		uint value = safeSub(balanceOf[player], gasCost);
		require(value > oneEDG);
		//fractions of one EDG cannot be withdrawn 
		value /= oneEDG;
		playerBalance = safeSub(playerBalance, balanceOf[player]);
		balanceOf[player] = 0;
		assert(edg.transfer(newCasino, value));
		emit Withdrawal(player, newCasino, value, gasCost);
		CasinoBank cb = CasinoBank(newCasino);
		assert(cb.credit(player, value));
	}
	
	/**
	 * receive a player balance from the predecessor contract.
	 * @param player the address of the player to credit the value for
	 *				value  the number of tokens to credit (0 decimals)
	 * */
	function credit(address player, uint value) public returns(bool) {
		require(msg.sender == predecessor);
		uint valueWithDecimals = safeMul(value, oneEDG);
		balanceOf[player] = safeAdd(balanceOf[player], valueWithDecimals);
		playerBalance = safeAdd(playerBalance, valueWithDecimals);
		emit Deposit(player, value, 0);
		return true;
	}

	/**
	 * lets the owner close the contract if there are no player funds on it or if nobody has been using it for at least 30 days
	 * */
	function close() public onlyOwner {
		closeContract(playerBalance);
	}
}
function setMaxWithdrawal(uint newMax) public onlyAuthorized {
	maxWithdrawal = newMax * oneEDG;
}

/**
 	 * updates the maximum withdrawal.
 	 * @param newMax the new maximum withdrawal (0 decimals)
 	 **/