// Contract: REFLECT
// Target function: function setRewardToken (address _token, bool ETH) external onlyOwner
 * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

interface IDividendDistributor {
    function setShare(address shareholder, uint256 amount) external;
    function deposit() external payable;
   
}

contract DividendDistributor is IDividendDistributor {
    using SafeMath for uint256;

    address _token;

    struct Share {
        uint256 amount;
        uint256 totalExcluded;
        uint256 totalRealised;
    }

    IERC20 RWRD = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); //replace with reward token address here
    
    IUniswapV2Router02 router;
    bool ETH;

    address[] shareholders;
    mapping (address => uint256) shareholderIndexes;
    mapping (address => uint256) shareholderClaims;

    mapping (address => Share) public shares;

    uint256 public totalShares;
    uint256 public totalDividends;
    uint256 public totalDistributed;
    uint256 public dividendsPerShare;
    uint256 public dividendsPerShareAccuracyFactor = 10 ** 36;

    bool initialized;
    modifier initialization() {
        require(!initialized);
        _;
        initialized = true;
    }

    modifier onlyToken() {
        require(msg.sender == _token); _;
    }

    constructor (address _router) {
        router = _router != address(0)
            ? IUniswapV2Router02(_router)
            : IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        _token = msg.sender;
    }

   

    function setShare(address shareholder, uint256 amount) external override onlyToken {
        

        if(amount > 0 && shares[shareholder].amount == 0){
            addShareholder(shareholder);
        }else if(amount == 0 && shares[shareholder].amount > 0){
            removeShareholder(shareholder);
        }

        totalShares = totalShares.sub(shares[shareholder].amount).add(amount);
        shares[shareholder].amount = amount;
        shares[shareholder].totalExcluded = getCumulativeDividends(shares[shareholder].amount);
    }

    function updateRewardToken (address _newToken, bool isETH) external onlyToken{
        require (_newToken != address(RWRD)&& _newToken != address(0),"already have same reward token & address should not be zero address");
        if(!isETH && _newToken != address(router.WETH())){
        if(ETH){
            ETH = false;
        }    
        RWRD = IERC20(_newToken);
        }
        if(isETH || _newToken == address(router.WETH())){
            ETH = true;
        }
    }

    function deposit() public payable override onlyToken {
       
        uint256 amount = msg.value;

        totalDividends = totalDividends.add(amount);
        dividendsPerShare = dividendsPerShare.add(dividendsPerShareAccuracyFactor.mul(amount).div(totalShares));
    }


    function distributeDividend(address shareholder) internal {
        if(shares[shareholder].amount == 0){ return; }

        uint256 amount = getUnpaidEarnings(shareholder);
            totalDistributed = totalDistributed.add(amount);
            if(!ETH){
            address[] memory path = new address[](2);
            path[0] = router.WETH();
            path[1] = address(RWRD);

           router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: amount}(
            0,
            path,
            shareholder,
            block.timestamp
            );
            }
        if(ETH){
            (bool os,) = shareholder.call{value: amount}("");
            require (os,"eth transfer failed");
        }
            shareholderClaims[shareholder] = block.timestamp;
            shares[shareholder].totalRealised = shares[shareholder].totalRealised.add(amount);
            shares[shareholder].totalExcluded = getCumulativeDividends(shares[shareholder].amount);
    }
    
    
    function claimDividend() external {
        distributeDividend(msg.sender);
    }

    function getUnpaidEarnings(address shareholder) public view returns (uint256) {
        if(shares[shareholder].amount == 0){ return 0; }

        uint256 shareholderTotalDividends = getCumulativeDividends(shares[shareholder].amount);
        uint256 shareholderTotalExcluded = shares[shareholder].totalExcluded;

        if(shareholderTotalDividends <= shareholderTotalExcluded){ return 0; }

        return shareholderTotalDividends.sub(shareholderTotalExcluded);
    }

    function getCumulativeDividends(uint256 share) internal view returns (uint256) {
        return share.mul(dividendsPerShare).div(dividendsPerShareAccuracyFactor);
    }

    function addShareholder(address shareholder) internal {
        shareholderIndexes[shareholder] = shareholders.length;
        shareholders.push(shareholder);
    }

    function removeShareholder(address shareholder) internal {
        shareholders[shareholderIndexes[shareholder]] = shareholders[shareholders.length-1];
        shareholderIndexes[shareholders[shareholders.length-1]] = shareholderIndexes[shareholder];
        shareholders.pop();
    }
   
   
}

