library Actions {
    // possible actions that can be performed
    enum ActionType {
        OpenVault,
        MintShortOption,
        BurnShortOption,
        DepositLongOption,
        WithdrawLongOption,
        DepositCollateral,
        WithdrawCollateral,
        SettleVault,
        Redeem,
        Call,
        Liquidate
    }

    struct ActionArgs {
        // type of action that is being performed on the system
        ActionType actionType;
        // address of the account owner
        address owner;
        // address which we move assets from or to (depending on the action type)
        address secondAddress;
        // asset that is to be transfered
        address asset;
        // index of the vault that is to be modified (if any)
        uint256 vaultId;
        // amount of asset that is to be transfered
        uint256 amount;
        // each vault can hold multiple short / long / collateral assets but we are restricting the scope to only 1 of each in this version
        // in future versions this would be the index of the short / long / collateral asset that needs to be modified
        uint256 index;
        // any other data that needs to be passed in for arbitrary function calls
        bytes data;
    }

    struct MintArgs {
        // address of the account owner
        address owner;
        // index of the vault from which the asset will be minted
        uint256 vaultId;
        // address to which we transfer the minted oTokens
        address to;
        // oToken that is to be minted
        address otoken;
        // each vault can hold multiple short / long / collateral assets but we are restricting the scope to only 1 of each in this version
        // in future versions this would be the index of the short / long / collateral asset that needs to be modified
        uint256 index;
        // amount of oTokens that is to be minted
        uint256 amount;
    }

    struct BurnArgs {
        // address of the account owner
        address owner;
        // index of the vault from which the oToken will be burned
        uint256 vaultId;
        // address from which we transfer the oTokens
        address from;
        // oToken that is to be burned
        address otoken;
        // each vault can hold multiple short / long / collateral assets but we are restricting the scope to only 1 of each in this version
        // in future versions this would be the index of the short / long / collateral asset that needs to be modified
        uint256 index;
        // amount of oTokens that is to be burned
        uint256 amount;
    }

    struct OpenVaultArgs {
        // address of the account owner
        address owner;
        // vault id to create
        uint256 vaultId;
        // vault type, 0 for spread/max loss and 1 for naked margin vault
        uint256 vaultType;
    }

    struct DepositArgs {
        // address of the account owner
        address owner;
        // index of the vault to which the asset will be added
        uint256 vaultId;
        // address from which we transfer the asset
        address from;
        // asset that is to be deposited
        address asset;
        // each vault can hold multiple short / long / collateral assets but we are restricting the scope to only 1 of each in this version
        // in future versions this would be the index of the short / long / collateral asset that needs to be modified
        uint256 index;
        // amount of asset that is to be deposited
        uint256 amount;
    }

    struct RedeemArgs {
        // address to which we pay out the oToken proceeds
        address receiver;
        // oToken that is to be redeemed
        address otoken;
        // amount of oTokens that is to be redeemed
        uint256 amount;
    }

    struct WithdrawArgs {
        // address of the account owner
        address owner;
        // index of the vault from which the asset will be withdrawn
        uint256 vaultId;
        // address to which we transfer the asset
        address to;
        // asset that is to be withdrawn
        address asset;
        // each vault can hold multiple short / long / collateral assets but we are restricting the scope to only 1 of each in this version
        // in future versions this would be the index of the short / long / collateral asset that needs to be modified
        uint256 index;
        // amount of asset that is to be withdrawn
        uint256 amount;
    }

    struct SettleVaultArgs {
        // address of the account owner
        address owner;
        // index of the vault to which is to be settled
        uint256 vaultId;
        // address to which we transfer the remaining collateral
        address to;
    }

    struct LiquidateArgs {
        // address of the vault owner to liquidate
        address owner;
        // address of the liquidated collateral receiver
        address receiver;
        // vault id to liquidate
        uint256 vaultId;
        // amount of debt(otoken) to repay
        uint256 amount;
        // chainlink round id
        uint256 roundId;
    }

    struct CallArgs {
        // address of the callee contract
        address callee;
        // data field for external calls
        bytes data;
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for an open vault action
     * @param _args general action arguments structure
     * @return arguments for a open vault action
     */
    function _parseOpenVaultArgs(ActionArgs memory _args)
        internal
        pure
        returns (OpenVaultArgs memory)
    {
        require(
            _args.actionType == ActionType.OpenVault,
            "Actions: can only parse arguments for open vault actions"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot open vault for an invalid account"
        );

        // if not _args.data included, vault type will be 0 by default
        uint256 vaultType;

        if (_args.data.length == 32) {
            // decode vault type from _args.data
            vaultType = abi.decode(_args.data, (uint256));
        }

        // for now we only have 2 vault types
        require(
            vaultType < 2,
            "Actions: cannot open vault with an invalid type"
        );

        return
            OpenVaultArgs({
                owner: _args.owner,
                vaultId: _args.vaultId,
                vaultType: vaultType
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for a mint action
     * @param _args general action arguments structure
     * @return arguments for a mint action
     */
    function _parseMintArgs(ActionArgs memory _args)
        internal
        pure
        returns (MintArgs memory)
    {
        require(
            _args.actionType == ActionType.MintShortOption,
            "Actions: can only parse arguments for mint actions"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot mint from an invalid account"
        );

        return
            MintArgs({
                owner: _args.owner,
                vaultId: _args.vaultId,
                to: _args.secondAddress,
                otoken: _args.asset,
                index: _args.index,
                amount: _args.amount
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for a burn action
     * @param _args general action arguments structure
     * @return arguments for a burn action
     */
    function _parseBurnArgs(ActionArgs memory _args)
        internal
        pure
        returns (BurnArgs memory)
    {
        require(
            _args.actionType == ActionType.BurnShortOption,
            "Actions: can only parse arguments for burn actions"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot burn from an invalid account"
        );

        return
            BurnArgs({
                owner: _args.owner,
                vaultId: _args.vaultId,
                from: _args.secondAddress,
                otoken: _args.asset,
                index: _args.index,
                amount: _args.amount
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for a deposit action
     * @param _args general action arguments structure
     * @return arguments for a deposit action
     */
    function _parseDepositArgs(ActionArgs memory _args)
        internal
        pure
        returns (DepositArgs memory)
    {
        require(
            (_args.actionType == ActionType.DepositLongOption) ||
                (_args.actionType == ActionType.DepositCollateral),
            "Actions: can only parse arguments for deposit actions"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot deposit to an invalid account"
        );

        return
            DepositArgs({
                owner: _args.owner,
                vaultId: _args.vaultId,
                from: _args.secondAddress,
                asset: _args.asset,
                index: _args.index,
                amount: _args.amount
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for a withdraw action
     * @param _args general action arguments structure
     * @return arguments for a withdraw action
     */
    function _parseWithdrawArgs(ActionArgs memory _args)
        internal
        pure
        returns (WithdrawArgs memory)
    {
        require(
            (_args.actionType == ActionType.WithdrawLongOption) ||
                (_args.actionType == ActionType.WithdrawCollateral),
            "Actions: can only parse arguments for withdraw actions"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot withdraw from an invalid account"
        );
        require(
            _args.secondAddress != address(0),
            "Actions: cannot withdraw to an invalid account"
        );

        return
            WithdrawArgs({
                owner: _args.owner,
                vaultId: _args.vaultId,
                to: _args.secondAddress,
                asset: _args.asset,
                index: _args.index,
                amount: _args.amount
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for an redeem action
     * @param _args general action arguments structure
     * @return arguments for a redeem action
     */
    function _parseRedeemArgs(ActionArgs memory _args)
        internal
        pure
        returns (RedeemArgs memory)
    {
        require(
            _args.actionType == ActionType.Redeem,
            "Actions: can only parse arguments for redeem actions"
        );
        require(
            _args.secondAddress != address(0),
            "Actions: cannot redeem to an invalid account"
        );

        return
            RedeemArgs({
                receiver: _args.secondAddress,
                otoken: _args.asset,
                amount: _args.amount
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for a settle vault action
     * @param _args general action arguments structure
     * @return arguments for a settle vault action
     */
    function _parseSettleVaultArgs(ActionArgs memory _args)
        internal
        pure
        returns (SettleVaultArgs memory)
    {
        require(
            _args.actionType == ActionType.SettleVault,
            "Actions: can only parse arguments for settle vault actions"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot settle vault for an invalid account"
        );
        require(
            _args.secondAddress != address(0),
            "Actions: cannot withdraw payout to an invalid account"
        );

        return
            SettleVaultArgs({
                owner: _args.owner,
                vaultId: _args.vaultId,
                to: _args.secondAddress
            });
    }

    function _parseLiquidateArgs(ActionArgs memory _args)
        internal
        pure
        returns (LiquidateArgs memory)
    {
        require(
            _args.actionType == ActionType.Liquidate,
            "Actions: can only parse arguments for liquidate action"
        );
        require(
            _args.owner != address(0),
            "Actions: cannot liquidate vault for an invalid account owner"
        );
        require(
            _args.secondAddress != address(0),
            "Actions: cannot send collateral to an invalid account"
        );
        require(
            _args.data.length == 32,
            "Actions: cannot parse liquidate action with no round id"
        );

        // decode chainlink round id from _args.data
        uint256 roundId = abi.decode(_args.data, (uint256));

        return
            LiquidateArgs({
                owner: _args.owner,
                receiver: _args.secondAddress,
                vaultId: _args.vaultId,
                amount: _args.amount,
                roundId: roundId
            });
    }

    /**
     * @notice parses the passed in action arguments to get the arguments for a call action
     * @param _args general action arguments structure
     * @return arguments for a call action
     */
    function _parseCallArgs(ActionArgs memory _args)
        internal
        pure
        returns (CallArgs memory)
    {
        require(
            _args.actionType == ActionType.Call,
            "Actions: can only parse arguments for call actions"
        );
        require(
            _args.secondAddress != address(0),
            "Actions: target address cannot be address(0)"
        );

        return CallArgs({callee: _args.secondAddress, data: _args.data});
    }
}
/**
 * @title Actions
 * @author Opyn Team
 * @notice A library that provides a ActionArgs struct, sub types of Action structs, and functions to parse ActionArgs into specific Actions.
 */
function _parseBurnArgs(ActionArgs memory _args)
    internal
    pure
    returns (BurnArgs memory)
{
    require(
        _args.actionType == ActionType.BurnShortOption,
        "Actions: can only parse arguments for burn actions"
    );
    require(
        _args.owner != address(0),
        "Actions: cannot burn from an invalid account"
    );

    return
        BurnArgs({
            owner: _args.owner,
            vaultId: _args.vaultId,
            from: _args.secondAddress,
            otoken: _args.asset,
            index: _args.index,
            amount: _args.amount
        });
}

/**
 * @notice parses the passed in action arguments to get the arguments for a burn action
 * @param _args general action arguments structure
 * @return arguments for a burn action
 */