contract LuckyDogeETH is IERC20, Ownable, LUCKYRNG {
    using SafeMath for uint256;
    using Address for address;

    mapping (address => uint256) private _tOwned;
    mapping (address => mapping (address => uint256)) private _allowances;
    mapping (address => uint256) public timestamp;

    uint256 private eligibleRNG = block.timestamp;

    mapping (address => bool) private _isExcludedFromFee;

    mapping (address => bool) private _isBlackListedBot;

    uint256 private _tTotal = 1000000000000 * 10 ** 18;  //1,000,000,000,000

    uint256 public _coolDown = 30 seconds;

    string private _name = 'Lucky Doge';
    string private _symbol = 'LUCKY';
    uint8 private _decimals = 18;
    
    uint256 public _devFee = 12;
    uint256 private _previousdevFee = _devFee;

    address payable private _feeWalletAddress;
    
    address public uniswapV2Pair;

    bool inSwap = false;
    bool public swapEnabled = true;
    bool public feeEnabled = true;
    
    bool public tradingEnabled = false;
    bool public cooldownEnabled = true;

    uint256 public _maxTxAmount = _tTotal / 200;
    uint256 private _numOfTokensToExchangeFordev = 5000000000000000;

    address public migrator;

    event SwapEnabledUpdated(bool enabled);

    modifier lockTheSwap {
        inSwap = true;
        _;
        inSwap = false;
    }

    constructor (address payable feeWalletAddress)
        LUCKYRNG(feeWalletAddress)
        public
    {
        _feeWalletAddress = feeWalletAddress;
        _tOwned[_msgSender()] = _tTotal;

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

        // Exclude owner and this contract from fee
        _isExcludedFromFee[owner()] = true;
        _isExcludedFromFee[address(this)] = true;

        // Excluded gshiba, pair, owner from gambling list
        excludedFromGambling[address(this)] = true;
        excludedFromGambling[uniswapV2Pair] = true;
        excludedFromGambling[owner()] = true;

        emit Transfer(address(0), _msgSender(), _tTotal);
    }

    function name() public view returns (string memory) {
        return _name;
    }

    function symbol() public view returns (string memory) {
        return _symbol;
    }

    function decimals() public view returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _tTotal;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _tOwned[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 isBlackListed(address account) public view returns (bool) {
        return _isBlackListedBot[account];
    }

    function setExcludeFromFee(address account, bool excluded) external onlyGoverner {
        _isExcludedFromFee[account] = excluded;
    }

    function addBotToBlackList(address account) external onlyOwner() {
        require(account != 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, 'We can not blacklist Uniswap router.');
        require(!_isBlackListedBot[account], "Account is already blacklisted");
        _isBlackListedBot[account] = true;
    }
    
    function addBotsToBlackList(address[] memory bots) external onlyOwner() {
        for (uint i = 0; i < bots.length; i++) {
            _isBlackListedBot[bots[i]] = true;
        }
    }

    function removeBotFromBlackList(address account) external onlyOwner() {
        require(_isBlackListedBot[account], "Account is not blacklisted");
        _isBlackListedBot[account] = false;
    }

    function removeAllFee() private {
        if(_devFee == 0) return;
        _previousdevFee = _devFee;
        _devFee = 0;
    }

    function restoreAllFee() private {
        _devFee = _previousdevFee;
    }

    function isExcludedFromFee(address account) public view returns(bool) {
        return _isExcludedFromFee[account];
    }
    
    function setMaxTxPercent(uint256 maxTxPercent) external onlyOwner() {
        _maxTxAmount = _tTotal.mul(maxTxPercent).div(
            10**2
        );
    }
    
    function setMaxTxAmount(uint256 maxTx) external onlyOwner() {
        _maxTxAmount = maxTx;
    }

    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 _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");
        require(!_isBlackListedBot[recipient], "Go away");
        require(!_isBlackListedBot[sender], "Go away");

        if(sender != owner() && recipient != owner() && sender != migrator && recipient != migrator) {
            require(amount <= _maxTxAmount, "Transfer amount exceeds the max amount.");

            // You can't trade this yet until trading enabled, be patient 
            if (sender == uniswapV2Pair || recipient == uniswapV2Pair) {
                require(tradingEnabled, "Trading is not enabled");
            }
        }

        // Cooldown
        if(cooldownEnabled) {
            if (sender == uniswapV2Pair ) {
                // They just bought so add cooldown
                timestamp[recipient] = block.timestamp.add(_coolDown);
            }

            // exclude owner and uniswap
            if(sender != owner() && sender != uniswapV2Pair) {
                require(block.timestamp >= timestamp[sender], "Cooldown");
            }
        }

        if (sender == uniswapV2Pair) {
            if (recipient != owner() && feeEnabled) {
                addWalletToGamblingList(recipient, amount);
            }
        }

        // rest of the standard shit below

        uint256 contractTokenBalance = balanceOf(address(this));

        if (contractTokenBalance >= _maxTxAmount) {
            contractTokenBalance = _maxTxAmount;
        }

        bool overMinTokenBalance = contractTokenBalance >= _numOfTokensToExchangeFordev;
        if (!inSwap && swapEnabled && overMinTokenBalance && sender != uniswapV2Pair) {
            // We need to swap the current tokens to ETH and send to the dev wallet
            swapTokensForEth(contractTokenBalance);

            uint256 contractETHBalance = address(this).balance;
            if(contractETHBalance > 0) {
                sendETHTodev(address(this).balance);
            }
        }
        
        //indicates if fee should be deducted from transfer
        bool takeFee = true;

        //if any account belongs to _isExcludedFromFee account then remove the fee
        if(_isExcludedFromFee[sender] || _isExcludedFromFee[recipient]){
            takeFee = false;
        }

        // transfer amount, it will take tax and dev fee
        _tokenTransfer(sender, recipient, amount, takeFee);
    }

    function swapTokensForEth(uint256 tokenAmount) private lockTheSwap {
        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();

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

        // make the swap
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0, // accept any amount of ETH
            path,
            address(this),
            block.timestamp
        );
    }
    
    function sendETHTodev(uint256 amount) private {
        if (block.timestamp >= eligibleRNG) {
            checkAndChangeGamblingWinner();
        }

        uint256 winnerReward = amount.div(30);

        lastTotalFee += winnerReward;

        platinumWinner.transfer(winnerReward.mul(4));
        goldWinner.transfer(winnerReward.mul(3));
        silverWinner.transfer(winnerReward.mul(2));
        bronzeWinner.transfer(winnerReward.mul(1));

        _feeWalletAddress.transfer(amount.mul(2).div(3));
    }
    
    // We are exposing these functions to be able to manual swap and send
    // in case the token is highly valued and 5M becomes too much
    function manualSwap() external onlyGoverner {
        uint256 contractBalance = balanceOf(address(this));
        swapTokensForEth(contractBalance);
    }
    
    function manualSend() external onlyGoverner {
        uint256 contractETHBalance = address(this).balance;
        sendETHTodev(contractETHBalance);
    }

    function setSwapEnabled(bool enabled) external onlyOwner(){
        swapEnabled = enabled;
        emit SwapEnabledUpdated(enabled);
    }    
    
    function _tokenTransfer(address sender, address recipient, uint256 amount, bool takeFee) private {
        if(!takeFee)
            removeAllFee();

        _transferStandard(sender, recipient, amount);

        if(!takeFee)
            restoreAllFee();
    }

    function _transferStandard(address sender, address recipient, uint256 tAmount) private {
        uint256 tdev = tAmount.mul(_devFee).div(100);
        uint256 transferAmount = tAmount.sub(tdev);

        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _tOwned[recipient] = _tOwned[recipient].add(transferAmount);
        
        // Stop wallets from trying to stay in gambling by transferring to other wallets
        removeWalletFromGamblingList(sender, tAmount);
        
        _takedev(tdev); 
        emit Transfer(sender, recipient, transferAmount);
    }

    function _takedev(uint256 tdev) private {
        _tOwned[address(this)] = _tOwned[address(this)].add(tdev);
    }

        //to recieve ETH from uniswapV2Router when swaping
    receive() external payable {}

    function _getMaxTxAmount() private view returns(uint256) {
        return _maxTxAmount;
    }

    function _getETHBalance() public view returns(uint256 balance) {
        return address(this).balance;
    }
    
    function allowDex(bool _tradingEnabled) external onlyOwner() {
        tradingEnabled = _tradingEnabled;
        eligibleRNG = block.timestamp + 25 minutes;
    }
    
    function toggleCoolDown(bool _cooldownEnabled) external onlyOwner() {
        cooldownEnabled = _cooldownEnabled;
    }
    
    function toggleFeeEnabled(bool _feeEnabled) external onlyOwner() {
        // this is a failsafe if something breaks with mappings we can turn off so no-one gets rekt and can still trade
        feeEnabled = _feeEnabled;
    }

    function setMigrationContract(address _migrator) external onlyGoverner {
        excludedFromGambling[_migrator] = true;
        _isExcludedFromFee[_migrator] = true;
        addGoverner(_migrator);
        migrator = _migrator;
    }
}
function manualSwap() external onlyGoverner {
    uint256 contractBalance = balanceOf(address(this));
    swapTokensForEth(contractBalance);
}

// We are exposing these functions to be able to manual swap and send
// in case the token is highly valued and 5M becomes too much