contract Bonus is Ownable{
	using SafeMath for uint256;

	uint256 public constant day = 24*60*60;

	uint256 public publicSale_first_stage_endTime;

	mapping(uint8 => uint256) public bonus_time_gate;
	mapping(uint8 => uint8) public bonus_rate;

	event UpdateBonusPhase(address indexed updater, uint8 indexed phase_type, uint256 time_gate, uint8 bonus);
	event UpdatePublicSaleFirstStageEndTime(address indexed updater, uint256 publicSale_first_stage_endTime);

	constructor(
		uint256 _publicSale_first_stage_endTime,
		uint256 _bonus_time_gate_1, 
		uint256 _bonus_time_gate_2,
		uint256 _bonus_time_gate_3, 
		uint256 _bonus_time_gate_4,
		uint8 _bonus_rate_1, 
		uint8 _bonus_rate_2,
		uint8 _bonus_rate_3,
		uint8 _bonus_rate_4) public
	{
		bonus_time_gate[0] = _bonus_time_gate_1*uint256(day);
		bonus_time_gate[1] = _bonus_time_gate_2*uint256(day);
		bonus_time_gate[2] = _bonus_time_gate_3*uint256(day);
		bonus_time_gate[3] = _bonus_time_gate_4*uint256(day);

		bonus_rate[0] = _bonus_rate_1;
		bonus_rate[1] = _bonus_rate_2;
		bonus_rate[2] = _bonus_rate_3;
		bonus_rate[3] = _bonus_rate_4;
	
		publicSale_first_stage_endTime = _publicSale_first_stage_endTime;

		emit UpdateBonusPhase(msg.sender, 1, _bonus_time_gate_1, _bonus_rate_1);
		emit UpdateBonusPhase(msg.sender, 2, _bonus_time_gate_2, _bonus_rate_2);
		emit UpdateBonusPhase(msg.sender, 3, _bonus_time_gate_3, _bonus_rate_3);
		emit UpdateBonusPhase(msg.sender, 4, _bonus_time_gate_4, _bonus_rate_4);
	}

	/**
	* @dev called for get bonus rate
	* @param _phase_type uint8 bonus phase block
	* @return A uint8, bonus rate that if the operation was successful.
	*/
	function getBonusRate(uint8 _phase_type) public view returns(uint8){
		return bonus_rate[_phase_type];
	}

	/**
	* @dev called for get bonus time block
	* @param _phase_type uint8 bonus phase block
	* @return A uint8, phase block time that if the operation was successful.	
	*/
	function getBonusTimeGate(uint8 _phase_type) public view returns(uint256){
		return bonus_time_gate[_phase_type];
	}

	/**
	* @dev called for get the public sale first stage end time
	* @return A uint256, the public sale first stage end time that if the operation was successful.	
	*/
	function getPublicSaleFirstStageEndTime() public view returns(uint256){
		return publicSale_first_stage_endTime;
	}

	/**
	* @dev called for get total token amount that include the bonus
	* @param _waiting_time uint256 KYV waiting time
	* @param _tokenAmount uint256 basic token amount
	* @return A uint256, total token amount that if the operation was successful.		
	*/
	function getTotalAmount(uint256 _waiting_time, uint256 _tokenAmount) public view returns(uint256){
		uint256 total_token_amount;

		if(_waiting_time < bonus_time_gate[0]){
			//user still can get bonus if user purchase token before publicSale first stage end time.
			if(now <= publicSale_first_stage_endTime){
				total_token_amount = _tokenAmount + (_tokenAmount * uint256(bonus_rate[0])) / 100;
			}else{
				total_token_amount = _tokenAmount;
			}
		}else if(_waiting_time < bonus_time_gate[1]){
			total_token_amount = _tokenAmount + (_tokenAmount * uint256(bonus_rate[0])) / 100;
		}else if(_waiting_time < bonus_time_gate[2]){
			total_token_amount = _tokenAmount + (_tokenAmount * uint256(bonus_rate[1])) / 100;
		}else if(_waiting_time < bonus_time_gate[3]){
			total_token_amount = _tokenAmount + (_tokenAmount * uint256(bonus_rate[2])) / 100;
		}else{
			total_token_amount = _tokenAmount + (_tokenAmount * uint256(bonus_rate[3])) / 100;
		}

		return total_token_amount;
	}
	
	/**
	* @dev called for update the public sale first stage end time
	* @param _new_stage_endTime uint256 new public sale first stage end time
	* @return A uint256, the public sale first stage end time that if the operation was successful.	
	*/
	function updatePublicSaleFirstStageEndTime(uint256 _new_stage_endTime)onlyOwner public returns(bool){
		publicSale_first_stage_endTime = _new_stage_endTime;

		emit UpdatePublicSaleFirstStageEndTime(msg.sender, _new_stage_endTime);
		return true;
	}

	/**
	* @dev called for update bonus phase time block and rate
	* @param _phase_type uint8 phase block
	* @param _new_days uint256 new phase block time
	* @param _new_bonus uint8 new rate
	* @return A bool that if the operation was successful.
	*/
	function updateBonusPhase(uint8 _phase_type, uint256 _new_days, uint8 _new_bonus)onlyOwner public returns(bool){

		uint256 _new_gate = _new_days * uint256(day);

		//gate phase only have 4 phase
		require(0 < _phase_type && _phase_type <= 4);

		//gate phase 1
		if(_phase_type == 1){

			//new gate time needs to be early than the next gate time
			require( _new_gate < bonus_time_gate[1] );

			//new gate rate needs to be less than the next gate's rate
			require( _new_bonus < bonus_rate[1] );

			bonus_time_gate[0] = _new_gate;
			bonus_rate[0] = _new_bonus;
		}else if(_phase_type == 2){

			//new gate time needs to be early than the next gate time and need to be late than the perious gate time
			require( bonus_time_gate[0] < _new_gate && _new_gate < bonus_time_gate[2] );

			//new gate rate needs to be less than the next gate's rate and need to be bigger than the perious gate rate
			require( bonus_rate[0] < _new_bonus && _new_bonus < bonus_rate[2] );

			bonus_time_gate[1] = _new_gate;
			bonus_rate[1] = _new_bonus;
		}else if(_phase_type == 3){

			require( bonus_time_gate[1] < _new_gate && _new_gate < bonus_time_gate[3] );
			require( bonus_rate[1] < _new_bonus && _new_bonus < bonus_rate[3] );

			bonus_time_gate[2] = _new_gate;
			bonus_rate[2] = _new_bonus;
		}else{

			require( bonus_time_gate[2] < _new_gate );
			require( bonus_rate[2] < _new_bonus );

			bonus_time_gate[3] = _new_gate;
			bonus_rate[3] = _new_bonus;
		}

		emit UpdateBonusPhase(msg.sender, _phase_type, _new_days, _new_bonus);
	}
}
function updateBonusPhase(uint8 _phase_type, uint256 _new_days, uint8 _new_bonus)onlyOwner public returns(bool){

	uint256 _new_gate = _new_days * uint256(day);

	//gate phase only have 4 phase
	require(0 < _phase_type && _phase_type <= 4);

	//gate phase 1
	if(_phase_type == 1){

		//new gate time needs to be early than the next gate time
		require( _new_gate < bonus_time_gate[1] );

		//new gate rate needs to be less than the next gate's rate
		require( _new_bonus < bonus_rate[1] );

		bonus_time_gate[0] = _new_gate;
		bonus_rate[0] = _new_bonus;
	}else if(_phase_type == 2){

		//new gate time needs to be early than the next gate time and need to be late than the perious gate time
		require( bonus_time_gate[0] < _new_gate && _new_gate < bonus_time_gate[2] );

		//new gate rate needs to be less than the next gate's rate and need to be bigger than the perious gate rate
		require( bonus_rate[0] < _new_bonus && _new_bonus < bonus_rate[2] );

		bonus_time_gate[1] = _new_gate;
		bonus_rate[1] = _new_bonus;
	}else if(_phase_type == 3){

		require( bonus_time_gate[1] < _new_gate && _new_gate < bonus_time_gate[3] );
		require( bonus_rate[1] < _new_bonus && _new_bonus < bonus_rate[3] );

		bonus_time_gate[2] = _new_gate;
		bonus_rate[2] = _new_bonus;
	}else{

		require( bonus_time_gate[2] < _new_gate );
		require( bonus_rate[2] < _new_bonus );

		bonus_time_gate[3] = _new_gate;
		bonus_rate[3] = _new_bonus;
	}

	emit UpdateBonusPhase(msg.sender, _phase_type, _new_days, _new_bonus);
}

/**
 	* @dev called for update bonus phase time block and rate
 	* @param _phase_type uint8 phase block
 	* @param _new_days uint256 new phase block time
 	* @param _new_bonus uint8 new rate
 	* @return A bool that if the operation was successful.
 	*/