contract POOR is Context, IERC20, Ownable {
    using SafeMath for uint256;
    using Address for address;

    mapping (address => bool) public _whitelistedUsers;
    mapping (address => uint256) public _rOwned;
    mapping (address => uint256) public _tOwned;
    mapping (address => mapping (address => uint256)) private _allowances;

    mapping (address => bool) private _isExcluded;
    address[] private _excluded;

    uint256 private constant MAX = ~uint256(0);
    uint256 constant private _tTotal = 100000000000000000000000000000000;
    uint256 private _rTotal = (MAX - (MAX % _tTotal));
    uint256 private _tFeeTotal;

    string private constant _name = 'Poor Quack';
    string private constant _symbol = 'POOR';
    uint8 private constant _decimals = 18;

    address payable public _wMarketing = 0x36E58a20C7b96B3503Dc9F2c6aba3af1f4876c83;

    //buy tax
    uint256 public _maxBuyTax = 12;   // max 12% total buy tax
    uint256 public _currentBuyTax = 10;   // 10% total buy tax
    uint256 public _totalBuyTax = 10;   // 10% total buy tax
    uint256 private _previousRefBuyTax = _totalBuyTax;

    //sell tax
    uint256 public _maxSellTax = 12;   // max 12% total buy tax
    uint256 public _currentSellTax = 12;   // 12% total sell tax
    uint256 public _totalSellTax = 12;   // 12% total sell tax
    uint256 private _previousRefSellTax = _totalSellTax;
    
    //breakup of 10% tax
    uint256 public _refPer = 0; // 0% for reflection

    //use percentage function so value is different to refPer, these values are after reflection has been done. 
    //reflection is zero, but can be changed in the future.
    uint256 public _autoLiqPer = 2000;  // 20% auto liqudity, marketing takes the remaining %, in this case 100-20 == 80%

    uint256 public _tokensCollectedAllTime;
    uint256 public _ethCollectedAllTime;
    uint256 public _liqAllTime;
    uint256 public _marketingAllTime;
                                     
    uint256 public _maxHoldAmount =  2000000000000000000000000000000; // 2.0 trillion
    uint256 public _maxTransAmount = 1300000000000000000000000000000; // 1.2 trillion
    uint256 public _minTokensForLiquidity = 30000000000000000000000000000; // 30 billion
    
    bool public _AutoTaxEnabled = false;
    bool public _lockLiquiditiesEnabled = false;
    bool public _timelock_enabled = false;
    bool public _whitelist_enabled = true;
    bool _inLockLiquidities;

    IUniswapV2Router02 public uniswapV2Router;
    address public uniswapV2Pair;

    enum Functions { BUYTAX, SELLTAX, SETMARK, REF, AUTOLIQPER, MINLIQ, MAXHOLD, MAXTX, AUTOTAXENABLED, LIQENABLED, EXC, INC, MARKFALL, SETROUT}    
    uint256 private constant _TIMELOCK = 2 days;
    mapping(Functions => uint256) public timelock;

    modifier notLocked(Functions _fn) {
        require(!_timelock_enabled || timelock[_fn] != 0 && timelock[_fn] <= block.timestamp, "Function is timelocked");
        _;
    }

    modifier lockLiquidities{
        _inLockLiquidities = true;
        _;
        _inLockLiquidities = false;
    }

    constructor () public {

        IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);

        //create a uniswap pair for this new token
        uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
            .createPair(address(this), _uniswapV2Router.WETH());

        //set the rest of the contract variables
        uniswapV2Router = _uniswapV2Router;

        _rOwned[_msgSender()] = _rTotal;
        emit Transfer(address(0), _msgSender(), _tTotal);
    }

    //to recieve BNB from uniswapV2Router when swapping
    receive() external payable {}

    function name() public pure returns(string memory) {

        return _name;
    }

    function symbol() public pure returns (string memory) {

        return _symbol;
    }

    function decimals() public pure returns (uint8) {

        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {

        return _tTotal;
    }

    function balanceOf(address account) public view override returns (uint256) {

        if (_isExcluded[account]) return _tOwned[account];
        return tokenFromReflection(_rOwned[account]);
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {

        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view override returns (uint256) {

        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public override returns (bool) {

        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {

        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {

        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {

        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    function isExcluded(address account) public view returns (bool) {

        return _isExcluded[account];
    }

    function totalFees() public view returns (uint256) {

        return _tFeeTotal;
    }
    
    function removeTax() private {
        
        if(_totalBuyTax == 0) return;
        
        _previousRefBuyTax = _totalBuyTax;
        
        _totalBuyTax = 0;

        if(_totalSellTax == 0) return;
        
        _previousRefSellTax = _totalSellTax;
        
        _totalSellTax = 0;
    }
    
    function restoreTax() private {
        
        _totalBuyTax = _previousRefBuyTax;
        _totalSellTax = _previousRefSellTax;
    }

    function reflect(uint256 tAmount) public {

        address sender = _msgSender();
        require(!_isExcluded[sender], "Excluded addresses cannot call this function");
        (uint256 rAmount,,,,) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rTotal = _rTotal.sub(rAmount);
        _tFeeTotal = _tFeeTotal.add(tAmount);
    }

    function reflectionFromToken(uint256 tAmount, bool deductTransferFee) public view returns(uint256) {

        require(tAmount <= _tTotal, "Amount must be less than supply");
        if (!deductTransferFee) {
            (uint256 rAmount,,,,) = _getValues(tAmount);
            return rAmount;
        } else {
            (,uint256 rTransferAmount,,,) = _getValues(tAmount);
            return rTransferAmount;
        }
    }

    function tokenFromReflection(uint256 rAmount) public view returns(uint256) {

        require(rAmount <= _rTotal, "Amount must be less than total reflections");
        uint256 currentRate =  _getRate();
        return rAmount.div(currentRate);
    }

    function excludeAccount(address account) external onlyOwner() notLocked(Functions.EXC) {

        require(!_isExcluded[account], "Account is already excluded");
        if(_rOwned[account] > 0) {
            _tOwned[account] = tokenFromReflection(_rOwned[account]);
        }
        _isExcluded[account] = true;
        _excluded.push(account);

        timelock[Functions.EXC] = 0;
    }

    function includeAccount(address account) external onlyOwner() notLocked(Functions.INC) {
        
        require(_isExcluded[account], "Account is already included");
        for (uint256 i = 0; i < _excluded.length; i++) {
            if (_excluded[i] == account) {
                _excluded[i] = _excluded[_excluded.length - 1];
				uint256 currentRate = _getRate();
				_rOwned[account] = _tOwned[account].mul(currentRate);
                _tOwned[account] = 0;
                _isExcluded[account] = false;
                _excluded.pop();
                break;
            }
        }

        timelock[Functions.INC] = 0;
    }

    function _approve(address owner, address spender, uint256 amount) private {

        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);
    }

    function _isBuy(address _sender) private view returns (bool) {
        return _sender == uniswapV2Pair;
    }

    function _transfer(address sender, address recipient, uint256 amount) private {

        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(amount > 0, "Transfer amount must be greater than zero");

        if(!_isExcluded[sender]) {
            require(amount <= _maxTransAmount, "Transfer amount exceeds the maxTransAmount.");
        }

        //check if whitelist is enabled if so only allow whitelisted users to transfer
        if(_whitelist_enabled) {
            require(_isWhitelisted(sender), "You are not whitelisted, try again in 10 mins");
        }
        
        if(_AutoTaxEnabled && !_inLockLiquidities && sender != uniswapV2Pair) {
            _doTokenomics();
        }
        
        if(_isExcluded[sender] || _isExcluded[recipient]) {
            removeTax();

        } else {

            if (_isBuy(sender)) {
                uint256 recipient_balance = balanceOf(address(recipient));
                uint256 recipient_new_balance = recipient_balance.add(amount);
                require(recipient_new_balance <= _maxHoldAmount, "Transfer amount exceeds the maxHoldAmount.");
            }
        }
            
        if (_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferFromExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && _isExcluded[recipient]) {
            _transferToExcluded(sender, recipient, amount);
        } else if (_isExcluded[sender] && _isExcluded[recipient]) {
            _transferBothExcluded(sender, recipient, amount);
        } else {
            _transferStandard(sender, recipient, amount);
        }
        
        if(_isExcluded[sender] || _isExcluded[recipient]) {
            restoreTax();
        }
    }

    function _transferStandard(address sender, address recipient, uint256 tAmount) private {

        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferToExcluded(address sender, address recipient, uint256 tAmount) private {

        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tTransferAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferFromExcluded(address sender, address recipient, uint256 tAmount) private {

        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferBothExcluded(address sender, address recipient, uint256 tAmount) private {

        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tTransferAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _reflectFee(uint256 rFee, uint256 tFee) private {
        
        uint256 newTFee = tFee.mul(_refPer).div(100);
        uint256 newRFee = rFee.mul(_refPer).div(100);

        uint256 tContractFee = tFee.sub(newTFee);
        uint256 rContractFee = rFee.sub(newRFee);
        
        _tOwned[address(this)] = _tOwned[address(this)].add(tContractFee);
        _rOwned[address(this)] = _rOwned[address(this)].add(rContractFee);
        
        _rTotal = _rTotal.sub(newRFee);
        _tFeeTotal = _tFeeTotal.add(newTFee);
    }

    function _getValues(uint256 tAmount) private view returns (uint256, uint256, uint256, uint256, uint256) {

        (uint256 tTransferAmount, uint256 tFee) = _getTValues(tAmount);
        uint256 currentRate =  _getRate();
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee) = _getRValues(tAmount, tFee, currentRate);
        return (rAmount, rTransferAmount, rFee, tTransferAmount, tFee);
    }

    function _getTValues(uint256 tAmount) private view returns (uint256, uint256) {

        uint256 tFee = tAmount.mul(_totalBuyTax).div(100); //buy - 10% tax is taken or whatever _totalTax is

        if (!_isBuy(msg.sender)) {
            tFee = tAmount.mul(_totalSellTax).div(100); //sell - 12% tax is taken or whatever _totalTax is
        }

        uint256 tTransferAmount = tAmount.sub(tFee);
        return (tTransferAmount, tFee);
    }

    function _getRValues(uint256 tAmount, uint256 tFee, uint256 currentRate) private pure returns (uint256, uint256, uint256) {

        uint256 rAmount = tAmount.mul(currentRate);
        uint256 rFee = tFee.mul(currentRate);
        uint256 rTransferAmount = rAmount.sub(rFee);
        return (rAmount, rTransferAmount, rFee);
    }

    function _getRate() private view returns(uint256) {

        (uint256 rSupply, uint256 tSupply) = _getCurrentSupply();
        return rSupply.div(tSupply);
    }

    function _getCurrentSupply() private view returns(uint256, uint256) {

        uint256 rSupply = _rTotal;
        uint256 tSupply = _tTotal;

        for (uint256 i = 0; i < _excluded.length; i++) {
            if (_rOwned[_excluded[i]] > rSupply || _tOwned[_excluded[i]] > tSupply) return (_rTotal, _tTotal);
            rSupply = rSupply.sub(_rOwned[_excluded[i]]);
            tSupply = tSupply.sub(_tOwned[_excluded[i]]);
        }

        if (rSupply < _rTotal.div(_tTotal)) return (_rTotal, _tTotal);
        return (rSupply, tSupply);
    }

    function _setMarketingWallet(address payable wallet) public onlyOwner() notLocked(Functions.SETMARK) {

        _wMarketing = wallet;
        timelock[Functions.SETMARK] = 0;
        emit setMarketingWallet(wallet);
    }

    function _setMaxHold(uint256 maxHoldAmount) external onlyOwner() notLocked(Functions.MAXHOLD) {

        require(maxHoldAmount >= 2000000000000000000000000000000, "Max hold amount is below threshold");

        _maxHoldAmount = maxHoldAmount;
        timelock[Functions.MAXHOLD] = 0;
        emit setMaxHold(maxHoldAmount);
    }

    function _setMaxTrans(uint256 maxTransAmount) external onlyOwner() notLocked(Functions.MAXTX) {

        require(maxTransAmount >= 1200000000000000000000000000000, "Max TX amount is below threshold");

        _maxTransAmount = maxTransAmount;
        timelock[Functions.MAXTX] = 0;
        emit setMaxTrans(maxTransAmount);
    }

    function _setTotalBuyTax(uint256 totalBuyTax) external onlyOwner() notLocked(Functions.BUYTAX) {

        require(totalBuyTax <= _maxBuyTax, "Buy tax too high");

        _currentBuyTax = totalBuyTax;
        _totalBuyTax = totalBuyTax;
        timelock[Functions.BUYTAX] = 0;
        emit setTotalBuyTax(totalBuyTax);
    }

    function _setTotalSellTax(uint256 totalSellTax) external onlyOwner() notLocked(Functions.SELLTAX) {

        require(totalSellTax <= _maxSellTax, "Sell tax too high");

        _currentSellTax = totalSellTax;
        _totalSellTax = totalSellTax;
        timelock[Functions.SELLTAX] = 0;
        emit setTotalSellTax(totalSellTax);
    }

    function _setRefPer(uint256 refPer) external onlyOwner() notLocked(Functions.REF) {

        _refPer = refPer;
        timelock[Functions.REF] = 0;
        emit setRefPer(refPer);
    }

    function _setAutoLiqPer(uint256 autoLiqPer) external onlyOwner() notLocked(Functions.AUTOLIQPER) {

        _autoLiqPer = autoLiqPer;
        timelock[Functions.AUTOLIQPER] = 0;
        emit setAutoLiqPer(autoLiqPer);
    }

    function _setMinTokensForLiquidity(uint256 minTokensForLiquidity) external onlyOwner() notLocked(Functions.MINLIQ) {

        _minTokensForLiquidity = minTokensForLiquidity;
        timelock[Functions.MINLIQ] = 0;
        emit setMinTokensForLiquidity(minTokensForLiquidity);
    }

    function _setLockLiquiditiesEnabled(bool lockLiquiditiesEnabled) external onlyOwner() notLocked(Functions.LIQENABLED) {

        _lockLiquiditiesEnabled = lockLiquiditiesEnabled;
        timelock[Functions.LIQENABLED] = 0;
        emit setLockLiquiditiesEnabled(lockLiquiditiesEnabled);
    }
    
    function _setAutoTaxEnabled(bool AutoTaxEnabled) external onlyOwner() notLocked(Functions.AUTOTAXENABLED) {

        _AutoTaxEnabled = AutoTaxEnabled;
        timelock[Functions.AUTOTAXENABLED] = 0;
        emit setAutoTaxEnabled(AutoTaxEnabled);
    }

    function _doTokenomics() public lockLiquidities {
        
        uint256 amount = balanceOf(address(this));
        
        if(amount >= _minTokensForLiquidity && _lockLiquiditiesEnabled == true && amount > 0) {

            if(amount >= _maxTransAmount) {
                amount = _maxTransAmount;
            }

            uint256 liqAmount = _findPercent(amount, _autoLiqPer);
            uint256 marketingAmount = amount.sub(liqAmount);

            _doLiquidity(liqAmount, marketingAmount);
            _doMarketing();
        }
    }

    function _doLiquidity(uint256 liqTokensAmount, uint256 marketingTokensAmount) private {

        _liqAllTime += liqTokensAmount;

        uint256 bnbHalf = liqTokensAmount.div(2);
        uint256 tokenHalf = liqTokensAmount.sub(bnbHalf);

        uint256 bnbBalance = address(this).balance; //current bnb balance

        _swapTokensForEth(bnbHalf + marketingTokensAmount, address(this)); //swap liquidity and marketing to ETH in one TX to save gas

        uint256 bnbNewBalance = address(this).balance.sub(bnbBalance); //get amount swapped to bnb

        _addLiquidity(tokenHalf, bnbNewBalance); //add liquidity using the tokens and bnb, ETH dust will be sent back for marketing wallet
    }

    function _doMarketing() private {

        uint256 bnbBalance = address(this).balance; //current bnb balance

        Address.sendValue(_wMarketing, bnbBalance); //send bnb amount to governance

        _marketingAllTime += bnbBalance;
    }

    function _findPercent(uint256 value, uint256 basePercent) private pure returns (uint256)  {

        uint256 percent = value.mul(basePercent).div(10000);
        return percent;
    }
    
    function _swapTokensForEth(uint256 tokenAmount, address tokenContract) private {

        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = tokenContract;
        path[1] = uniswapV2Router.WETH();

        _approve(tokenContract, address(uniswapV2Router), tokenAmount);

        // make the swap
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0, // accept any amount of ETH
            path,
            tokenContract,
            block.timestamp
        );
    }

    function _addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {

        // approve token transfer to cover all possible scenarios
        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // add the liquidity
        uniswapV2Router.addLiquidityETH{value: ethAmount}(
            address(this),
            tokenAmount,
            0, // slippage is unavoidable
            0, // slippage is unavoidable
            owner(),
            block.timestamp
        );
    }

    function _setRouterAddress(address newRouter) public onlyOwner() notLocked(Functions.SETROUT) {

        IUniswapV2Router02 _newPancakeRouter = IUniswapV2Router02(newRouter);
        uniswapV2Pair = IUniswapV2Factory(_newPancakeRouter.factory()).createPair(address(this), _newPancakeRouter.WETH());
        uniswapV2Router = _newPancakeRouter;

        timelock[Functions.SETROUT] = 0;
        emit setRouterAddressChanged(newRouter);
    }

    function _setWhitelistedAddresses(address[] memory whitelist) public onlyOwner {

        for (uint i = 0; i < whitelist.length; i++) {
            _whitelistedUsers[whitelist[i]] = true;
        }

        emit setWhitelistedUsers(whitelist.length);
    }

    function _renounceWhiteList() external onlyOwner {

        require(_whitelist_enabled == true, "Whitelist already renounced");

        _whitelist_enabled = false;
    }

    function _isWhitelisted(address user) public view returns (bool) {

        return _whitelistedUsers[user];
    }

    //unlock timelock
    function _unlockFunction(Functions _fn) public onlyOwner {
        timelock[_fn] = block.timestamp + _TIMELOCK;
    }

    //lock timelock
    function _lockFunction(Functions _fn) public onlyOwner {
        timelock[_fn] = 0;
    }

    event setRouterAddressChanged(address newRouter);
    event setAutoTaxEnabled(bool AutoTaxEnabled);
    event setLockLiquiditiesEnabled(bool lockLiquiditiesEnabled);
    event setMinTokensForLiquidity(uint256 minTokensForLiquidity);
    event setAutoLiqPer(uint256 autoLiqPer);
    event setRefPer(uint256 refPer);
    event setTotalSellTax(uint256 totalSellTax);
    event setTotalBuyTax(uint256 totalBuyTax);
    event setMaxTrans(uint256 maxTransAmount);
    event setMaxHold(uint256 maxHoldAmount);
    event setMarketingWallet(address wallet);
    event setWhitelistedUsers(uint256 amount);
}
//to recieve BNB from uniswapV2Router when swapping