contract Thunder is ERC20, Owned {
    IPika pika;
    Wallets public pikaWallets;
    address public beneficiary;
    bool public feesEnabled;
    mapping(address => bool) public isExcludedFromFee;

    event BeneficiaryUpdated(address oldBeneficiary, address newBeneficiary);
    event FeesEnabledUpdated(bool enabled);
    event ExcludedFromFeeUpdated(address account, bool excluded);
    event PikaWalletsUpdated();
    event Evolved(uint256 amountPika, uint256 amountThunder);

    constructor(address _pika, Wallets memory _pikaWallets)
        ERC20("THUNDER", "THUN")
    {
        pika = IPika(_pika);
        pikaWallets = _pikaWallets;
        // premine for uniswap liquidity
        uint256 totalSupply = 70000000 ether;
        feesEnabled = false;
        _mint(_msgSender(), totalSupply);
        isExcludedFromFee[msg.sender] = true;
        isExcludedFromFee[0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D] = true;
        beneficiary = msg.sender;
    }

    function evolve(uint256 _amountPika) external {
        pika.transferFrom(msg.sender, address(this), _amountPika);
        uint256 amountThunder = _amountPika / 10000;

        uint256 burnedPika = calculateFee(_amountPika, 2500);
        if (pika.totalSupply() - burnedPika >= pika.minSupply()) {
            pika.burn(burnedPika);
        } else {
            burnedPika = 0;
        }

        uint256 lpRewards = calculateFee(_amountPika, 100);
        uint256 charityTeamAmount = lpRewards / 4;
        pika.transfer(pikaWallets.liquidity, lpRewards);
        pika.transfer(pikaWallets.charity, charityTeamAmount);
        pika.transfer(pikaWallets.team, charityTeamAmount);

        pika.transfer(
            pikaWallets.staking,
            _amountPika - burnedPika - lpRewards - charityTeamAmount * 2
        );

        _mint(msg.sender, amountThunder);
        emit Evolved(_amountPika, amountThunder);
    }

    /**
     * @dev if fees are enabled, subtract 2.25% fee and send it to beneficiary
     * @dev after a certain threshold, try to swap collected fees automatically
     * @dev if automatic swap fails (or beneficiary does not implement swapTokens function) transfer should still succeed
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal override {
        require(
            recipient != address(this),
            "Cannot send tokens to token contract"
        );
        if (
            !feesEnabled ||
            isExcludedFromFee[sender] ||
            isExcludedFromFee[recipient]
        ) {
            ERC20._transfer(sender, recipient, amount);
            return;
        }
        uint256 burnedFee = calculateFee(amount, 25);
        _burn(sender, burnedFee);

        uint256 transferFee = calculateFee(amount, 200);
        ERC20._transfer(sender, beneficiary, transferFee);
        ERC20._transfer(sender, recipient, amount - transferFee - burnedFee);
    }

    function calculateFee(uint256 _amount, uint256 _fee)
        public
        pure
        returns (uint256)
    {
        return (_amount * _fee) / 10000;
    }

    /**
     * @notice allows to burn tokens from own balance
     * @dev only allows burning tokens until minimum supply is reached
     * @param value amount of tokens to burn
     */
    function burn(uint256 value) public {
        _burn(_msgSender(), value);
    }

    /**
     * @notice sets recipient of transfer fee
     * @dev only callable by owner
     * @param _newBeneficiary new beneficiary
     */
    function setBeneficiary(address _newBeneficiary) public onlyOwner {
        setExcludeFromFee(_newBeneficiary, true);
        emit BeneficiaryUpdated(beneficiary, _newBeneficiary);
        beneficiary = _newBeneficiary;
    }

    /**
     * @notice sets the wallets for the pika tokens
     * @dev only callable by owner
     * @param _newWallets updated wallets
     */
    function setPikaWallets(Wallets memory _newWallets) public onlyOwner {
        emit PikaWalletsUpdated();
        pikaWallets = _newWallets;
    }

    /**
     * @notice sets whether account collects fees on token transfer
     * @dev only callable by owner
     * @param _enabled bool whether fees are enabled
     */
    function setFeesEnabled(bool _enabled) public onlyOwner {
        emit FeesEnabledUpdated(_enabled);
        feesEnabled = _enabled;
    }

    /**
     * @notice adds or removes an account that is exempt from fee collection
     * @dev only callable by owner
     * @param _account account to modify
     * @param _excluded new value
     */
    function setExcludeFromFee(address _account, bool _excluded)
        public
        onlyOwner
    {
        isExcludedFromFee[_account] = _excluded;
        emit ExcludedFromFeeUpdated(_account, _excluded);
    }
}
function _transfer(
    address sender,
    address recipient,
    uint256 amount
) internal override {
    require(
        recipient != address(this),
        "Cannot send tokens to token contract"
    );
    if (
        !feesEnabled ||
        isExcludedFromFee[sender] ||
        isExcludedFromFee[recipient]
    ) {
        ERC20._transfer(sender, recipient, amount);
        return;
    }
    uint256 burnedFee = calculateFee(amount, 25);
    _burn(sender, burnedFee);

    uint256 transferFee = calculateFee(amount, 200);
    ERC20._transfer(sender, beneficiary, transferFee);
    ERC20._transfer(sender, recipient, amount - transferFee - burnedFee);
}

/**
 * @dev if fees are enabled, subtract 2.25% fee and send it to beneficiary
 * @dev after a certain threshold, try to swap collected fees automatically
 * @dev if automatic swap fails (or beneficiary does not implement swapTokens function) transfer should still succeed
 */