contract NftSale is Ownable {

	uint public constant MAX_UNITS_PER_TRANSACTION = 5;
	uint public constant MAX_NFT_TO_SELL = 7444;
	
	uint public constant INITIAL_PRICE = 3.3 ether;
	uint public constant FINAL_PRICE = 0.3 ether;
	uint public constant PRICE_DROP = 0.1 ether;
	uint public constant PRICE_DROP_TIME = 5 minutes;
	uint public constant START_TIME = 1634547907;
	
	address payable public receiver;
	
	INFT public nft;
	uint public tokensSold;

	constructor(address _nftAddress, address payable _receiverAddress) {
		nft = INFT(_nftAddress);
		
		//receiver - 0xD3db8094b50F2F094D164C1131BB9E604dfe0590
		receiver = _receiverAddress;
	}
	
	/*
	 * @dev calculate NFT price on current time. 
	 */
	function getCurrentPrice() public view returns(uint) {
		return getPriceOnTime(block.timestamp);
	}
	
	/*
	 * @dev calculate NFT price on exact time. 
	 * @param _time time for calculation
	 */
	function getPriceOnTime(uint _time) public pure returns(uint) {
		if(_time < START_TIME) {
			return 0;
		}
		uint maxRange = (INITIAL_PRICE - FINAL_PRICE) / PRICE_DROP;
		uint currentRange = (_time - START_TIME) / PRICE_DROP_TIME;

		if(currentRange >= maxRange) {
			return FINAL_PRICE;
		}
        
		return INITIAL_PRICE - (currentRange * PRICE_DROP);
	}

	/*
	 * @dev function to buy tokens. 
	 * @param _amount how much tokens can be bought.
	 */
	function buyBatch(uint _amount) external payable {
		require(block.timestamp >= START_TIME, "sale is not started yet");
		require(tokensSold + _amount <= MAX_NFT_TO_SELL, "exceed sell limit");
		require(_amount > 0, "empty input");
		require(_amount <= MAX_UNITS_PER_TRANSACTION, "exceed MAX_UNITS_PER_TRANSACTION");

		uint currentPrice = getCurrentPrice() * _amount;
		require(msg.value >= currentPrice, "too low value");
		if(msg.value > currentPrice) {
			//send the rest back
			(bool sent, ) = payable(msg.sender).call{value: msg.value - currentPrice}("");
        	require(sent, "Failed to send Ether");
		}
		
		tokensSold += _amount;
		nft.mintBatch(msg.sender, _amount);
		
		(bool sent, ) = receiver.call{value: address(this).balance}("");
        require(sent, "Something wrong with receiver");
// 		receiver.transfer(address(this).balance);
	}

	function cashOut(address _to) public onlyOwner {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        require(_to != address(0), "invalid address");
        
        (bool sent, ) = _to.call{value: address(this).balance}("");
        require(sent, "Failed to send Ether");
    }
}
function getPriceOnTime(uint _time) public pure returns(uint) {
		if(_time < START_TIME) {
			return 0;
		}
		uint maxRange = (INITIAL_PRICE - FINAL_PRICE) / PRICE_DROP;
		uint currentRange = (_time - START_TIME) / PRICE_DROP_TIME;

		if(currentRange >= maxRange) {
			return FINAL_PRICE;
		}
        
		return INITIAL_PRICE - (currentRange * PRICE_DROP);
	}
/*
 	 * @dev calculate NFT price on exact time. 
 	 * @param _time time for calculation
 	 */