// Contract: EOU
// Target function: function _transferStandard(address sender, address recipient, uint256 tAmount) private
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return payable(msg.sender);
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    function owner() public view returns (address) {
        return _owner;
    }

    modifier onlyOwner() {
        require(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
   
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract EOU is Context, IERC20, Ownable {
    mapping (address => uint256) private _rOwned;
    mapping (address => uint256) private _tOwned;
    mapping (address => mapping (address => uint256)) private _allowances;

    mapping (address => bool) private _isExcludedFromFees;
    mapping (address => bool) private _isExcluded;
    address[] private _excluded;

    string private _name     = "Exodus Our Universe";
    string private _symbol   = "EOU";  
    uint8  private _decimals = 18;
   
    uint256 private constant MAX = type(uint256).max;
    uint256 private _tTotal = 100_000_000 * (10 ** _decimals);
    uint256 private _rTotal = (MAX - (MAX % _tTotal));
    uint256 private _tFeeTotal;

    uint256 public taxCap = 10;

    uint256 public reflectionFee = 1;
    uint256 private _previousReflectionFee = reflectionFee;
    
    uint256 public liquidityFee = 2;
    uint256 private _previousLiquidityFee = liquidityFee;

    uint256 public marketingFee = 2;
    uint256 private _previousMarketingFee = marketingFee;

    uint256 public developmentFee = 5;
    uint256 private _previousDevelopmentFee = developmentFee;

    address public marketingWallet = 0x0000000000000000000000000000000000000000;
    address public liquidityWallet = 0x0000000000000000000000000000000000000000;
    address public developmentWallet = 0x0000000000000000000000000000000000000000;

    event ExcludeFromFees(address indexed account, bool isExcluded);
    event MarketingWalletChanged(address marketingWallet);
    event LiquidityWalletChanged(address liquidityWallet);
    event DevelopmentWalletChanged(address developmentWallet);
    event ReflectionFeeChanged(uint256 reflectionFee);
    event LiquidityFeeChanged(uint256 liquidityFee);
    event MarketingFeeChanged(uint256 marketingFee);
    event DevelopmentFeeChanged(uint256 developmentFee);
    event TaxCapChanged(uint256 taxCap);
    
    constructor() { 
        _isExcludedFromFees[owner()] = true;
        _isExcludedFromFees[marketingWallet] = true;
        _isExcludedFromFees[liquidityWallet] = true;
        _isExcludedFromFees[developmentWallet] = true;
        _isExcludedFromFees[address(this)] = true;

        _rOwned[owner()] = _rTotal;
        emit Transfer(address(0), owner(), _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) {
        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;
    }

    // This function returns the amount of tokens that a spender is allowed to spend on behalf of an owner
    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    // This function allows a spender to spend tokens on behalf of the caller, up to the approved amount.
    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    // This function transfers tokens from the sender's address to the recipient's address on 
    // the condition that the sender has previously been approved to spend the required amount of tokens.
    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - amount);
        return true;
    }

    // This function increases the amount of tokens that a spender is allowed to spend on behalf of the caller.
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    // This function decreases the amount of tokens that a spender is allowed to spend on behalf of the caller.
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
        return true;
    }

    // This function returns a boolean indicating whether an account is excluded from receiving rewards.
    function isExcludedFromReward(address account) public view returns (bool) {
        return _isExcluded[account];
    }

    // This function returns the total amount of reflections distributed as a result of token transfers.
    function totalReflectionDistributed() public view returns (uint256) {
        return _tFeeTotal;
    }

    // This is a public view function that calculates and returns the corresponding token amount for a 
    // given reflection amount rAmount. Reflections are a technique used by some tokens to calculate rewards 
    // and fees, and this function converts them back to their equivalent token amount.
    function tokenFromReflection(uint256 rAmount) public view returns(uint256) {
        require(rAmount <= _rTotal, "Amount must be less than total reflections");
        uint256 currentRate =  _getRate();
        return rAmount / currentRate;
    }

    // This is a public function that allows the owner of the contract to exclude an address account from 
    // receiving rewards for holding the token. If the address is not already excluded, the function sets 
    // _isExcluded[account] to true and adds it to the _excluded array. If the address already owns some tokens, 
    // it calculates the token amount based on the reflection balance and sets 
    function excludeFromReward(address account) public onlyOwner() {
        require(!_isExcluded[account], "Account is already excluded");
        if(_rOwned[account] > 0) {
            _tOwned[account] = tokenFromReflection(_rOwned[account]);
        }
        _isExcluded[account] = true;
        _excluded.push(account);
    }

    // This is a public function that allows the owner of the contract to include an address account in receiving 
    // rewards for holding the token. If the address is already excluded, the function sets _isExcluded[account] 
    // to false and removes it from the _excluded array. If the address was previously excluded, it sets 
    // _tOwned[account] to 0.
    function includeInReward(address account) external onlyOwner() {
        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];
                _tOwned[account] = 0;
                _isExcluded[account] = false;
                _excluded.pop();
                break;
            }
        }
    }

    // This is a private function that handles the reflection fee mechanism. It reduces the total reflection 
    // supply _rTotal by rFee and increases the total fee _tFeeTotal by tFee.
    function _reflectFee(uint256 rFee, uint256 tFee) private {
        _rTotal = _rTotal - rFee;
        _tFeeTotal = _tFeeTotal + tFee;
    }

    // This is a private function that approves a spender spender to transfer tokens on behalf of the owner 
    // owner up to a certain amount amount.
    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);
    }

    // This is a private function that transfers tokens from the from address to the to address, and handles 
    // the associated fees and liquidity. If either address is excluded from fees, it temporarily removes all 
    // fees before executing the transfer, and restores the fees afterward. It then calls one of several 
    // different transfer functions based on the combination of excluded or included status of the sender and 
    // recipient address.
    function _transfer(address from, address to, uint256 amount) private {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(amount > 0, "Transfer amount must be greater than zero");
        
        bool restoreFees = false;

        if (_isExcludedFromFees[from] || 
            _isExcludedFromFees[to]
        ) {
            removeAllFee();
            restoreFees = true;
        }

        if (_isExcluded[from] && !_isExcluded[to]) {
            _transferFromExcluded(from, to, amount);
        } else if (!_isExcluded[from] && _isExcluded[to]) {
            _transferToExcluded(from, to, amount);
        } else if (!_isExcluded[from] && !_isExcluded[to]) {
            _transferStandard(from, to, amount);
        } else if (_isExcluded[from] && _isExcluded[to]) {
            _transferBothExcluded(from, to, amount);
        } else {
            _transferStandard(from, to, amount);
        }

        if (restoreFees) {
            restoreAllFee();
        }
    }