contract DAO1PublicSale  is Ownable {
    using SafeMath for uint256;
    event ClaimableAmount(address _user, uint256 _claimableAmount);
    // address public owner;
    uint256 public rate;
    uint256 public allowedUserBalance;
    bool public presaleOver;
    IERC20 public usdt;
    mapping(address => uint256) public claimable;
    uint256 public hardcap;
    constructor(uint256 _rate, address _usdt, uint256 _hardcap, uint256 _allowedUserBalance)  {
        rate = _rate;
        usdt = IERC20(_usdt);
        presaleOver = true;
        // owner = msg.sender;
        hardcap = _hardcap;
        allowedUserBalance = _allowedUserBalance;
    }
    modifier isPresaleOver() {
        require(presaleOver == true, "The presale is not over");
        _;
    }
    function changeHardCap(uint256 _hardcap) onlyOwner public {
        hardcap = _hardcap;
    }
    function changeAllowedUserBalance(uint256 _allowedUserBalance) onlyOwner public {
        allowedUserBalance = _allowedUserBalance;
    }
    function endPresale() external onlyOwner returns (bool) {
        presaleOver = true;
        return presaleOver;
    }
    function startPresale() external onlyOwner returns (bool) {
        presaleOver = false;
        return presaleOver;
    }
    function buyTokenWithUSDT(uint256 _amount) external {
        // user enter amount of ether which is then transfered into the smart contract and tokens to be given is saved in the mapping
        require(presaleOver == false, "presale is over you cannot buy now");
        uint256 tokensPurchased = _amount.mul(rate);
        uint256 userUpdatedBalance = claimable[msg.sender].add(tokensPurchased);
        require( _amount.add(usdt.balanceOf(address(this))) <= hardcap, "Hardcap for the tokens reached");
        // for USDT
        require(userUpdatedBalance.div(rate) <= allowedUserBalance, "Exceeded allowed user balance");
        // usdt.transferFrom(msg.sender, address(this), _amount);
        doTransferIn(address(usdt), msg.sender, _amount);
        claimable[msg.sender] = userUpdatedBalance;
        emit ClaimableAmount(msg.sender, tokensPurchased);
    }
    // function claim() external isPresaleOver {
    //     // it checks for user msg.sender claimable amount and transfer them to msg.sender
    //     require(claimable[msg.sender] > 0, "NO tokens left to be claim");
    //     usdc.transfer(msg.sender, claimable[msg.sender]);
    //     claimable[msg.sender] = 0;
    // }
    function doTransferIn(
        address tokenAddress,
        address from,
        uint256 amount
    ) internal returns (uint256) {
        INonStandardERC20 _token = INonStandardERC20(tokenAddress);
        uint256 balanceBefore = INonStandardERC20(tokenAddress).balanceOf(address(this));
        _token.transferFrom(from, address(this), amount);
        bool success;
        assembly {
            switch returndatasize()
                case 0 {
                    // This is a non-standard ERC-20
                    success := not(0) // set success to true
                }
                case 32 {
                    // This is a compliant ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0) // Set success = returndata of external call
                }
                default {
                    // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        require(success, "TOKEN_TRANSFER_IN_FAILED");
        // Calculate the amount that was actually transferred
        uint256 balanceAfter = INonStandardERC20(tokenAddress).balanceOf(address(this));
        require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW");
        return balanceAfter.sub(balanceBefore); // underflow already checked above, just subtract
    }
    function doTransferOut(
        address tokenAddress,
        address to,
        uint256 amount
    ) internal {
        INonStandardERC20 _token = INonStandardERC20(tokenAddress);
        _token.transfer(to, amount);
        bool success;
        assembly {
            switch returndatasize()
                case 0 {
                    // This is a non-standard ERC-20
                    success := not(0) // set success to true
                }
                case 32 {
                    // This is a complaint ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0) // Set success = returndata of external call
                }
                default {
                    // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        require(success, "TOKEN_TRANSFER_OUT_FAILED");
    }
    function fundsWithdrawal(uint256 _value) external onlyOwner isPresaleOver {
        // claimable[owner] = claimable[owner].sub(_value);
        // usdt.transfer(_msgSender(), _value);
        doTransferOut(address(usdt), _msgSender(), _value);
    }
}
function doTransferIn(
        address tokenAddress,
        address from,
        uint256 amount
    ) internal returns (uint256) {
        INonStandardERC20 _token = INonStandardERC20(tokenAddress);
        uint256 balanceBefore = INonStandardERC20(tokenAddress).balanceOf(address(this));
        _token.transferFrom(from, address(this), amount);
        bool success;
        assembly {
            switch returndatasize()
                case 0 {
                    // This is a non-standard ERC-20
                    success := not(0) // set success to true
                }
                case 32 {
                    // This is a compliant ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0) // Set success = returndata of external call
                }
                default {
                    // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        require(success, "TOKEN_TRANSFER_IN_FAILED");
        // Calculate the amount that was actually transferred
        uint256 balanceAfter = INonStandardERC20(tokenAddress).balanceOf(address(this));
        require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW");
        return balanceAfter.sub(balanceBefore); // underflow already checked above, just subtract
    }
// function claim() external isPresaleOver {
//     // it checks for user msg.sender claimable amount and transfer them to msg.sender
//     require(claimable[msg.sender] > 0, "NO tokens left to be claim");
//     usdc.transfer(msg.sender, claimable[msg.sender]);
//     claimable[msg.sender] = 0;
// }