// File: /contracts/PrismaVoterProxy.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "./interfaces/IDeposit.sol";
import "./interfaces/ITokenLocker.sol";
import "./interfaces/ITokenMinter.sol";
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';


contract PrismaVoterProxy {

    address public immutable prisma;
    address public immutable escrow;
    uint256 public constant MAX_LOCK_WEEKS = 52;

    address public owner;
    address public pendingOwner;
    address public operator;
    address public depositor;
    
    event SetPendingOwner(address indexed _address);
    event OwnerChanged(address indexed _address);
    
    constructor(address _prisma, address _veprisma){
        owner = msg.sender;
        prisma = _prisma;
        escrow = _veprisma;
        IERC20(prisma).approve(escrow, type(uint256).max);
    }

    function getName() external pure returns (string memory) {
        return "PrismaVoterProxy";
    }

    //set next owner
    function setPendingOwner(address _po) external {
        require(msg.sender == owner, "!auth");
        pendingOwner = _po;
        emit SetPendingOwner(_po);
    }

    //claim ownership
    function acceptPendingOwner() external {
        require(msg.sender == pendingOwner, "!p_owner");

        owner = pendingOwner;
        pendingOwner = address(0);
        emit OwnerChanged(owner);
    }

    function setOperator(address _operator) external {
        require(msg.sender == owner, "!auth");
        require(operator == address(0) || IDeposit(operator).isShutdown() == true, "needs shutdown");
        
        //require isshutdown interface
        require(IDeposit(_operator).isShutdown() == false, "no shutdown interface");
        
        operator = _operator;
    }

    function setDepositor(address _depositor) external {
        require(msg.sender == owner, "!auth");

        depositor = _depositor;
    }

    function lock(uint256 _value) external returns(bool){
        require(msg.sender == depositor, "!auth");
        ITokenLocker(escrow).lock(address(this), _value, MAX_LOCK_WEEKS);
        return true;
    }

    function freeze() external{
        require(msg.sender == depositor, "!auth");
        ITokenLocker(escrow).freeze();
    }

    function execute(
        address _to,
        uint256 _value,
        bytes calldata _data
    ) external returns (bool, bytes memory) {
        require(msg.sender == operator,"!auth");

        (bool success, bytes memory result) = _to.call{value:_value}(_data);

        return (success, result);
    }

}
// File: /contracts/interfaces/ITokenMinter.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ITokenMinter{
    function mint(address,uint256) external;
    function burn(address,uint256) external;
    function setOperator(address _operator, bool _active) external;
}
// File: /contracts/interfaces/ITokenLocker.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

interface ITokenLocker {
    struct LockData {
        uint amount;
        uint weeksToUnlock;
    }

    function lock(address _account, uint256 _amount, uint256 _weeks) external returns (bool);
    function withdrawExpiredLocks(uint256 _weeks) external returns (bool);
    function withdrawWithPenalty(uint amountToWithdraw) external returns (uint);

    function getAccountBalances(address account) external view returns (uint256 locked, uint256 unlocked);
    function getAccountActiveLocks(
        address account,
        uint minWeeks
    ) external view returns (LockData[] memory lockData, uint frozenAmount);

    function getAccountWeightAt(address account, uint week) external view returns (uint256);

    function getTotalWeightAt(uint week) external view returns (uint256);

    function getWithdrawWithPenaltyAmounts(address account, uint amountToWithdraw) external view returns (uint amountWithdrawn, uint penaltyAmountPaid);

    function lockToTokenRatio() external view returns (uint256);

    function freeze() external;
}

// File: /contracts/interfaces/IDeposit.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IDeposit {
   function isShutdown() external view returns(bool);
   function balanceOf(address _account) external view returns(uint256);
   function totalSupply() external view returns(uint256);
   function poolInfo(uint256) external view returns(address,address,address,address,address, bool);
   function rewardClaimed(uint256,address,uint256) external;
   function withdrawTo(uint256,uint256,address) external;
   function claimRewards(uint256,address) external returns(bool);
   function rewardArbitrator() external returns(address);
   function setGaugeRedirect(uint256 _pid) external returns(bool);
   function owner() external returns(address);
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}