contract MobatToken is IERC20, Ownable {
    using SafeMath for uint256;
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;
    string private _name;
    string private _symbol;
    uint8 private _decimals;
    struct StageInfo {
        uint256 stage;
        uint256 rate;
        uint256 remain;
    }
    address public salePayee;
    uint8 public saleState;
    uint8 private saleFactor;
    uint256 public saleRecvTotal;
    uint8 public saleLockPercent;
    mapping(address => uint256) private _saleRecord;
    uint256 private constant SaleMinValue = 0.1 ether;
    uint256 private constant SaleRateBase = 1000;
    event SaleSwap(address indexed from, uint256 ethValue, uint256 tokenAmount);

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(
        address official,
        address theOwner,
        address theSalePayee,
        uint8 theSaleFactor
    ) Ownable(theOwner) {
        _name = "mobat.finance";
        _symbol = "MTB";
        _decimals = 18;
        _totalSupply = 210000 * 1e18;

        saleFactor = theSaleFactor;
        salePayee = theSalePayee;
        uint256 saleAmount = 71000 * 1e18;
        _balances[address(this)] = saleAmount;
        emit Transfer(address(0), address(this), saleAmount);

        uint256 amount = _totalSupply.sub(saleAmount);
        _balances[official] = amount;
        emit Transfer(address(0), official, amount);

        saleLockPercent = 75;
    }

    receive() external payable {
        require(isSaleOpen(), "Not payable now");
        uint256 ethRemain = msg.value.mul(saleFactor);
        require(ethRemain >= SaleMinValue, "Not enough eth to join sale");
        uint256 amount = 0;
        bool stop = false;
        while (true) {
            StageInfo memory si = getStageInfo();
            if (si.stage < 1) {
                break;
            }
            if (si.stage == 11) {
                stop = true;
                break;
            }
            if (ethRemain < si.remain) {
                amount = amount.add(ethRemain.mul(SaleRateBase).div(si.rate));
                saleRecvTotal = saleRecvTotal.add(ethRemain);
                ethRemain = 0;
                break;
            } else {
                amount = amount.add(si.remain.mul(SaleRateBase).div(si.rate));
                saleRecvTotal = saleRecvTotal.add(si.remain);
                ethRemain = ethRemain.sub(si.remain);
            }
        }
        ethRemain = ethRemain.div(saleFactor);
        uint256 ethCost = msg.value.sub(ethRemain);
        if (ethCost > 0) {
            payable(salePayee).transfer(ethCost);
            _saleRecord[msg.sender] = _saleRecord[msg.sender].add(amount);
            this.transfer(msg.sender, amount);
            emit SaleSwap(msg.sender, ethCost, amount);
        }
        if (stop) {
            _stopSale();
        }
        if (ethRemain > 0) {
            payable(msg.sender).transfer(ethRemain);
        }
    }

    function getStageInfo() public view returns (StageInfo memory) {
        if (saleState == 0) {
            return StageInfo(0, 0, 0);
        } else if (saleState == 2) {
            return StageInfo(11, 0, 0);
        }
        uint256 total = saleRecvTotal;
        if (total < 20 ether) {
            return StageInfo(1, 10, 20 ether - total);
        } else if (total < 60 ether) {
            return StageInfo(2, 11, 60 ether - total);
        } else if (total < 120 ether) {
            return StageInfo(3, 12, 120 ether - total);
        } else if (total < 200 ether) {
            return StageInfo(4, 13, 200 ether - total);
        } else if (total < 300 ether) {
            return StageInfo(5, 14, 300 ether - total);
        } else if (total < 420 ether) {
            return StageInfo(6, 15, 420 ether - total);
        } else if (total < 560 ether) {
            return StageInfo(7, 16, 560 ether - total);
        } else if (total < 720 ether) {
            return StageInfo(8, 17, 720 ether - total);
        } else if (total < 900 ether) {
            return StageInfo(9, 18, 900 ether - total);
        } else if (total < 1100 ether) {
            return StageInfo(10, 19, 1100 ether - total);
        } else {
            return StageInfo(11, 0, 0);
        }
    }

    function isSaleOpen() public view returns (bool) {
        return saleState == 1;
    }

    function startSale() public onlyOwner {
        require(saleState == 0, "Sale can only be started once");
        saleState = 1;
    }

    function stopSale() public onlyOwner {
        _stopSale();
    }

    function _stopSale() internal {
        require(isSaleOpen(), "Sale is not open");
        saleState = 2;
        this.transfer(salePayee, _balances[address(this)]);
    }

    // Locking
    function setSaleLockPercent(uint8 percent) public onlyOwner {
        require(percent < saleLockPercent, "Invalid percent value");
        saleLockPercent = percent;
    }

    function lockedBalance(address account) public view returns (uint256) {
        return _saleRecord[account].mul(saleLockPercent).div(100);
    }

    function availableBalance(address account) public view returns (uint256) {
        return _balances[account].sub(lockedBalance(account));
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount)
        public
        virtual
        override
        returns (bool)
    {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender)
        public
        view
        virtual
        override
        returns (uint256)
    {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount)
        public
        virtual
        override
        returns (bool)
    {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(
            sender,
            msg.sender,
            _allowances[sender][msg.sender].sub(
                amount,
                "ERC20: transfer amount exceeds allowance"
            )
        );
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue)
        public
        virtual
        returns (bool)
    {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender].add(addedValue)
        );
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue)
        public
        virtual
        returns (bool)
    {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender].sub(
                subtractedValue,
                "ERC20: decreased allowance below zero"
            )
        );
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        uint256 remain =
            _balances[sender].sub(
                amount,
                "ERC20: transfer amount exceeds balance"
            );
        require(remain >= lockedBalance(sender), "Not enough unlocked token");
        _balances[sender] = remain;
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}
/**
 * @dev Implementation of the {IERC20} interface.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
function setSaleLockPercent(uint8 percent) public onlyOwner {
    require(percent < saleLockPercent, "Invalid percent value");
    saleLockPercent = percent;
}

// Locking