contract REFLECT is ERC20, Ownable {
    using SafeMath for uint256;

    // CONFIG START
    
    uint256 private initialSupply;
   
    uint256 private denominator = 100;

    uint256 private swapThreshold = 0.0000005 ether; // The contract will only swap to ETH, once the fee tokens reach the specified threshold
    
    uint256 private reflectionTaxBuy;
    uint256 private marketingTaxBuy;
    
    uint256 private reflectionTaxSell;
    uint256 private marketingTaxSell;
   
    
    address private reflectionTaxWallet;
    address private marketingTaxWallet;

    uint256 public maxTxAmount;
    uint256 public maxWalletAmount;
   
    
    // CONFIG END
    
    mapping (address => bool) private blacklist;
    mapping (address => bool) private whitelisted;
    mapping (address => bool) public isDividendExempt;
    mapping (address => bool) public maxTxLimitExcluded;
    mapping (address => bool) public maxWalletLimitExcluded;

    
    mapping (string => uint256) private buyTaxes;
    mapping (string => uint256) private sellTaxes;
    mapping (string => address) private taxWallets;
    
    bool public taxStatus = true;
    
    IUniswapV2Router02 private uniswapV2Router02;
    IUniswapV2Factory private uniswapV2Factory;
    IUniswapV2Pair private uniswapV2Pair;
    DividendDistributor public distributor;

    constructor() ERC20("REFLECTARIUM", "REFRIUM") payable
    {
        initialSupply = 1000000000 * (10**18);
        _setOwner(msg.sender);
        maxTxAmount = initialSupply / 100;
        maxWalletAmount = maxTxAmount * 2;
        uniswapV2Router02 = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        uniswapV2Factory = IUniswapV2Factory(uniswapV2Router02.factory());
        uniswapV2Pair = IUniswapV2Pair(uniswapV2Factory.createPair(address(this), uniswapV2Router02.WETH()));
        setMarketingWallet(address(0xedDE8f4A918e3e29fbafe8cad5f49d90d9f4A327));
        setBuyTax(2,4);
        setSellTax(3,6);
         distributor = new DividendDistributor(address(uniswapV2Router02));
        isDividendExempt[address(uniswapV2Pair)] = true;
        isDividendExempt[address(this)] = true;
        isDividendExempt[msg.sender] = false;
        isDividendExempt[address(0xdead)] = true;
       
        whitelist(msg.sender);
        whitelist(address(this));
     
        _mint(msg.sender, initialSupply);
    }
    
    uint256 private marketingTokens;
    uint256 private reflectionTokens;
   
    
    /**
     * @dev Calculates the tax, transfer it to the contract. If the user is selling, and the swap threshold is met, it executes the tax.
     */
    function handleTax(address from, address to, uint256 amount) private returns (uint256) {
        address[] memory sellPath = new address[](2);
        sellPath[0] = address(this);
        sellPath[1] = uniswapV2Router02.WETH();
        
        if(!iswhitelistd(from) && !iswhitelistd(to)) {
            uint256 tax;
            uint256 baseUnit = amount / denominator;
            if (!maxTxLimitExcluded[from] || !maxTxLimitExcluded[to]){
                require (amount <= maxTxAmount, "Max Tx Limit reached");
            }
            if(from == address(uniswapV2Pair) ) { 
                tax += baseUnit * buyTaxes["marketing"];
                tax += baseUnit * buyTaxes["reflection"];

                
                
                if (amount >= maxTxAmount){
                    tax = tax * 8/10; // 20% tax off for buying equals to maxTx
                }
                
                if(tax > 0) {
                    _transfer(from, address(this), tax);   
                }
                
                marketingTokens += baseUnit * buyTaxes["marketing"];
                reflectionTokens += baseUnit * buyTaxes["reflection"];

                if (amount >= maxTxAmount){
                    marketingTokens = marketingTokens * 8 / 10;
                    reflectionTokens = reflectionTokens * 8 /10;
                }

               
            } else if(to == address(uniswapV2Pair)) {
                tax += baseUnit * sellTaxes["marketing"];
                tax += baseUnit * sellTaxes["reflection"];
               
                if(amount >= maxTxAmount / 10){
                    tax = tax * 15 / 10; // 1.5x tax for bigger sells
                }
                
                if(tax > 0 ) {
                    _transfer(from, address(this), tax);   
                }
                
                marketingTokens += baseUnit * sellTaxes["marketing"];
                reflectionTokens += baseUnit * sellTaxes["reflection"];

                if(amount >= maxTxAmount/10){
                    marketingTokens = marketingTokens * 15 / 10;
                    reflectionTokens = reflectionTokens * 15 /10;
                }
               
                
                uint256 taxSum = marketingTokens + reflectionTokens;
                
                if(taxSum == 0) return amount;
                
                uint256 ethValue = uniswapV2Router02.getAmountsOut(marketingTokens + reflectionTokens, sellPath)[1];
                
                if(ethValue >= swapThreshold) {
                    uint256 startBalance = address(this).balance;

                    uint256 toSell = marketingTokens + reflectionTokens;
                    
                    _approve(address(this), address(uniswapV2Router02), toSell);
            
                    uniswapV2Router02.swapExactTokensForETH(
                        toSell,
                        0,
                        sellPath,
                        address(this),
                        block.timestamp
                    );
                    
                    uint256 ethGained = address(this).balance - startBalance;
                    
                    
                    uint256 marketingETH = (ethGained * ((marketingTokens * 10**18) / taxSum)) / 10**18;
                    uint256 reflectionETH = (ethGained * ((reflectionTokens * 10**18) / taxSum)) / 10**18;
                    
                  (bool sent,) =  taxWallets["marketing"].call{value: marketingETH}("");
                  require (sent, "eth to marketing failed");
                    distributor.deposit{value :reflectionETH}();
                    
                    marketingTokens = 0;
                    reflectionTokens = 0;
                   
                }
                
            }
            
            amount -= tax;
            if(to != address(uniswapV2Pair) && !maxWalletLimitExcluded[to]){
                require(balanceOf(to) + amount <= maxWalletAmount,"Amount exceeds maxWallet Limit");
            }
        }
        
        return amount;
    }
    
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal override virtual {
        require(!isBlacklisted(msg.sender), "Token: sender blacklisted");
        require(!isBlacklisted(recipient), "Token: recipient blacklisted");
        require(!isBlacklisted(tx.origin), "Token: sender blacklisted");
        
        if(taxStatus) {
            amount = handleTax(sender, recipient, amount);   
        }
        
        super._transfer(sender, recipient, amount);

         if(!isDividendExempt[sender]) {
            try distributor.setShare(sender, balanceOf(sender)) {} catch {}
        }

        if(!isDividendExempt[recipient]) {
            try distributor.setShare(recipient, balanceOf(recipient)) {} catch {} 
        }
    }
    
     /**
     * @dev claim rewards
     */
    function claimDividend() external {
        distributor.claimDividend();
    }