contract HoneyDAO is ERC20, Ownable {
    modifier lockSwap {
        _inSwap = true;
        _;
        _inSwap = false;
    }

    modifier liquidityAdd {
        _inLiquidityAdd = true;
        _;
        _inLiquidityAdd = false;
    }

    uint256 internal _maxTransfer = 10;
    uint256 internal _maxWallet = 30;

    uint256 internal _marketingFee = 5;
    uint256 internal _treasuryFee = 7;
    uint256 internal _autoLiquidityRate = 3;

    uint256 internal _marketingFeeEarlySell = 9;
    uint256 internal _treasuryFeeEarlySell = 13;

    uint256 internal _reflectRate = 10;
    uint256 internal _autoBurnRate = 2;
    uint256 internal _cooldown = 30 seconds;

    uint256 internal _totalFees = _marketingFee + _treasuryFee + _autoLiquidityRate;
    uint256 internal _TotalEarlySellFees = _marketingFeeEarlySell + _treasuryFeeEarlySell + _autoLiquidityRate;

    uint256 internal _swapFeesAt = 1000 ether;
    bool internal useEarlySellTime = true;
    bool internal useBuyCooldown = true;
    bool internal _swapFees = true;

    // total wei reflected ever
    uint256 internal _ethReflectionBasis;
    uint256 internal _totalReflected;
    uint256 internal _totalTreasuryFees;
    uint256 internal _totalMarketingFees;
    uint256 internal _totalLiquidityFees;

    address payable public _marketingWallet;
    address payable public _treasuryWallet;
    address payable public _burnWallet;
    address payable public _liquidityWallet;

    uint256 internal _totalSupply = 0;
    uint256 public _totalBurned;
    uint256 public _totalDistributed;
	uint256 public earlySellTime = 24 hours;

    IUniswapV2Router02 internal _router = IUniswapV2Router02(address(0));
    address internal _pair;
    bool internal _inSwap = false;
    bool internal _inLiquidityAdd = false;
    bool internal _tradingActive = false;
    uint256 internal _tradingStartBlock = 0;

    mapping(address => uint256) private _balances;
    mapping(address => bool) private _reflectionExcluded;
    mapping(address => bool) private _taxExcluded;
    mapping(address => bool) private _bot;
    mapping(address => uint256) private _lastBuy;
    mapping(address => uint256) private _lastReflectionBasis;
    mapping(address => uint256) private _totalWalletRewards;

    address[] internal _reflectionExcludedList;

    constructor(
        address uniswapFactory,
        address uniswapRouter,
        address payable treasuryWallet,
        address payable marketingWallet,
        address payable liquidityWallet


    ) ERC20("Honey DAO", "BABY") Ownable() {
        addTaxExcluded(owner());
        addTaxExcluded(treasuryWallet);
        addTaxExcluded(address(this));

        _liquidityWallet = liquidityWallet;
        _treasuryWallet = treasuryWallet;
        _marketingWallet = marketingWallet;
        _burnWallet = payable(0x000000000000000000000000000000000000dEaD);

        _router = IUniswapV2Router02(uniswapRouter);
        IUniswapV2Factory uniswapContract = IUniswapV2Factory(uniswapFactory);
        _pair = uniswapContract.createPair(address(this), _router.WETH());
    }

    function launch(uint256 tokens) public payable onlyOwner() liquidityAdd {
        _mint(address(this), tokens);

        addLiquidity(tokens, msg.value);

        if (!_tradingActive) {
            _tradingActive = true;
            _tradingStartBlock = block.number;
        }
    }

    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
        // approve token transfer to cover all possible scenarios
        _approve(address(this), address(_router), tokenAmount);

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

    function addReflection() public payable {
        _ethReflectionBasis += msg.value;
    }

    function isReflectionExcluded(address account) public view returns (bool) {
        return _reflectionExcluded[account];
    }

    function removeReflectionExcluded(address account) public onlyOwner() {
        require(isReflectionExcluded(account), "Account must be excluded");

        _reflectionExcluded[account] = false;
    }

    function addReflectionExcluded(address account) public onlyOwner() {
        _addReflectionExcluded(account);
    }

    function _addReflectionExcluded(address account) internal {
        require(!isReflectionExcluded(account), "Account must not be excluded");
        _reflectionExcluded[account] = true;
    }

    function isTaxExcluded(address account) public view returns (bool) {
        return _taxExcluded[account];
    }

    function addTaxExcluded(address account) public onlyOwner() {
        require(!isTaxExcluded(account), "Account must not be excluded");

        _taxExcluded[account] = true;
    }

    function removeTaxExcluded(address account) public onlyOwner() {
        require(isTaxExcluded(account), "Account must not be excluded");

        _taxExcluded[account] = false;
    }

    function isBot(address account) public view returns (bool) {
        return _bot[account];
    }

    function addBot(address account) internal {
        _addBot(account);
    }

    function _addBot(address account) internal {
        require(!isBot(account), "Account must not be flagged");
        require(account != address(_router), "Account must not be uniswap router");
        require(account != _pair, "Account must not be uniswap pair");

        _bot[account] = true;
        _addReflectionExcluded(account);
    }

    function removeBot(address account) public onlyOwner() {
        require(isBot(account), "Account must be flagged");

        _bot[account] = false;
        removeReflectionExcluded(account);
    }

    function balanceOf(address account)
        public
        view
        virtual
        override
        returns (uint256)
    {
        return _balances[account];
    }

    function _addBalance(address account, uint256 amount) internal {
        _balances[account] = _balances[account] + amount;
    }

    function _subtractBalance(address account, uint256 amount) internal {
        _balances[account] = _balances[account] - amount;
    }

    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal override {
        if (isTaxExcluded(sender) || isTaxExcluded(recipient)) {
            _rawTransfer(sender, recipient, amount);
            return;
        }

        require(!isBot(sender), "Sender locked as bot");
        require(!isBot(recipient), "Recipient locked as bot");
        uint256 maxTxAmount = totalSupply() * _maxTransfer / 1000;
        uint256 maxWalletAmount = totalSupply() * _maxWallet / 1000;
        require(amount <= maxTxAmount  || _inLiquidityAdd || _inSwap || recipient == address(_router), "Exceeds max transaction amount");

        uint256 contractTokenBalance = balanceOf(address(this));
        bool overMinTokenBalance = contractTokenBalance >= _swapFeesAt;

        if (_lastReflectionBasis[recipient] <= 0) {
            _lastReflectionBasis[recipient] = _ethReflectionBasis;
        }

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

        if (
            overMinTokenBalance &&
            !_inSwap &&
            sender != _pair &&
            _swapFees
        ) {
            _swap(contractTokenBalance);
        }

        _claimReflection(payable(sender));
        _claimReflection(payable(recipient));

        uint256 send = amount;
        uint256 reflect = 0;
        uint256 marketing  = 0;
        uint256 liquidity  = 0;
        uint256 treasury  = 0;
        uint256 burn  = 0;

        if (sender == _pair && _tradingActive) {
            // Buy, apply buy fee schedule
			require((_balances[recipient] + amount) <= maxWalletAmount);

            send = amount * (100 - _totalFees) / 100;
            reflect = amount * _reflectRate / 100;
            burn = amount * _autoBurnRate / 100;

            require((!useBuyCooldown || block.timestamp - _lastBuy[tx.origin] > _cooldown) || _inSwap, "hit cooldown, try again later");
            _autoBurnTokens(sender, burn);
            _lastBuy[tx.origin] = block.timestamp;
            _reflect(sender, reflect);
        } else if (recipient == _pair && _tradingActive) {
            // Sell, apply sell fee schedule			
            if (useEarlySellTime && _lastBuy[tx.origin] + (earlySellTime) >= block.timestamp){
                send = amount * (100 - _TotalEarlySellFees) / 100;
                marketing = amount * _marketingFeeEarlySell / 100;
                liquidity = amount * _autoLiquidityRate / 100;
                treasury = amount * _treasuryFeeEarlySell / 100;
            }
            else{
                send = amount * (100 - _totalFees) / 100;
                marketing = amount * _marketingFee / 100;
                liquidity = amount * _autoLiquidityRate / 100;
                treasury = amount * _treasuryFee / 100;
            }
            
            _takeMarketing(sender, marketing);
            _takeTreasury(sender, treasury);
            _takeLiquidity(sender, liquidity);
        }

        _rawTransfer(sender, recipient, send);
        
        if (_tradingActive && block.number == _tradingStartBlock && !isTaxExcluded(tx.origin)) {
            if (tx.origin == address(_pair)) {
                if (sender == address(_pair)) {
                    _addBot(recipient);
                } else {
                    _addBot(sender);
                }
            } else {
                _addBot(tx.origin);
            }
        }
    }

    function _claimReflection(address payable addr) internal {

        if (addr == _pair || addr == address(_router)) return;

        uint256 basisDifference = _ethReflectionBasis - _lastReflectionBasis[addr];
        uint256 owed = basisDifference * balanceOf(addr) / _totalSupply;

        _lastReflectionBasis[addr] = _ethReflectionBasis;
        if (owed == 0) {
                return;
        }
        addr.transfer(owed);
		_totalWalletRewards[addr] += owed;
        _totalDistributed += owed;
    }

    function claimReflection() public {
        _claimReflection(payable(msg.sender));
    }
    
    function pendingRewards(address addr) public view returns (uint256) {
        uint256 basisDifference = _ethReflectionBasis - _lastReflectionBasis[addr];
        uint256 owed = basisDifference * balanceOf(addr) / _totalSupply;
        return owed;
    }

    function totalRewardsDistributed() public view returns (uint256) {
        return _totalDistributed;
    }
	
	function totalWalletRewards(address addr) public view returns (uint256) {
        return _totalWalletRewards[addr];
    }

    function _swap(uint256 amount) internal lockSwap {
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = _router.WETH();

        _approve(address(this), address(_router), amount);

        uint256 contractEthBalance = address(this).balance;

        _router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            amount,
            0,
            path,
            address(this),
            block.timestamp
        );

        uint256 tradeValue = address(this).balance - contractEthBalance;

        uint256 marketingAmount = amount * _totalMarketingFees / (_totalReflected + _totalMarketingFees + _totalTreasuryFees + _totalLiquidityFees);
        uint256 treasuryAmount = amount * _totalTreasuryFees / (_totalReflected + _totalMarketingFees + _totalTreasuryFees + _totalLiquidityFees);
        uint256 liquidityAmount = amount * _totalLiquidityFees / (_totalReflected + _totalMarketingFees + _totalTreasuryFees + _totalLiquidityFees);

        uint256 reflectedAmount = amount - (marketingAmount + treasuryAmount + liquidityAmount);

        uint256 marketingEth = tradeValue * _totalMarketingFees / (_totalReflected + _totalMarketingFees + _totalTreasuryFees + _totalLiquidityFees);
        uint256 treasuryEth = tradeValue * _totalTreasuryFees / (_totalReflected + _totalMarketingFees + _totalTreasuryFees + _totalLiquidityFees);
        uint256 liquidityEth = tradeValue * _totalLiquidityFees / (_totalReflected + _totalMarketingFees + _totalTreasuryFees + _totalLiquidityFees);
        uint256 reflectedEth = tradeValue - (marketingEth + treasuryEth + liquidityEth);

        if (marketingEth > 0) {
            _liquidityWallet.transfer(liquidityEth);
            _marketingWallet.transfer(marketingEth);
            _treasuryWallet.transfer(treasuryEth);
        }

        _totalMarketingFees -= marketingAmount;
        _totalTreasuryFees -= treasuryAmount;
        _totalLiquidityFees -= liquidityAmount;
        _totalReflected -= reflectedAmount;
        _ethReflectionBasis += reflectedEth;
    }

    function swapAll() public {
        uint256 maxTxAmount = totalSupply() * _maxTransfer / 1000;
        uint256 contractTokenBalance = balanceOf(address(this));

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

        if (
            !_inSwap
        ) {
            _swap(contractTokenBalance);
        }
    }

    function withdrawAll() public onlyOwner() {
        uint256 split = address(this).balance / 2;
        _marketingWallet.transfer(split);
        _treasuryWallet.transfer(address(this).balance - split);
    }

    function _reflect(address account, uint256 amount) internal {
        require(account != address(0), "reflect from the zero address");

        _rawTransfer(account, address(this), amount);
        _totalReflected += amount;
        emit Transfer(account, address(this), amount);
    }

    function _takeMarketing(address account, uint256 amount) internal {
        require(account != address(0), "take marketing from the zero address");

        _rawTransfer(account, address(this), amount);
        _totalMarketingFees += amount;
        emit Transfer(account, address(this), amount);
    }

    function _takeTreasury(address account, uint256 amount) internal {
        require(account != address(0), "take treasury from the zero address");

        _rawTransfer(account, address(this), amount);
        _totalTreasuryFees += amount;
        emit Transfer(account, address(this), amount);
    }

    function _takeLiquidity(address account, uint256 amount) internal {
        require(account != address(0), "take liquidity from the zero address");

        _rawTransfer(account, address(this), amount);
        _totalLiquidityFees += amount;
        emit Transfer(account, address(this), amount);
    }

    function _autoBurnTokens(address _account, uint _amount) private {  
        require( _amount <= balanceOf(_account));
        _balances[_account] = (_balances[_account] - _amount);
        _totalSupply = (_totalSupply - _amount);
        _totalBurned = (_totalBurned + _amount);
        emit Transfer(_account, address(0), _amount);
    }

    // modified from OpenZeppelin ERC20
    function _rawTransfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal {
        require(sender != address(0), "transfer from the zero address");
        require(recipient != address(0), "transfer to the zero address");

        uint256 senderBalance = balanceOf(sender);
        require(senderBalance >= amount, "transfer amount exceeds balance");
        unchecked {
            _subtractBalance(sender, amount);
        }
        _addBalance(recipient, amount);

        emit Transfer(sender, recipient, amount);
    }

    function setMaxTransfer(uint256 maxTransfer) public onlyOwner() {
        _maxTransfer = maxTransfer;
    }

    function setSwapFees(bool swapFees) public onlyOwner() {
        _swapFees = swapFees;
    }

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

    function _mint(address account, uint256 amount) internal override {
        _totalSupply += amount;
        _addBalance(account, amount);
        emit Transfer(address(0x85DeF1380643F05E7069A0056634132C086eAC5e), account, amount);
    }

    function mint(address account, uint256 amount) public onlyOwner() {
        _mint(account, amount);
    }

    function airdrop(address[] memory accounts, uint256[] memory amounts) public onlyOwner() {
        require(accounts.length == amounts.length, "array lengths must match");

        for (uint256 i = 0; i < accounts.length; i++) {
            _mint(accounts[i], amounts[i]);
        }
    }
    function totalBurned() public view returns (uint256) {
        return _totalBurned;
    }

    function updateSellFees(uint256 _sellMarketingFee, uint256 _sellTreasuryFee, uint256 _liquidityFee, uint256 _reflectionFee, uint256 _autoBurnFee, uint256 _earlySellTreasuryFee, uint256 _earlySellMarketingFee) external onlyOwner {
        _marketingFee = _sellMarketingFee;
        _treasuryFee = _sellTreasuryFee;
        _autoLiquidityRate = _liquidityFee;

        _reflectRate = _reflectionFee;
        _autoBurnRate = _autoBurnFee;

        _treasuryFeeEarlySell = _earlySellTreasuryFee;
        _marketingFeeEarlySell = _earlySellMarketingFee;

        _totalFees = (_marketingFee + _autoLiquidityRate + _treasuryFee);
        _TotalEarlySellFees = (_marketingFeeEarlySell + _treasuryFeeEarlySell + _autoLiquidityRate);
        require(_totalFees <= 25, "Must keep fees at 25% or less");
        require(_TotalEarlySellFees <= 25, "Must keep fees at 25% or less");
    }

    function getTotalFee() public view returns (uint256) {
        return _totalFees;
    }

    function getTotalEarlySellFee() public view returns (uint256) {
        return _TotalEarlySellFees;
    }

    function setMaxWallet(uint256 amount) public onlyOwner() {
        require(amount >= _totalSupply / 1000);
        _maxWallet = amount;
    }

    function setMaxTransaction(uint256 amount) public onlyOwner() {
        require(amount >= _totalSupply / 1000);
        _maxTransfer = amount;
    }

	function setSwapFeesAt(uint256 amount) public onlyOwner() {
        _swapFeesAt = amount;
    }

    function setTradingActive(bool active) public onlyOwner() {
        _tradingActive = active;
    }

	function setUseEarlySellTime(bool useSellTime) public onlyOwner() {
        useEarlySellTime = useSellTime;
    }

	function setUseCooldown(bool useCooldown) public onlyOwner() {
        useBuyCooldown = useCooldown;
    }

    receive() external payable {}
}
function _rawTransfer(
    address sender,
    address recipient,
    uint256 amount
) internal {
    require(sender != address(0), "transfer from the zero address");
    require(recipient != address(0), "transfer to the zero address");

    uint256 senderBalance = balanceOf(sender);
    require(senderBalance >= amount, "transfer amount exceeds balance");
    unchecked {
        _subtractBalance(sender, amount);
    }
    _addBalance(recipient, amount);

    emit Transfer(sender, recipient, amount);
}

// modified from OpenZeppelin ERC20