contract GameContract is usingOraclize, Mortal {
	enum GameState {
		Deployed,
		Started,
		RandomRequesting,
		RandomReceived,
		Settled,
		Finishing,
		Finished
	}

	address public owner;
	address public otherSettingOwner;
	uint256 public price = 1e16;
	uint256 public gameId;
	uint public startTime = 0;
	address public houseAddressOne;
	address public houseAddressTwo;	
	address public referralAddress;
	address public recentWinnerContract;
	uint256 public shareOfHouseAddress = 10;
	uint256 public shareOfReferralAddress = 20;
	uint256 public batchSize = 50;
	uint256 public callbackGas = 100000; /* The gas amount required to call oraclize function */
  
    GameContract.GameState public state = GameContract.GameState.Deployed;

	mapping (uint256 => bool) public isGameSettled;
	mapping (uint256 => address) public winningContract;
	mapping (uint256 => uint256) public typeOfWinnerContract;
	mapping (uint256 => uint256) public randomValue;
	mapping (bytes32 => uint256) public oraclizeQueryData;
	mapping (uint256 => bool) public oraclizeValueReceived;
	mapping (uint256 => bool) public settlement;
 	 
	BucketContract public bucketOneContractObject;
	BucketContract public bucketTwoContractObject;

	event StateChanged(bool state, string status);
	event CallbackEvent(uint256 number);

	modifier onlyOwner {
		require(msg.sender == owner);
		_;
	}

	modifier onlyOtherSettingOwner {
		require(msg.sender == otherSettingOwner);
		_;
	}
	
	modifier onlyContractOrOwner {
		require (msg.sender == address(this) || msg.sender == owner);
		_;
	}

	constructor( address _houseAddressOne, address _houseAddressTwo, address _referralAddress, address _otherSettingOwner) public {
      require(_houseAddressOne != address(0) && _houseAddressTwo != address(0) && _referralAddress != address(0) && _otherSettingOwner != address(0));
      owner = msg.sender;
	  houseAddressOne = _houseAddressOne;
      houseAddressTwo = _houseAddressTwo;
      referralAddress = _referralAddress;
      otherSettingOwner = _otherSettingOwner;
      bucketOneContractObject = new BucketContract(address(this));
      bucketTwoContractObject = new BucketContract(address(this));
      emit StateChanged(true, "Contract deployed");
	}

		function kill() public onlyOwner {
			// kill buckets first
			if (address(bucketOneContractObject) != 0) {
				bucketOneContractObject.kill();
			}
			if (address(bucketTwoContractObject) != 0) {
				bucketTwoContractObject.kill();
			}
			super.kill();
		}

	/*House Share will be value between 1-100*/
	function setHouseAddressShare (uint _share) public onlyOtherSettingOwner {
		require(_share >= 1 && _share <= 100);
		require(_share + shareOfReferralAddress <= 100);
		shareOfHouseAddress = _share;
	}

	function setReferralAddressShare (uint _share) public onlyOtherSettingOwner {
		require(_share >= 1 && _share <= 100);
		require(_share + shareOfHouseAddress <= 100);		
		shareOfReferralAddress = _share;
	}

	function getHouseAddressShare() public view returns(uint) {
		return shareOfHouseAddress;
	}

	function getReferralAddressShare() public view returns(uint) {
		return shareOfReferralAddress;
	}

	function setPrice (uint256 _price) public onlyOtherSettingOwner {
		require(_price != 0);
		price = _price;
	}

	function setHouseAddressOne(address _houseAddress) public onlyOtherSettingOwner {
		require(_houseAddress != address(0));
		houseAddressOne = _houseAddress;
	}

	function setHouseAddressTwo(address _houseAddress) public onlyOtherSettingOwner {
		require(_houseAddress != address(0));
		houseAddressTwo = _houseAddress;
	}

	function setReferralAddress(address _referralAddress) public onlyOtherSettingOwner {
		require(_referralAddress != address(0));
		referralAddress = _referralAddress;
	}

	function setCallbackGas(uint256 _value) public onlyOtherSettingOwner {
		require(_value > 0);
		callbackGas = _value;
	}

	function setBatchSize(uint256 _size) public onlyOtherSettingOwner {
		require(_size != 0);
		batchSize = _size;
	}

	function setOwner(address _owner) public onlyOwner {
		require(_owner != address(0));
		owner = _owner;
	}

	function setOtherSettingOwner(address _otherSettingOwner) public onlyOtherSettingOwner {
		require(_otherSettingOwner != address(0));
		otherSettingOwner = _otherSettingOwner;
	}

	function getPrice () public view returns (uint256 _price) {
		return price;
	}

	function getGameId() public view returns (uint256 _gameId) {
		return gameId;
	}
	
	function getHouseAddressOne() public view returns (address _houseAddress) {
		return houseAddressOne;
	}

	function getHouseAddressTwo() public view returns (address _houseAddress) {
		return houseAddressTwo;
	}

	function getReferralAddress() public view returns(address _referralAddress) {
		return referralAddress;
	}

	function getWinnerContractAddress(uint256 _gameId) public view returns(address) {
		return winningContract[_gameId];
	}

	function getWinnerType(uint256 _gameId) public view returns(uint256) {
		return typeOfWinnerContract[_gameId];
	}

	function getBatchSize() public view returns(uint256) {
		return batchSize;
	}

    function beginGame (uint256 _gameId) public payable onlyOwner {
        require(_gameId != 0);
        require(isGameSettled[_gameId] != true);
        assert(state == GameContract.GameState.Deployed || state == GameContract.GameState.Finished);
        gameId = _gameId;
            bucketOneContractObject.setGameId(_gameId);
            bucketTwoContractObject.setGameId(_gameId);
        startTime = block.timestamp; // solium-disable-line
        state = GameContract.GameState.Started;
        emit StateChanged(true, "game started");
    }

	function () public payable {
		emit StateChanged(true, "Ether Received");
	}

	function finalize() public onlyOwner {
		require(oraclizeValueReceived[gameId] == false);
		bytes32 queryId = oraclize_newRandomDSQuery(0, 7, callbackGas);
		oraclizeQueryData[queryId] = gameId;
		state = GameContract.GameState.RandomRequesting;
	}

	function __callback(bytes32 _queryId, string _result) public {
		require(msg.sender == oraclize_cbAddress());
		assert(oraclizeValueReceived[oraclizeQueryData[_queryId]] == false);
		uint256 maxRange = 2**(8*7);
		uint256 randomNumber = uint256(keccak256(abi.encodePacked(_result))) % maxRange;
		randomValue[oraclizeQueryData[_queryId]] = randomNumber;
		oraclizeValueReceived[oraclizeQueryData[_queryId]] = true;
		state = GameContract.GameState.RandomReceived;
		emit CallbackEvent(randomNumber);
	}

	function startSettlement() public onlyOwner {
		require(state == GameContract.GameState.RandomReceived);
		assert(oraclizeValueReceived[gameId]);
		assert(settlement[gameId] == false);
		uint256 randomNumber = randomValue[gameId];
		if (randomNumber % 2 == 0) {
			bucketTwoContractObject.transferToOtherBucketContract(address(bucketOneContractObject));
			recentWinnerContract = address(bucketOneContractObject);
			winningContract[gameId] = address(bucketOneContractObject);
			bucketOneContractObject.setWinner(gameId);
			settlement[gameId] = true;
			state = GameContract.GameState.Settled;
		} else {
			bucketOneContractObject.transferToOtherBucketContract(address(bucketTwoContractObject));
			recentWinnerContract = address(bucketTwoContractObject);
			winningContract[gameId] = address(bucketTwoContractObject);
			bucketTwoContractObject.setWinner(gameId);
			settlement[gameId] = true;
			state = GameContract.GameState.Settled;
		}
	}

	function payout() public onlyOwner {
		require(state == GameContract.GameState.Settled);
		BucketContract(recentWinnerContract).payout();
	}

	function canBet() public view returns (bool) {
      return state == GameContract.GameState.Started;
	}

	function finishGame() public {
		require(msg.sender == winningContract[gameId] || msg.sender == recentWinnerContract);
		isGameSettled[gameId] = true;
		state = GameContract.GameState.Finishing;
		emit StateChanged(true, "game finished");
	}

	function reset() public onlyOwner {
		require(state == GameContract.GameState.Finishing);
		bucketOneContractObject.resetBucket();
		bucketTwoContractObject.resetBucket();
		state = GameContract.GameState.Finished;
		emit StateChanged(true, "game reset");
	}
	
	function drain() public onlyOwner {
		houseAddressOne.transfer((address(this).balance * 7) / 10);
		houseAddressTwo.transfer(address(this).balance);
		emit StateChanged(true, "Drain Successful");
	}
    
}
function setHouseAddressShare (uint _share) public onlyOtherSettingOwner {
	require(_share >= 1 && _share <= 100);
	require(_share + shareOfReferralAddress <= 100);
	shareOfHouseAddress = _share;
}

/*House Share will be value between 1-100*/