// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/Config.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**

   ____     ____        __      _   _________    _____      _____   
  / ___)   / __ \      /  \    / ) (_   _____)  (_   _)    / ___ \  
 / /      / /  \ \    / /\ \  / /    ) (___       | |     / /   \_) 
( (      ( ()  () )   ) ) ) ) ) )   (   ___)      | |    ( (  ____  
( (      ( ()  () )  ( ( ( ( ( (     ) (          | |    ( ( (__  ) 
 \ \___   \ \__/ /   / /  \ \/ /    (   )        _| |__   \ \__/ /  
  \____)   \____/   (_/    \__/      \_/        /_____(    \____/   
                                                                
Dapp: https://www.jungle-protocol.com/
Telegram: https://t.me/Jungle_Protocol
Twitter: https://twitter.com/Jungle_Protocol
 */

import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IAttackRewardCalculator.sol";
import "./interfaces/IBanansToken.sol";
import "./interfaces/IMonkey.sol";
import "./interfaces/IStolenPool.sol";
import "./interfaces/IDexInterfacer.sol";
import "./interfaces/IBananChef.sol";
import "./interfaces/IFullProtec.sol";

/**
Manager for config variables

Notes:
- once operation is stable, timelock will be set as owner
- openBananChefDeposits: first stolen pool epoch (epoch 0) starts at the same time as the banan chef deposits open
 */


contract Config is Ownable {
    //======================================================================================
    // setup
    //======================================================================================

    IMonkey public monkey;
    IBanansToken public banans;
    IStolenPool public stolenPool;
    IAttackRewardCalculator public rewardCalculator;
    IDexInterfacer public dexInterfacer;
    IBananChef public bananChef;
    IFullProtec public bananFullProtec;

    address public treasuryAddress;
    address public uniswapFactoryAddress; 
    address public uniswapRouterAddress; 
    address public banansPoolAddress;
    address public timelockControllerAddress;
    address public banansAddress;
    address public bananChefAddress;
    address public bananFullProtecAddress;
    address public bananStolenPoolAddress;
    address public monkeyAddress;
    address public randomizerAddress; 
    address public attackRewardCalculatorAddress;
    address public dexInterfacerAddress;

    constructor(
        address _treasuryAddress,
        address _uniswapFactoryAddress,
        address _uniswapRouterAddress,
        address _randomizerAddress
    ) {
        treasuryAddress = _treasuryAddress;
        uniswapFactoryAddress = _uniswapFactoryAddress;
        uniswapRouterAddress = _uniswapRouterAddress;
        randomizerAddress = _randomizerAddress;
    }

    function transferOwnershipToTimelock() external onlyOwner {
        transferOwnership(timelockControllerAddress);
    }

    //======================================================================================
    // include setters for each "global" parameter --> gated by onlyOwner
    //======================================================================================

    function setTreasuryAddress(address _treasuryAddress) external onlyOwner {
        treasuryAddress = _treasuryAddress;
    }

    function setUniswapFactoryAddress(address _uniswapFactoryAddress) external onlyOwner {
        uniswapFactoryAddress = _uniswapFactoryAddress;        
    }

    function setUniswapRouterAddress(address _uniswapRouterAddress) external onlyOwner {
        uniswapRouterAddress = _uniswapRouterAddress;        
    }

    function setBanansPoolAddress(address _banansPoolAddress) external onlyOwner {
        banansPoolAddress = _banansPoolAddress;        
    }

    function setTimelockControllerAddress(address _timelockControllerAddress) external onlyOwner {
        timelockControllerAddress = _timelockControllerAddress;       
    }

    function setBananTokenAddress(address _bananTokenAddress) external onlyOwner {
        banansAddress = _bananTokenAddress;
        banans = IBanansToken(_bananTokenAddress);    
    }

    function setBananChefAddress(address _bananChefAddress) external onlyOwner {
        bananChefAddress = _bananChefAddress;
        bananChef = IBananChef(_bananChefAddress);       
    }

    function setBananFullProtecAddress(address _fullProtecAddress) external onlyOwner {
        bananFullProtecAddress = _fullProtecAddress;
        bananFullProtec = IFullProtec(_fullProtecAddress);      
    }

    function setBananStolenPoolAddress(address _stolenPoolAddress) external onlyOwner {
        bananStolenPoolAddress = _stolenPoolAddress;
        stolenPool = IStolenPool(_stolenPoolAddress);   
    }

    function setMonkeyAddress(address _monkeyAddress) external onlyOwner {
        monkeyAddress = _monkeyAddress;
        monkey = IMonkey(_monkeyAddress);       
    }

    function setRandomizerAddress(address _randomizerRequesterAddress) external onlyOwner {
        randomizerAddress = _randomizerRequesterAddress;        
    }

    function setRewardCalculatorAddress(address _rewardCalculatorAddress) external onlyOwner {
        attackRewardCalculatorAddress = _rewardCalculatorAddress;
        rewardCalculator = IAttackRewardCalculator(_rewardCalculatorAddress);       
    }

    function setDexInterfacerAddress(address _dexInterfacerAddress) external onlyOwner {
        dexInterfacerAddress = _dexInterfacerAddress;
        dexInterfacer = IDexInterfacer(_dexInterfacerAddress);      
    }

    //======================================================================================
    // NEW SETTERS FOR BANANS CONFIG (CALLS FUNCTIONS ON BANANS)
    //======================================================================================

    function setBuyTaxRate(uint16 _buyTaxRate) external onlyOwner {
        banans.setBuyTaxRate(_buyTaxRate);
    }

    function setSellTaxRate(uint16 _sellTaxRate) external onlyOwner {
        banans.setSellTaxRate(_sellTaxRate);
    }

    function setFinalTaxRate() external onlyOwner {
        banans.setFinalTaxRate();
    }

    function removeMaxWallet() external onlyOwner {
        banans.removeMaxWallet();
    }

    function enableTrading() external onlyOwner {
        banans.enableTrading();
    }

    function addDexAddress(address _dexAddress) external onlyOwner {
        banans.addDexAddress(_dexAddress);
    }

    function removeDexAddress(address _dexAddress) external onlyOwner {
        banans.removeDexAddress(_dexAddress);
    }

    function setMaxScaleFactorDecreasePercentagePerDebase(uint256 _maxScaleFactorDecreasePercentagePerDebase) external onlyOwner {
        banans.setMaxScaleFactorDecreasePercentagePerDebase(_maxScaleFactorDecreasePercentagePerDebase);
    }

    function setTaxSwapAmountThreshold(uint256 _taxSwapAmountThreshold) external onlyOwner {
        banans.setTaxSwapAmountThreshold(_taxSwapAmountThreshold);
    }

    function setDivertTaxToStolenPoolRate(uint256 _divertRate) external onlyOwner {
        banans.setDivertTaxToStolenPoolRate(_divertRate);
    }

    //======================================================================================
    // NEW SETTERS FOR MONKEY (CALLS FUNCTIONS ON MONKEY)
    //======================================================================================

    function setMonkeyMintIsOpen(bool _monkeyMintIsOpen) external onlyOwner {
        monkey.setMonkeyMintIsOpen(_monkeyMintIsOpen);
    }

    function setMonkeyBatchSize(uint16 _monkeyBatchSize) external onlyOwner {
        monkey.setMonkeyBatchSize(_monkeyBatchSize);
    }

    function setMonkeyMintSecondsBetweenBatches(uint32 _monkeyMintSecondsBetweenBatches) external onlyOwner {
        monkey.setMonkeyMintSecondsBetweenBatches(_monkeyMintSecondsBetweenBatches);
    }

    function setMonkeyMaxPerWallet(uint8 _monkeyMaxPerWallet) external onlyOwner {
        monkey.setMonkeyMaxPerWallet(_monkeyMaxPerWallet);
    }

    function setMonkeyMintPriceInBanans(uint128 _monkeyMintPriceInBanans) external onlyOwner {
        monkey.setMonkeyMintPriceInBanans(_monkeyMintPriceInBanans);
    }

    function setMonkeyMintBananFeePercentageToBurn(uint16 _monkeyMintBananFeePercentageToBurn) external onlyOwner {
        monkey.setMonkeyMintBananFeePercentageToBurn(_monkeyMintBananFeePercentageToBurn);
    }

    function setMonkeyMintBananFeePercentageToStolenPool(uint16 _monkeyMintBananFeePercentageToStolenPool) external onlyOwner {
        monkey.setMonkeyMintBananFeePercentageToStolenPool(_monkeyMintBananFeePercentageToStolenPool);
    }

    function setMonkeyMintTier1Threshold(uint16 _monkeyMintTier1Threshold) external onlyOwner {
        monkey.setMonkeyMintTier1Threshold(_monkeyMintTier1Threshold);
    }

    function setMonkeyMintTier2Threshold(uint16 _monkeyMintTier2Threshold) external onlyOwner {
        monkey.setMonkeyMintTier2Threshold(_monkeyMintTier2Threshold);
    }

    function setMonkeyHP(uint8 _monkeyHP) external onlyOwner {
        monkey.setMonkeyHP(_monkeyHP);
    }

    function setMonkeyHitRate(uint16 _monkeyHitRate) external onlyOwner {
        monkey.setMonkeyHitRate(_monkeyHitRate);
    }

    function setMonkeyAttackIsOpen(bool _isOpen) external onlyOwner {
        stolenPool.setStolenPoolAttackIsOpen(_isOpen);
        monkey.setMonkeyAttackIsOpen(_isOpen);
    }

    function setAttackCooldownSeconds(uint32 _attackCooldownSeconds) external onlyOwner {
        monkey.setAttackCooldownSeconds(_attackCooldownSeconds);
    }

    function setAttackHPDeductionAmount(uint8 _attackHPDeductionAmount) external onlyOwner {
        monkey.setAttackHPDeductionAmount(_attackHPDeductionAmount);
    }

    function setAttackHPDeductionThreshold(uint16 _attackHPDeductionThreshold) external onlyOwner {
        monkey.setAttackHPDeductionThreshold(_attackHPDeductionThreshold);
    }


    //======================================================================================
    // NEW SETTERS FOR BANAN CHEF (CALLS FUNCTIONS ON BANAN CHEF)
    //======================================================================================

    function setBananChefPoolAllocPoints(uint256 _pid, uint128 _allocPoints, bool _withUpdate) external onlyOwner {
        bananChef.setAllocationPoint(_pid, _allocPoints, _withUpdate);
    }

    function setBananChefLockDuration(uint256 _pid, uint256 _lockDuration) external onlyOwner {
        bananChef.setLockDuration(_pid, _lockDuration);
    }

    function updateBananChefRewardPerBlock(uint88 _bananChefRewardPerBlock) external onlyOwner {
        bananChef.updateRewardPerBlock(_bananChefRewardPerBlock);
    }

    function setBananChefDebaseMultiplier(uint48 _debaseMultiplier) external onlyOwner {
        bananChef.setDebaseMultiplier(_debaseMultiplier);
    }

    function openBananChefDeposits() external onlyOwner {
        bananChef.openBananChefDeposits();
        stolenPool.setStolenPoolOpenTimestamp();
    }

    function setDepositIsPaused(bool _depositIsPaused) external onlyOwner {
        bananChef.setDepositIsPaused(_depositIsPaused);
    }

    function setClaimTaxRate(uint16 _maxClaimTaxRate) external onlyOwner {
        bananChef.setClaimTaxRate(_maxClaimTaxRate);
    }

    function setFullProtecLiquidityProportion(uint16 _fullProtecLiquidityProportion) external onlyOwner {
        bananChef.setFullProtecLiquidityProportion(_fullProtecLiquidityProportion);
    }

    //======================================================================================
    // NEW SETTERS FOR FULL PROTEC
    //======================================================================================

    function openFullProtecDeposits() external onlyOwner {
        bananFullProtec.openFullProtecDeposits();
    }

    function setFullProtecLockDuration(uint32 _lockDuration) external onlyOwner {
        bananFullProtec.setFullProtecLockDuration(_lockDuration);
    }

    function setThresholdFullProtecBananBalance(uint224 _thresholdFullProtecBananBalance) external onlyOwner {
        bananFullProtec.setThresholdFullProtecBananBalance(_thresholdFullProtecBananBalance);
    }

    //======================================================================================
    // NEW SETTERS FOR STOLEN POOL 
    //======================================================================================

    function setAttackBurnPercentage(uint16 _attackBurnPercentage) external onlyOwner {
        stolenPool.setAttackBurnPercentage(_attackBurnPercentage);
    }

    function setIsApprovedStolenPoolDepositor(address _depositor, bool _isApproved) external onlyOwner {
        stolenPool.setIsApprovedDepositor(_depositor, _isApproved);
    }
}
// File: contracts/interfaces/IAttackRewardCalculator.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IAttackRewardCalculator {
    function calculateRewardPerAttackByTier(
        uint256 tier1Attacks,
        uint256 tier2Attacks,
        uint256 tier3Attacks,
        uint256 tier1Weight,
        uint256 tier2Weight,
        uint256 tier3Weight,
        uint256 totalKarrotsDepositedThisEpoch
    ) external view returns (uint256[] memory);
}
// File: contracts/interfaces/IBananChef.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IBananChef {
    function deposit(uint256 _pid, uint256 _amount) external;
    function withdraw(uint256 _pid, uint256 _amount) external;
    function claim(uint256 _pid) external;
    function attack() external;
    function getUserStakedAmount(address _user) external view returns (uint256);
    function getTotalStakedAmount() external view returns (uint256);
    function setAllocationPoint(uint256 _pid, uint128 _allocPoint, bool _withUpdatePools) external;
    function setLockDuration(uint256 _pid, uint256 _lockDuration) external;
    function updateRewardPerBlock(uint88 _rewardPerBlock) external;
    function setDebaseMultiplier(uint48 _debaseMultiplier) external;
    function openBananChefDeposits() external;
    function setDepositIsPaused(bool _isPaused) external;
    function setThresholdFullProtecBananBalance(uint256 _thresholdFullProtecBananBalance) external;
    function setClaimTaxRate(uint16 _maxTaxRate) external;
    function setFullProtecLiquidityProportion(uint16 _fullProtecLiquidityProportion) external;
    function getFullToChefRatio(address _user) external view returns (uint256);
}
// File: contracts/interfaces/IBanansToken.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IBanansToken {
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function addDexAddress(address _dexAddress) external;
    function removeDexAddress(address _dexAddress) external;
    function mint(address to, uint256 amount) external;
    function burn(uint256 amount) external;
    function burnFrom(address account, uint256 amount) external;
    function rebase(uint256 epoch, uint256 indexDelta, bool positive) external returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function transferUnderlying(address to, uint256 value) external returns (bool);
    function fragmentToBanans(uint256 value) external view returns (uint256);
    function banansToFragment(uint256 banans) external view returns (uint256);
    function balanceOfUnderlying(address who) external view returns (uint256);
    function setSellTaxRate(uint16 _sellTaxRate) external;
    function setBuyTaxRate(uint16 _buyTaxRate) external;
    function setFinalTaxRate() external;
    function removeMaxWallet() external;
    function enableTrading() external;
    function setMaxScaleFactorDecreasePercentagePerDebase(uint256 _maxScaleFactorDecreasePercentagePerDebase) external;
    function setTaxSwapAmountThreshold(uint256 _taxSwapAmountThreshold) external;
    function setDivertTaxToStolenPoolRate(uint256 _divertRate) external;
}
// File: contracts/interfaces/IDexInterfacer.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IDexInterfacer {
    function updateConfig() external;
    function depositEth() external payable;
    function depositErc20(uint256 _amount) external;
    function getPoolIsCreated() external view returns (bool);
    function getPoolIsFunded() external view returns (bool);
}
// File: contracts/interfaces/IFullProtec.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IFullProtec {
    function getUserStakedAmount(address _user) external view returns (uint256);
    function getTotalStakedAmount() external view returns (uint256);
    function getIsUserAboveThresholdToAvoidClaimTax(address _user) external view returns (bool);
    function updateConfig() external;
    function openFullProtecDeposits() external;
    function setFullProtecLockDuration(uint32 _lockDuration) external;
    function setThresholdFullProtecBananBalance(uint224 _thresholdFullProtecBananBalance) external;
}
// File: contracts/interfaces/IMonkey.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IMonkey {
    function getMonkeySupply() external view returns (uint256);
    function getMonkeyIdsByOwner(address _owner) external view returns (uint256[] memory);
    function setMonkeyMintIsOpen(bool _isOpen) external;
    function setMonkeyBatchSize(uint16 _batchSize) external;
    function setMonkeyMintSecondsBetweenBatches(uint32 _secondsBetweenBatches) external;
    function setMonkeyMaxPerWallet(uint8 _maxPerWallet) external;
    function setMonkeyMintPriceInBanans(uint128 _priceInBanans) external;
    function setMonkeyMintBananFeePercentageToBurn(uint16 _monkeyMintBananFeePercentageToBurn) external;
    function setMonkeyMintBananFeePercentageToStolenPool(uint16 _monkeyMintBananFeePercentageToStolenPool) external;
    function setMonkeyMintTier1Threshold(uint16 _tier1Threshold) external;
    function setMonkeyMintTier2Threshold(uint16 _tier2Threshold) external;
    function setMonkeyHP(uint8 _HP) external;
    function setMonkeyHitRate(uint16 _hitRate) external;
    function setMonkeyAttackIsOpen(bool _isOpen) external;
    function setAttackCooldownSeconds(uint32 _attackCooldownSeconds) external;
    function setAttackHPDeductionAmount(uint8 _attackHPDeductionAmount) external;
    function setAttackHPDeductionThreshold(uint16 _attackHPDeductionThreshold) external;
}
// File: contracts/interfaces/IStolenPool.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IStolenPool {
    function virtualDeposit(uint256 _amount) external;
    function attack(address _sender, uint256 _monkeyTier, uint256 _monkeyId) external;
    function updateConfig() external;
    function setStolenPoolOpenTimestamp() external;
    function setStolenPoolAttackIsOpen(bool _isOpen) external;
    function setAttackBurnPercentage(uint16 _attackBurnPercentage) external;
    function setIsApprovedDepositor(address _depositor, bool _isApproved) external;
}