contract Taxer is OwnableUpgradeSafe {
    using SafeMath for uint256;

    function initialize(address _tropAddress) public initializer {
        OwnableUpgradeSafe.__Ownable_init();
        tropTokenAddress = _tropAddress;
        feePercentX100 = 20;
        paused = true; // We start paused until sync post LGE happens.
        _editNoFeeList(tropTokenAddress, true); //this is to not apply fees for transfer from the token contrat itself
        isOnUniswap = false;
        nerdLpTokenMap[0x61E3FDF3Fb5808aCfd8b9cfE942c729c07b0fE21] = true;
        nerdLpTokenMap[0x3473C92d47A2226B1503dFe7C929b2aE454F6b22] = true;
        nerdLpTokenMap[0x7Ad060bd80E088F0c1adef7Aa50F4Df58BAf58d5] = true;
        nerdLpTokenList.push(0x3473C92d47A2226B1503dFe7C929b2aE454F6b22);
        nerdLpTokenList.push(0x61E3FDF3Fb5808aCfd8b9cfE942c729c07b0fE21);
        nerdLpTokenList.push(0x7Ad060bd80E088F0c1adef7Aa50F4Df58BAf58d5);
    }

    address tropTokenAddress;
    address tropVaultAddress;
    uint8 public feePercentX100; // max 255 = 25.5% artificial clamp
    bool paused;
    bool isOnUniswap;

    mapping(address => bool) public allowedTransferBeforeUniswap; //the list should contains sales and token contract
    mapping(address => bool) public noFeeList;
    mapping(address => bool) public nerdLpTokenMap;
    address[] public nerdLpTokenList;

    //fee discounts based on nerd of the user, the snapshot is taken once per day bydefault, any one can update their own
    mapping(address => uint256) public feeDiscounts;
    mapping(address => uint256) public feeDiscountsLastUpdate;

    // TROP token is pausable
    function setPaused(bool _pause) public onlyOwner {
        paused = _pause;
    }

    //this is to prevent some one else add token to uniswap before the token owner
    function setOnUniswap(bool _uni) public onlyOwner {
        isOnUniswap = _uni;
    }

    function setAllowedTransferBeforeUniswap(address _addr, bool allowed)
        public
        onlyOwner
    {
        allowedTransferBeforeUniswap[_addr] = allowed;
    }

    function setNerdLPAddresses(address[] calldata _addrs) public onlyOwner {
        //reset old list
        for (uint256 i = 0; i < nerdLpTokenList.length; i++) {
            nerdLpTokenMap[nerdLpTokenList[i]] = false;
        }
        delete nerdLpTokenList;
        for (uint256 i = 0; i < _addrs.length; i++) {
            nerdLpTokenList.push(_addrs[i]);
            nerdLpTokenMap[_addrs[i]] = true;
        }
    }

    function setFeeMultiplier(uint8 _feeMultiplier) public onlyOwner {
        feePercentX100 = _feeMultiplier;
    }

    function setTropVaultAddress(address _tropVaultAddress) public onlyOwner {
        tropVaultAddress = _tropVaultAddress;
        noFeeList[tropVaultAddress] = true;
    }

    function editNoFeeList(address _address, bool noFee) public onlyOwner {
        _editNoFeeList(_address, noFee);
    }

    function _editNoFeeList(address _address, bool noFee) internal {
        noFeeList[_address] = noFee;
    }

    IERC20 public nerd = IERC20(0x32C868F6318D6334B2250F323D914Bc2239E4EeE);
    INerdVault public nerdVault = INerdVault(
        0x47cE2237d7235Ff865E1C74bF3C6d9AF88d1bbfF
    );
    INerdStaking public nerdStaking = INerdStaking(
        0x357ADa6E0da1BB40668BDDd3E3aF64F472Cbd9ff
    );

    //any one can update this
    function computeDiscountFee(address _sender, bool _forceUpdate)
        public
        returns (uint256)
    {
        if (nerdLpTokenMap[_sender]) return 0; //no discount for lp token
        if (!_forceUpdate) {
            if (feeDiscountsLastUpdate[_sender].add(86400) > block.timestamp) {
                return feeDiscounts[_sender];
            }
        }
        //force update
        feeDiscountsLastUpdate[_sender] = block.timestamp;
        uint256 feeDiscount = 0;
        {
            uint256 poolLength = nerdLpTokenList.length;
            for (uint256 i = 0; i < poolLength; i++) {
                //lp amount
                uint256 lpAmount = nerdVault.getRemainingLP(i, _sender);
                uint256 lpSupply = IERC20(nerdLpTokenList[i]).balanceOf(
                    address(nerdVault)
                );
                uint256 shareX1000 = lpAmount.mul(1000).div(lpSupply);
                if (shareX1000 >= 10) {
                    //share > 1%
                    feeDiscounts[_sender] = 45;
                    return 45;
                } else if (shareX1000 >= 1) {
                    feeDiscount = 20;
                }
            }
        }

        {
            uint256 staked = nerdStaking.getRemainingNerd(_sender);
            if (staked >= 50e18) {
                feeDiscounts[_sender] = 30;
                return 30;
            } else if (staked >= 10e18) {
                feeDiscounts[_sender] = 20;
                return 20;
            }
        }
        if (feeDiscount >= 15) {
            feeDiscounts[_sender] = 15;
            return 15;
        }
        {
            uint256 nerdBal = nerd.balanceOf(_sender);
            if (nerdBal >= 50e18) {
                feeDiscounts[_sender] = 15;
                return 15;
            } else if (nerdBal >= 5e18) {
                feeDiscounts[_sender] = 10;
                return 10;
            }
        }
        return 0;
    }

    bool feeDiscountEnable = false;

    function setEnableFeeDiscount(bool _enable) external onlyOwner {
        feeDiscountEnable = _enable;
    }

    function calculateAmountsAfterFee(
        address sender,
        address recipient, // unusued maybe use din future
        uint256 amount
    )
        public
        returns (
            uint256 transferToAmount,
            uint256 transferToFeeDistributorAmount
        )
    {
        require(paused == false, "FEE APPROVER: Transfers Paused");
        //this is to prevent bad actors listing token on uniswap to create bad price before an official listing from the team

        if (noFeeList[sender]) {
            // Dont have a fee when tropvault is sending, or infinite loop
            transferToFeeDistributorAmount = 0;
            transferToAmount = amount;
        } else {
            if (feePercentX100 > 0) {
                uint256 discountFeePercent = 0;
                if (feeDiscountEnable) {
                    discountFeePercent = computeDiscountFee(sender, false);
                }
                transferToFeeDistributorAmount = amount.mul(feePercentX100).div(
                    1000
                );
                transferToFeeDistributorAmount = transferToFeeDistributorAmount
                    .mul(100 - discountFeePercent)
                    .div(100);
                transferToAmount = amount.sub(transferToFeeDistributorAmount);
            } else {
                transferToFeeDistributorAmount = 0;
                transferToAmount = amount;
            }
        }
    }
}
function setOnUniswap(bool _uni) public onlyOwner {
    isOnUniswap = _uni;
}

//this is to prevent some one else add token to uniswap before the token owner