contract GameSetting is developed, escaped, SettingInterface {
	using SafeMath for uint256;

	address public spinwinAddress;
	address public spinlotteryAddress;

	mapping(bytes32 => uint256) internal _uintSettings;    // Array containing all uint256 settings
	mapping(bytes32 => bool) internal _boolSettings;       // Array containing all bool settings

	uint256 constant public PERCENTAGE_DIVISOR = 10 ** 6;   // 1000000 = 100%
	uint256 constant public HOUSE_EDGE_DIVISOR = 1000;
	uint256 constant public CURRENCY_DIVISOR = 10**18;
	uint256 constant public TWO_DECIMALS = 100;
	uint256 constant public MAX_NUMBER = 99;
	uint256 constant public MIN_NUMBER = 2;
	uint256 constant public MAX_HOUSE_EDGE = 1000;          // 0% House Edge
	uint256 constant public MIN_HOUSE_EDGE = 0;             // 100% House edge

	TokenInterface internal _spintoken;
	SpinWinInterface internal _spinwin;

	/**
	 * @dev Log dev sets uint setting
	 */
	event LogSetUintSetting(address indexed who, bytes32 indexed name, uint256 value);

	/**
	 * @dev Log dev sets bool setting
	 */
	event LogSetBoolSetting(address indexed who, bytes32 indexed name, bool value);

	/**
	 * @dev Log when dev adds some funds
	 */
	event LogAddBankRoll(uint256 amount);

	/**
	 * @dev Log when the token to Wei exchange rate is updated
	 */
	event LogUpdateTokenToWeiExchangeRate(uint256 exchangeRate, uint256 exchangeRateBlockNumber);

	/**
	 * @dev Log when developer set spinwin contract to emergency mode
	 */
	event LogSpinwinEscapeHatch();

	/**
	 * Constructor
	 */
	constructor(address _spintokenAddress) public {
		_spintoken = TokenInterface(_spintokenAddress);
		devSetUintSetting('minBet', CURRENCY_DIVISOR.div(100));			// init min bet (0.01 ether)
		devSetUintSetting('maxProfitAsPercentOfHouse', 200000);         // init 200000 = 20% commission
		devSetUintSetting('minBankroll', CURRENCY_DIVISOR.mul(20));     // init min bank roll (20 eth)
		devSetTokenExchangeMinBankrollPercent(900000);                  // init token exchange min bank roll percentage (90%)
		devSetUintSetting('referralPercent', 10000);                    // init referral percentage (1%)
		devSetUintSetting('gasForLottery', 250000);                     // init gas for lottery
		devSetUintSetting('maxBlockSecurityCount', 256);                // init max block security count (256)
		devSetUintSetting('blockSecurityCount', 3);                     // init block security count (3)
		devSetUintSetting('tokenExchangeBlockSecurityCount', 3);        // init token exchange block security count (3)
		devSetUintSetting('maxProfitBlockSecurityCount', 3);            // init max profit block security count (3)
		devSetUintSetting('spinEdgeModifier', 80);                      // init spin edge modifier (0.8)
		devSetUintSetting('spinBankModifier', 50);                      // init spin bank modifier (0.5)
		devSetUintSetting('spinNumberModifier', 5);                     // init spin number modifier (0.05)
		devSetUintSetting('maxMinBankroll', CURRENCY_DIVISOR.mul(5000));   // init max value for min bankroll (5,000 eth)
		devSetUintSetting('lastProcessedBetInternalId', 1);             // init lastProcessedBetInternalId = 1
		devSetUintSetting('exchangeAmountDivisor', 2);                  // init exchangeAmountDivisor = 2
		devSetUintSetting('tokenExchangeRatio', 10);                    // init tokenExchangeRatio = 0.1 (divided by TWO_DECIMALS)
		devSetUintSetting('spinToWeiRate', CURRENCY_DIVISOR);           // init spinToWeiRate = 1
		devSetUintSetting('blockToSpinRate', CURRENCY_DIVISOR);         // init blockToSpinRate = 1
		devSetUintSetting('blockToWeiRate', CURRENCY_DIVISOR);          // init blockToWeiRate = 1
		devSetUintSetting('gasForClearingBet', 320000);                 // init gasForClearingBet = 320000 gas
		devSetUintSetting('gasPrice', 40000000000);                     // init gasPrice = 40 gwei
		devSetUintSetting('clearSingleBetMultiplier', 200);             // init clearSingleBetMultiplier = 2x (divided by TWO_DECIMALS)
		devSetUintSetting('clearMultipleBetsMultiplier', 100);          // init clearMultipleBetMultiplier = 1x (divided by TWO_DECIMALS)
		devSetUintSetting('maxNumClearBets', 4);                        // init maxNumClearBets = 4
		devSetUintSetting('lotteryTargetMultiplier', 200);              // init lotteryTargetMultiplier = 2x (divided by TWO_DECIMALS)
		_setMaxProfit(true);
	}

	/**
	 * @dev Checks only spinwinAddress is calling
	 */
	modifier onlySpinwin {
		require(msg.sender == spinwinAddress);
		_;
	}

	/**
	 * @dev Checks only spinlotteryAddress is calling
	 */
	modifier onlySpinlottery {
		require(msg.sender == spinlotteryAddress);
		_;
	}

	/******************************************/
	/*       DEVELOPER ONLY METHODS           */
	/******************************************/

	/**
	 * @dev Allows developer to set spinwin contract address
	 * @param _address The contract address to be set
	 */
	function devSetSpinwinAddress(address _address) public onlyDeveloper {
		require (_address != address(0));
		spinwinAddress = _address;
		_spinwin = SpinWinInterface(spinwinAddress);
	}

	/**
	 * @dev Allows developer to set spinlottery contract address
	 * @param _address The contract address to be set
	 */
	function devSetSpinlotteryAddress(address _address) public onlyDeveloper {
		require (_address != address(0));
		spinlotteryAddress = _address;
	}

	/**
	 * @dev Allows dev to set uint setting
	 * @param name The setting name to be set
	 * @param value The value to be set
	 */
	function devSetUintSetting(bytes32 name, uint256 value) public onlyDeveloper {
		_uintSettings[name] = value;
		emit LogSetUintSetting(developer, name, value);
	}

	/**
	 * @dev Allows dev to set bool setting
	 * @param name The setting name to be set
	 * @param value The value to be set
	 */
	function devSetBoolSetting(bytes32 name, bool value) public onlyDeveloper {
		_boolSettings[name] = value;
		emit LogSetBoolSetting(developer, name, value);
	}

	/**
	 * @dev Allows developer to set min bank roll
	 * @param minBankroll The new min bankroll value to be set
	 */
	function devSetMinBankroll(uint256 minBankroll) public onlyDeveloper {
		_uintSettings['minBankroll'] = minBankroll;
		_uintSettings['tokenExchangeMinBankroll'] = _uintSettings['minBankroll'].mul(_uintSettings['tokenExchangeMinBankrollPercent']).div(PERCENTAGE_DIVISOR);
	}

	/**
	 * @dev Allows developer to set token exchange min bank roll percent
	 * @param tokenExchangeMinBankrollPercent The new value to be set
	 */
	function devSetTokenExchangeMinBankrollPercent(uint256 tokenExchangeMinBankrollPercent) public onlyDeveloper {
		_uintSettings['tokenExchangeMinBankrollPercent'] = tokenExchangeMinBankrollPercent;
		_uintSettings['tokenExchangeMinBankroll'] = _uintSettings['minBankroll'].mul(_uintSettings['tokenExchangeMinBankrollPercent']).div(PERCENTAGE_DIVISOR);
	}

	/******************************************/
	/*      ESCAPE ACTIVATOR ONLY METHODS     */
	/******************************************/

	/**
	 * @dev Allows escapeActivator to trigger spinwin emergency mode. Will disable all bets and only allow token exchange at a fixed rate
	 */
	function spinwinEscapeHatch() public onlyEscapeActivator {
		_spinwin.refundPendingBets();
		_boolSettings['contractKilled'] = true;
		_uintSettings['contractBalanceHonor'] = _uintSettings['contractBalance'];
		_uintSettings['tokenExchangeMinBankroll'] = 0;
		_uintSettings['tokenExchangeMinBankrollHonor'] = 0;
		/**
		 * tokenToWeiExchangeRate is ETH in 36 decimals or WEI in 18 decimals to account for
		 * the state when token's totalSupply is 10^18 more than contractBalance.
		 * Otherwise the tokenToWeiExchangeRate will always be 0.
		 * This means, in the exchange token function, we need to divide
		 * tokenToWeiExchangeRate with CURRENCY_DIVISOR
		 */
		_uintSettings['tokenToWeiExchangeRate'] = _spintoken.getTotalSupply() > 0 ? _uintSettings['contractBalance'].mul(CURRENCY_DIVISOR).mul(CURRENCY_DIVISOR).div(_spintoken.getTotalSupply()) : 0;
		_uintSettings['tokenToWeiExchangeRateHonor'] = _uintSettings['tokenToWeiExchangeRate'];
		_uintSettings['tokenToWeiExchangeRateBlockNum'] = block.number;
		emit LogUpdateTokenToWeiExchangeRate(_uintSettings['tokenToWeiExchangeRateHonor'], _uintSettings['tokenToWeiExchangeRateBlockNum']);
		emit LogSpinwinEscapeHatch();
	}

	/******************************************/
	/*         SPINWIN ONLY METHODS           */
	/******************************************/
	/**
	 * @dev Allows spinwin to set uint setting
	 * @param name The setting name to be set
	 * @param value The value to be set
	 */
	function spinwinSetUintSetting(bytes32 name, uint256 value) public onlySpinwin {
		_uintSettings[name] = value;
		emit LogSetUintSetting(spinwinAddress, name, value);
	}

	/**
	 * @dev Allows spinwin to increment existing uint setting value
	 * @param name The setting name to be set
	 */
	function spinwinIncrementUintSetting(bytes32 name) public onlySpinwin {
		_uintSettings[name] = _uintSettings[name].add(1);
		emit LogSetUintSetting(spinwinAddress, name, _uintSettings[name]);
	}

	/**
	 * @dev Allows spinwin to set bool setting
	 * @param name The setting name to be set
	 * @param value The value to be set
	 */
	function spinwinSetBoolSetting(bytes32 name, bool value) public onlySpinwin {
		_boolSettings[name] = value;
		emit LogSetBoolSetting(spinwinAddress, name, value);
	}

	/**
	 * @dev Add funds to the spinwin contract
	 * @param amount The amount of eth sent
	 */
	function spinwinAddFunds(uint256 amount) public onlySpinwin {
		// Safely update contract balance
		_uintSettings['contractBalance'] = _uintSettings['contractBalance'].add(amount);

		// Update max profit
		_setMaxProfit(false);

		emit LogAddBankRoll(amount);
	}

	/**
	 * @dev Allow spinwin to update token to Wei exchange rate.
	 */
	function spinwinUpdateTokenToWeiExchangeRate() public onlySpinwin {
		_updateTokenToWeiExchangeRate();
	}

	/**
	 * @dev Allow spinwin to update settings when roll dice
	 * Increment totalBets
	 * Add betValue to totalWeiWagered
	 *
	 * @param betValue The bet value
	 * @return The internal bet ID
	 */
	function spinwinRollDice(uint256 betValue) public onlySpinwin {
		_uintSettings['totalBets']++;
		_uintSettings['totalWeiWagered'] = _uintSettings['totalWeiWagered'].add(betValue);
	}

	/**
	 * @dev Allows spinwin to update uint setting when player wins
	 * @param playerProfit The player profit to be subtracted from contractBalance and added to totalWeiWon
	 */
	function spinwinUpdateWinMetric(uint256 playerProfit) public onlySpinwin {
		_uintSettings['contractBalance'] = _uintSettings['contractBalance'].sub(playerProfit);
		_uintSettings['totalWeiWon'] = _uintSettings['totalWeiWon'].add(playerProfit);
		_setMaxProfit(false);
	}

	/**
	 * @dev Allows spinwin to update uint setting when player loses
	 * @param betValue The original wager
	 * @param tokenRewardValue The amount of token to be rewarded
	 */
	function spinwinUpdateLoseMetric(uint256 betValue, uint256 tokenRewardValue) public onlySpinwin {
		_uintSettings['contractBalance'] = _uintSettings['contractBalance'].add(betValue).sub(1);
		_uintSettings['totalWeiWon'] = _uintSettings['totalWeiWon'].add(1);
		_uintSettings['totalWeiLost'] = _uintSettings['totalWeiLost'].add(betValue).sub(1);
		_uintSettings['totalTokenPayouts'] = _uintSettings['totalTokenPayouts'].add(tokenRewardValue);
		_setMaxProfit(false);
	}

	/**
	 * @dev Allows spinwin to update uint setting when there is a lottery contribution
	 * @param lotteryContribution The amount to be contributed to lottery
	 */
	function spinwinUpdateLotteryContributionMetric(uint256 lotteryContribution) public onlySpinwin {
		_uintSettings['contractBalance'] = _uintSettings['contractBalance'].sub(lotteryContribution);
		_setMaxProfit(true);
	}

	/**
	 * @dev Allows spinwin to update uint setting when there is a token exchange transaction
	 * @param exchangeAmount The converted exchange amount
	 */
	function spinwinUpdateExchangeMetric(uint256 exchangeAmount) public onlySpinwin {
		_uintSettings['contractBalance'] = _uintSettings['contractBalance'].sub(exchangeAmount);
		_setMaxProfit(false);
	}


	/******************************************/
	/*      SPINLOTTERY ONLY METHODS          */
	/******************************************/
	/**
	 * @dev Allows spinlottery to set uint setting
	 * @param name The setting name to be set
	 * @param value The value to be set
	 */
	function spinlotterySetUintSetting(bytes32 name, uint256 value) public onlySpinlottery {
		_uintSettings[name] = value;
		emit LogSetUintSetting(spinlotteryAddress, name, value);
	}

	/**
	 * @dev Allows spinlottery to increment existing uint setting value
	 * @param name The setting name to be set
	 */
	function spinlotteryIncrementUintSetting(bytes32 name) public onlySpinlottery {
		_uintSettings[name] = _uintSettings[name].add(1);
		emit LogSetUintSetting(spinwinAddress, name, _uintSettings[name]);
	}

	/**
	 * @dev Allows spinlottery to set bool setting
	 * @param name The setting name to be set
	 * @param value The value to be set
	 */
	function spinlotterySetBoolSetting(bytes32 name, bool value) public onlySpinlottery {
		_boolSettings[name] = value;
		emit LogSetBoolSetting(spinlotteryAddress, name, value);
	}

	/**
	 * @dev Allow spinlottery to update token to Wei exchange rate.
	 */
	function spinlotteryUpdateTokenToWeiExchangeRate() public onlySpinlottery {
		_updateTokenToWeiExchangeRate();
	}

	/**
	 * @dev Allows lottery to set spinwin minBankroll value
	 * @param _minBankroll The new value to be set
	 * @return Return true if success
	 */
	function spinlotterySetMinBankroll(uint256 _minBankroll) public onlySpinlottery returns (bool) {
		if (_minBankroll > _uintSettings['maxMinBankroll']) {
			_minBankroll = _uintSettings['maxMinBankroll'];
		} else if (_minBankroll < _uintSettings['contractBalance']) {
			_minBankroll = _uintSettings['contractBalance'];
		}
		_uintSettings['minBankroll'] = _minBankroll;
		_uintSettings['tokenExchangeMinBankroll'] = _uintSettings['minBankroll'].mul(_uintSettings['tokenExchangeMinBankrollPercent']).div(PERCENTAGE_DIVISOR);

		// Update max profit
		_setMaxProfit(false);

		return true;
	}

	/******************************************/
	/*         PUBLIC ONLY METHODS            */
	/******************************************/
	/**
	 * @dev Gets uint setting value
	 * @param name The name of the uint setting
	 * @return The value of the setting
	 */
	function uintSettings(bytes32 name) public constant returns (uint256) {
		return _uintSettings[name];
	}

	/**
	 * @dev Gets bool setting value
	 * @param name The name of the bool setting
	 * @return The value of the setting
	 */
	function boolSettings(bytes32 name) public constant returns (bool) {
		return _boolSettings[name];
	}

	/**
	 * @dev Check if contract is active
	 * @return Return true if yes, false otherwise.
	 */
	function isActive() public constant returns (bool) {
		if (_boolSettings['contractKilled'] == false && _boolSettings['gamePaused'] == false) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @dev Check whether current bet is valid
	 * @param rewardValue The winning amount
	 * @param betValue The original wager
	 * @param playerNumber The player chosen number
	 * @param houseEdge The house edge
	 * @return Return true if yes, false otherwise.
	 */
	function canBet(uint256 rewardValue, uint256 betValue, uint256 playerNumber, uint256 houseEdge) public constant returns (bool) {
		if (_boolSettings['contractKilled'] == false && _boolSettings['gamePaused'] == false && rewardValue <= _uintSettings['maxProfitHonor'] && betValue >= _uintSettings['minBet'] && houseEdge >= MIN_HOUSE_EDGE && houseEdge <= MAX_HOUSE_EDGE && playerNumber >= MIN_NUMBER && playerNumber <= MAX_NUMBER) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @dev Check whether token exchange is allowed
	 * @param playerAddress The player address to be checked
	 * @param tokenAmount The amount of token to be exchanged
	 * @return Return true if yes, false otherwise.
	 */
	function isExchangeAllowed(address playerAddress, uint256 tokenAmount) public constant returns (bool) {
		if (_boolSettings['gamePaused'] == false && _boolSettings['tokenExchangePaused'] == false && _uintSettings['contractBalanceHonor'] >= _uintSettings['tokenExchangeMinBankrollHonor'] && _uintSettings['tokenToWeiExchangeRateHonor'] > 0 && _spintoken.getBalanceOf(playerAddress) >= tokenAmount) {
			return true;
		} else {
			return false;
		}
	}

	/******************************************/
	/*        INTERNAL ONLY METHODS           */
	/******************************************/

	/**
	 * @dev Calculates and sets the latest max profit a bet can possibly earn. Also update the honor variables that we are going to promise players.
	 * @param force If true, bypass the block security check and update honor settings
	 */
	function _setMaxProfit(bool force) internal {
		_uintSettings['maxProfit'] = _uintSettings['contractBalance'].mul(_uintSettings['maxProfitAsPercentOfHouse']).div(PERCENTAGE_DIVISOR);
		if (force || block.number > _uintSettings['maxProfitBlockNum'].add(_uintSettings['maxProfitBlockSecurityCount'])) {
			if (_uintSettings['contractBalance'] < 10 ether) {
				_uintSettings['maxProfitAsPercentOfHouse'] = 200000; // 20%
			} else if (_uintSettings['contractBalance'] >= 10 ether && _uintSettings['contractBalance'] < 100 ether) {
				_uintSettings['maxProfitAsPercentOfHouse'] = 100000; // 10%
			} else if (_uintSettings['contractBalance'] >= 100 ether && _uintSettings['contractBalance'] < 1000 ether) {
				_uintSettings['maxProfitAsPercentOfHouse'] = 50000; // 5%
			} else {
				_uintSettings['maxProfitAsPercentOfHouse'] = 10000; // 1%
			}
			_uintSettings['maxProfitHonor'] = _uintSettings['maxProfit'];
			_uintSettings['contractBalanceHonor'] = _uintSettings['contractBalance'];
			_uintSettings['minBankrollHonor'] = _uintSettings['minBankroll'];
			_uintSettings['tokenExchangeMinBankrollHonor'] = _uintSettings['tokenExchangeMinBankroll'];
			_uintSettings['totalWeiLostHonor'] = _uintSettings['totalWeiLost'];
			_uintSettings['maxProfitBlockNum'] = block.number;
		}
	}

	/**
	 * @dev Updates token to Wei exchange rate.
	 * The exchange rate will be updated everytime there is a transaction happens in spinwin.
	 * If contract is killed, we don't need to do anything
	 */
	function _updateTokenToWeiExchangeRate() internal {
		if (!_boolSettings['contractKilled']) {
			if (_uintSettings['contractBalance'] >= _uintSettings['tokenExchangeMinBankroll'] && _spintoken.getTotalSupply() > 0) {
				/**
				 * tokenToWeiExchangeRate is ETH in 36 decimals or WEI in 18 decimals to account for
				 * the state when token's totalSupply is 10^18 more than contractBalance.
				 * Otherwise the tokenToWeiExchangeRate will always be 0.
				 * This means, in the exchange token function, we need to divide
				 * tokenToWeiExchangeRate with CURRENCY_DIVISOR
				 */
				_uintSettings['tokenToWeiExchangeRate'] = ((_uintSettings['contractBalance'].sub(_uintSettings['tokenExchangeMinBankroll'])).mul(CURRENCY_DIVISOR).mul(CURRENCY_DIVISOR).div(_uintSettings['exchangeAmountDivisor'])).div(_spintoken.getTotalSupply().mul(_uintSettings['tokenExchangeRatio']).div(TWO_DECIMALS));
			} else {
				_uintSettings['tokenToWeiExchangeRate'] = 0;
			}

			if (block.number > _uintSettings['tokenToWeiExchangeRateBlockNum'].add(_uintSettings['tokenExchangeBlockSecurityCount'])) {
				_uintSettings['tokenToWeiExchangeRateHonor'] = _uintSettings['tokenToWeiExchangeRate'];
				_uintSettings['tokenToWeiExchangeRateBlockNum'] = block.number;
				emit LogUpdateTokenToWeiExchangeRate(_uintSettings['tokenToWeiExchangeRateHonor'], _uintSettings['tokenToWeiExchangeRateBlockNum']);
			}
		}
	}
}
/**
 * @title GameSetting
 */
function spinwinUpdateTokenToWeiExchangeRate() public onlySpinwin {
	_updateTokenToWeiExchangeRate();
}

/**
 	 * @dev Allow spinwin to update token to Wei exchange rate.
 	 */