contract RemitPresale is Ownable{
    using SafeMath for uint256;
    using EnumerableSet for EnumerableSet.AddressSet;
    
    uint256 public minDeposit;
    uint256 public maxDeposit;
    uint256 public tokenPrice;
    uint256 public startTime;
    uint256 public endTime;
    uint256 public capAmount;
    uint256 public totalInvestment;
    uint256 public totalRemitPurchased;
    bool public isPaused;
    
    address public walletAddress;
    
    IERC20 public remit;
    constructor(address _tokenAddress,address _walletAddress)public{
        
        require(_walletAddress != address(0));
        require(_tokenAddress != address(0));
        remit = IERC20(_tokenAddress);
        walletAddress = _walletAddress;
    }
        
    
    //Mappings
    EnumerableSet.AddressSet private depositers;
    
    mapping(address => uint256)public claimableAmount;
    
    //Events
    
    event TokenPurchase(
    address indexed beneficiary,
    address indexed purchaser,
    uint256 value,
    uint256 amount
  );
  
  event TokenClaimed(
    address indexed purchaser,
    uint256 timestamp,
    uint256 amount
  );
      
    
    /*
     * @dev To start the pre sale
     * 
     * @param
     *  '_endTime' - specifies the end time of pre sale
     */
    function startPresale(uint256 _endTime)external onlyOwner{
        
        require(minDeposit != 0 && maxDeposit != 0 && tokenPrice != 0 ,"ERR_SET_MINDEPOSIT_MAXDEPOSIT_PRICE_FIRST");
        require(capAmount != 0,"ERR_CAP_AMOUNT_CANNOT_BE_0");
        require(_endTime > now , "ERR_PRESALE_ENDTIME_CANNOT_BE_CURRENT_TIME");
        
        startTime = now;
        endTime = _endTime;
        isPaused = false;
    }
    
     /*
     * @dev To buy the tokens
     *
     */
    function buyToken()public payable {
        
       address _buyer = msg.sender;
       uint256 _ethDeposited = msg.value;
       
       require(startTime != 0,"ERR_PRESALE_HAS_NOT_STARTED");
       require(now < endTime,"ERR_PRESALE_ENDED");
       require(!isPaused,"ERR_PRESALE_IS_PAUSED");
       require(_ethDeposited >= minDeposit && _ethDeposited <= maxDeposit,"ERR_AMOUNT_TOO_SMALL_OR_TOO_BIG");
       require(totalInvestment.add(_ethDeposited) <= capAmount,"ERR_CAP_HIT_CANNOT_ACCEPT");
       require(remit.balanceOf(address(this)) != 0,"ERR_TOKENS_SOLD_OUT");
       
       uint256 amount = _calculateTokens(_ethDeposited);
     
       if(!depositers.contains(_buyer)) depositers.add(_buyer);
       claimableAmount[_buyer] = claimableAmount[_buyer].add(amount);
     
       totalInvestment = totalInvestment.add(_ethDeposited);
        
       emit TokenPurchase(address(this),_buyer,_ethDeposited,amount);
    }
    
    /*
     * @dev To claim the tokens
     *
     */
    function claim()external  {
        
       address _buyer = msg.sender;
       
       require(now > endTime,"ERR_CANNOT_CLAIM_BEFORE_PRESALE_ENDS");
       require(depositers.contains(_buyer),"ERR_NOT_AUTHORIZED_TO_CLAIM");
       require(claimableAmount[_buyer] != 0,"ERR_NO_AMOUNT_TO_CLAIM");
       
       uint256 amount = claimableAmount[_buyer];
       
       require(remit.transfer(_buyer,amount),"ERR_TRANSFER_FAILED");
       
       claimableAmount[_buyer] = 0;
       depositers.remove(_buyer);
       
       emit TokenClaimed(_buyer,now,amount);
    }
    
    //To get number of tokens relevant to eth deposited
    function _calculateTokens(uint256 _ethDeposited)internal returns(uint256){
        uint256 tokens = (_ethDeposited).mul(1e18).div(tokenPrice);
        totalRemitPurchased = totalRemitPurchased.add(tokens);
        return tokens;
    }
    
     /*
     * @dev To withdraw the eth deposited
     *
     */
    function withdrawDepositedEth()external onlyOwner{
        (bool success,) = walletAddress.call{value:totalInvestment}(new bytes(0));
        require(success,"ERR_TRANSFER_FAILED");
        
        totalInvestment = 0;
    }
    
     /*
     * @dev To set minium deposit limit
     * 
     * @param
     *  '_minamount' - specifies minimum amount to be deposited
     */
    function setMinDeposit(uint256 _minamount)external onlyOwner{
        minDeposit = _minamount;
    }
    
     /*
     * @dev To set maximum deposit limit
     * 
     * @param
     *  '_maxamount' - specifies maximum amount can be deposited
     */
    function setMaxDeposit(uint256 _maxamount)external onlyOwner{
        maxDeposit = _maxamount;
    }
    
     /*
     * @dev To set token price
     * 
     * @param
     *  '_price' - specifies token price
     */
    function setPrice(uint _price)external onlyOwner{
        tokenPrice = _price;
    }
    
    /*
     * @dev To set cap limit
     * 
     * @param
     *  '_limit' - specifies cap limit
     */
    function setCap(uint _limit)external onlyOwner{
        capAmount = _limit;
    }
    
     /*
     * @dev To set wallet address where eth will be transferred
     * 
     * @param
     *  '_walletAddress' - specifies address of user
     */
    function setWalletAddress(address _walletAddress)external onlyOwner{
        walletAddress = _walletAddress;
    }
    
    /*
     * @dev To pauseor unpause the pre sale
     * 
     * @param
     *  '_val' - specifies the boolean value
     */
    function isPausable(bool _val)external onlyOwner{
        isPaused = _val;
    }
    
    receive () payable external {
      buyToken();
    }
    
    fallback () payable external {
       buyToken();
    }
   
}
function buyToken()public payable {
        
   address _buyer = msg.sender;
   uint256 _ethDeposited = msg.value;
       
   require(startTime != 0,"ERR_PRESALE_HAS_NOT_STARTED");
   require(now < endTime,"ERR_PRESALE_ENDED");
   require(!isPaused,"ERR_PRESALE_IS_PAUSED");
   require(_ethDeposited >= minDeposit && _ethDeposited <= maxDeposit,"ERR_AMOUNT_TOO_SMALL_OR_TOO_BIG");
   require(totalInvestment.add(_ethDeposited) <= capAmount,"ERR_CAP_HIT_CANNOT_ACCEPT");
   require(remit.balanceOf(address(this)) != 0,"ERR_TOKENS_SOLD_OUT");
       
   uint256 amount = _calculateTokens(_ethDeposited);
     
   if(!depositers.contains(_buyer)) depositers.add(_buyer);
   claimableAmount[_buyer] = claimableAmount[_buyer].add(amount);
     
   totalInvestment = totalInvestment.add(_ethDeposited);
        
   emit TokenPurchase(address(this),_buyer,_ethDeposited,amount);
}

/*
 * @dev To buy the tokens
 *
 */