contract StabilizeGasTreasury is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    
    // variables
    address public treasuryFurnaceAddress; // The address for the normal Treasury furnance
    address public operatorAddress; // The address for the operator contract
    uint256 public lastRebalanceTime; // The last time the gas treasury paid for a rebalance
    uint256 public maxGasPrice = 30; // This is the maximum gas price in Gwei that this contract will payout at
    uint256 public minRebalanceInterval = 86400; // The minimum amount of time needed in between rebalance periods
    address constant wethAddress = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

    constructor(
        address _treasury,
        address _operator
    ) public {
        treasuryFurnaceAddress = _treasury;
        operatorAddress = _operator;
    }
    
    // functions
    
    function balance() public view returns (uint256) {
        IERC20 token = IERC20(wethAddress);
        return token.balanceOf(address(this));        
    }
    
    function subsidizedMintNewWeek() external {
        uint256 startGas = gasleft();
        uint256 weiGasPriceMax = maxGasPrice.mul(10**9); // The maximum gas price in Wei units
        uint256 weiGasPrice = tx.gasprice; // The gas price for the current transaction
        if(weiGasPrice > weiGasPriceMax){
            // User should not spend more than the gas price max
            weiGasPrice = weiGasPriceMax;
        }
        Operator(operatorAddress).mintNewWeek(); // Mint the new week
        uint256 gasPayoutEstimate = startGas.sub(gasleft()).add(60000); // Estimate the amount of gas used by this operation and the next few
        gasPayoutEstimate = gasPayoutEstimate.mul(weiGasPrice);
        uint256 wethBalance = balance();
        if(gasPayoutEstimate > wethBalance){
            gasPayoutEstimate = wethBalance; // Payout cannot be greater than contract balance
        }
        if(gasPayoutEstimate > 0){
            WrappedEther(wethAddress).withdraw(gasPayoutEstimate); // This will send ETH to this contract and burn WETH
            msg.sender.transfer(gasPayoutEstimate); // Send ETH back to user
        }
    }
    
    function subsidizedRebalancePoolRewards() external {
        uint256 startGas = gasleft();
        require(now.sub(lastRebalanceTime) > minRebalanceInterval, "Too soon to run another subsidized rebalance event");
        lastRebalanceTime = now;
        uint256 weiGasPriceMax = maxGasPrice.mul(10**9); // The maximum gas price in Wei units
        uint256 weiGasPrice = tx.gasprice; // The gas price for the current transaction
        if(weiGasPrice > weiGasPriceMax){
            // User should not spend more than the gas price max
            weiGasPrice = weiGasPriceMax;
        }
        Operator(operatorAddress).rebalancePoolRewards(); // Rebalance the pool
        uint256 gasPayoutEstimate = startGas.sub(gasleft()).add(60000); // Estimate the amount of gas used by this operation and the next few
        gasPayoutEstimate = gasPayoutEstimate.mul(weiGasPrice);
        uint256 wethBalance = balance();
        if(gasPayoutEstimate > wethBalance){
            gasPayoutEstimate = wethBalance; // Payout cannot be greater than contract balance
        }
        if(gasPayoutEstimate > 0){
            WrappedEther(wethAddress).withdraw(gasPayoutEstimate); // This will send ETH to this contract and burn WETH
            msg.sender.transfer(gasPayoutEstimate); // Send ETH back to user
        }
    }
    
    receive() external payable {
        // We need an anonymous fallback function to accept ether into this contract
    }
    
    // Governance only functions
    
    // Non-timelock governance
    
    // Recover non-WETH lost tokens
    function recoverLostTokens(address _token, uint256 _amount) external onlyGovernance {
        require(_token != wethAddress, "Cannot recover WETH via this function");
        IERC20 token = IERC20(_token);
        token.safeTransfer(governance(), _amount);        
    }
    
    // Governance at anytime can push WETH to the main TreasuryFurnace
    function pushBalanceToTreasury(uint256 _amount) external onlyGovernance {
        IERC20 weth = IERC20(wethAddress);
        weth.safeTransfer(treasuryFurnaceAddress, _amount);        
    }
    
    // Timelock variables
    // Timelock doesn't activate until WETH is present at address
    
    uint256 private _timelockStart; // The start of the timelock to change governance variables
    uint256 private _timelockType; // The function that needs to be changed
    uint256 constant _timelockDuration = 86400; // Timelock is 24 hours
    
    // Reusable timelock variables
    address private _timelock_address;
    uint256 private _timelock_data;
    
    modifier timelockConditionsMet(uint256 _type) {
        require(_timelockType == _type, "Timelock not acquired for this function");
        _timelockType = 0; // Reset the type once the timelock is used
        if(balance() > 0){
            // Timelock is only required after weth is in contract
            require(now >= _timelockStart + _timelockDuration, "Timelock time not met");
        }
        _;
    }
    
    // Change the owner of the token contract
    // --------------------
    function startGovernanceChange(address _address) external onlyGovernance {
        _timelockStart = now;
        _timelockType = 1;
        _timelock_address = _address;       
    }
    
    function finishGovernanceChange() external onlyGovernance timelockConditionsMet(1) {
        transferGovernance(_timelock_address);
    }
    // --------------------
   
    // Change the treasury furnance address
    // --------------------
    function startChangeTreasuryFurnace(address _address) external onlyGovernance {
        _timelockStart = now;
        _timelockType = 2;
        _timelock_address = _address;
    }
    
    function finishChangeTreasuryFurnace() public onlyGovernance timelockConditionsMet(2) {
        treasuryFurnaceAddress = _timelock_address;
    }
    // --------------------
    
    // Change the gas price maximum
    // --------------------
    function startChangeGasPriceMax(uint256 _price) external onlyGovernance {
        require(_price > 0, "Price must be greater than 0");
        _timelockStart = now;
        _timelockType = 3;
        _timelock_data = _price;
    }
    
    function finishChangeGasPriceMax() public onlyGovernance timelockConditionsMet(3) {
        maxGasPrice = _timelock_data;
    }
    // --------------------
    
    // Change the gas price maximum
    // --------------------
    function startChangeMinInterval(uint256 _interval) external onlyGovernance {
        _timelockStart = now;
        _timelockType = 4;
        _timelock_data = _interval;
    }
    
    function finishChangeMinInterval() public onlyGovernance timelockConditionsMet(4) {
        minRebalanceInterval = _timelock_data;
    }
    // --------------------
    
    // Pull funds from contract to governance for expenditures
    // --------------------
    function startWithdrawForGovernance(uint256 _amount) external onlyGovernance {
        _timelockStart = now;
        _timelockType = 5;
        _timelock_data = _amount;
    }
    
    function finishWithdrawForGovernance() public onlyGovernance timelockConditionsMet(5) {
        uint256 amount = _timelock_data;
        IERC20 weth = IERC20(wethAddress);
        weth.safeTransfer(governance(), amount);
    }
    // --------------------
}
// Gas Treasury is an intermediate treasury that can be used to pay for operations required by the Stabilize protocol
// The gas treasury can call mintnewweek and rebalancepoolrewards and pay the caller back for those calls (up to a certain gas price)
// The treasury is also used as a reservoir for the team to obtain funds needed to build new contracts or run maintenance calls
// The team can also forward funds from the gas treasury to the treasury furnace when ready
// In case of lost tokens, team can recover them
function balance() public view returns (uint256) {
    IERC20 token = IERC20(wethAddress);
    return token.balanceOf(address(this));        
}

// functions