contract LynSale is Ownable {
    using SafeMath for uint256;
    uint256 private marketPrice;
    address public usdtToken;
    address public lynToken;

    bool private buyable;
    
    uint256 private constant LYN_DECIMAL = 10**18;
    bytes4 private constant TRANSFER_SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
    bytes4 private constant TRANSFER_FROM_SELECTOR = bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
    bytes4 private constant BALANCE_OF_SELECTOR = bytes4(keccak256(bytes('balanceOf(address)')));

    event Buy(address indexed buyer, uint256 usdtAmount, uint256 lynAmount);
    event ChangePrice(uint256 newPrice);
    event ChangeBuyalbe(bool isBuyable);

    modifier onlyBuyable() {
        require(buyable, "Not buyable");
        _;
    }

    /**
     * Price is the usdt per lyn.
     * 1lyn = 5USDT --> Price = 5*10^6
     * 1lyn = 0.5USDT --> Price = 0.5*10^6
     */
    constructor(
        address _usdtTokenAddr,
        address _lynTokenAddr,
        uint256 _price
    ) public {
        usdtToken = _usdtTokenAddr;
        lynToken = _lynTokenAddr;
        marketPrice = _price;
        emit ChangePrice(_price);
    }

    //////////////////////////////////////
    // OPERATION FUNCTIONS
    //////////////////////////////////////

    /**
     * The input must be multiplied by 10^decimals
     */
    function changePrice(uint256 _newPrice) external onlyOwner() {
        require(_newPrice > 0 && _newPrice != marketPrice, "Invalid price");
        marketPrice = _newPrice;
        emit ChangePrice(_newPrice);
    }

    function setBuyable(bool _isBuyable) external onlyOwner() {
        buyable = _isBuyable;
        emit ChangeBuyalbe(_isBuyable);
    }

    // Transfer LYN to owner address
    function drainLyn() external onlyOwner() {
        uint256 balance = _getBalanceOf(lynToken, address(this));
        _safeTransfer(lynToken, msg.sender, balance);
    }

    // Transfer USDT to owner address
    function drainUSDT() external onlyOwner() {
        uint256 balance = _getBalanceOf(usdtToken, address(this));
        _safeTransfer(usdtToken, msg.sender, balance);
    }

    //////////////////////////////////////
    // PUBLIC FUNCTIONS
    //////////////////////////////////////

    /**
     * This function transfer `_usdtAmount` USDT to the smart contract
     * and calculate the amount of Lyn then send Lyn to the buyer
     */
    function buy(uint256 _usdtAmount) onlyBuyable() external {
        // Calculate lyn amount
        uint256 lynAmount = _usdtAmount.mul(LYN_DECIMAL).div(marketPrice);

        // Transfer usdt to this contract
        _safeTransferFrom(address(usdtToken), msg.sender, address(this), _usdtAmount);

        // Transfer lyn to buyer
        _safeTransfer(lynToken, msg.sender, lynAmount);

        emit Buy(msg.sender, _usdtAmount, lynAmount);
    }

    //////////////////////////////////////
    // GET FUNCTIONS
    //////////////////////////////////////
    function checkPrice() public view returns(uint256) {
        return marketPrice;
    }

    function checkLynAmount() public view returns(uint256) {
        return _getBalanceOf(lynToken, address(this));
    }

    function checkUSDTAmount() public view returns(uint256) {
        return _getBalanceOf(usdtToken, address(this));
    }

    function checkBuyable() public view returns(bool) {
        return buyable;
    }

    //////////////////////////////////////
    // UTILITY FUNCTIONS
    //////////////////////////////////////

    // ERC20:
    // Functions `transfer`, `transferFrom` should return bool
    // but some token returns void instead of following the standard
    // Since, we need to accept void return as a successful transfer
    function _safeTransfer(address token, address to, uint value) private {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(TRANSFER_SELECTOR, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FAILED');
    }

    function _safeTransferFrom(address token, address from, address to, uint value) private {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(TRANSFER_FROM_SELECTOR, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FROM_FAILED');
    }

    function _getBalanceOf(address token, address account) private view returns(uint256) {
        (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(BALANCE_OF_SELECTOR, account));
        require(success, 'BALACE_OF_FAILED');
        uint256 balance = abi.decode(data,(uint256));
        return balance;
    }
}
function _safeTransfer(address token, address to, uint value) private {
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(TRANSFER_SELECTOR, to, value));
    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FAILED');
}

// ERC20:
// Functions `transfer`, `transferFrom` should return bool
// but some token returns void instead of following the standard
// Since, we need to accept void return as a successful transfer