# coding: utf-8

"""
    LUSID API

    FINBOURNE Technology  # noqa: E501

    Contact: info@finbourne.com
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from datetime import datetime
from typing import Any, Dict, List, Optional, Union
from pydantic.v1 import Field, StrictBool, StrictFloat, StrictInt, StrictStr, conlist, validator
from lusid.models.barrier import Barrier
from lusid.models.lusid_instrument import LusidInstrument
from lusid.models.premium import Premium
from lusid.models.touch import Touch

class FxOption(LusidInstrument):
    """
    LUSID representation of an FX Option.  Including Vanilla, American, European, and Digital (Binary) options.  # noqa: E501
    """
    start_date: datetime = Field(..., alias="startDate", description="The start date of the instrument. This is normally synonymous with the trade-date.")
    dom_ccy: StrictStr = Field(..., alias="domCcy", description="The domestic currency of the instrument.")
    dom_amount: Optional[Union[StrictFloat, StrictInt]] = Field(None, alias="domAmount", description="The Amount of DomCcy that will be exchanged if the option is exercised.  This amount should be a positive number, with the Call/Put flag used to indicate direction.  The corresponding amount of FgnCcy that will be exchanged is this amount times the strike.  Note there is no rounding performed on this computed value.  This is an optional field, if not set the option ContractSize will default to 1.")
    fgn_ccy: StrictStr = Field(..., alias="fgnCcy", description="The foreign currency of the FX.")
    fgn_amount: Optional[Union[StrictFloat, StrictInt]] = Field(None, alias="fgnAmount", description="For a vanilla FxOption contract, FgnAmount cannot be set.  In case of a digital FxOption (IsPayoffDigital==true)  a payoff (if the option is in the money) can be either  in domestic or in foreign currency - for the latter  FgnAmount must be set.  Note: It is invalid to have FgnAmount and DomAmount  at the same time.")
    strike: Optional[Union[StrictFloat, StrictInt]] = Field(None, description="The strike of the option.")
    barriers: Optional[conlist(Barrier)] = Field(None, description="For a barrier option the list should not be empty. Up to two barriers are supported.  An option cannot be at the same time barrier- and touch-option.  One (or both) of the lists must be empty.")
    exercise_type: Optional[StrictStr] = Field(None, alias="exerciseType", description="Type of optionality that is present; European, American.    Supported string (enumeration) values are: [European, American].")
    is_call_not_put: StrictBool = Field(..., alias="isCallNotPut", description="True if the option is a call, false if the option is a put.")
    is_delivery_not_cash: StrictBool = Field(..., alias="isDeliveryNotCash", description="True if the option delivers the FX underlying, False if the option is settled in cash.")
    is_payoff_digital: Optional[StrictBool] = Field(None, alias="isPayoffDigital", description="By default IsPayoffDigital is false. If IsPayoffDigital=true,  the option is 'digital', and the option payoff is 0 or 1 unit of currency,  instead of a vanilla CallPayoff=max(spot-strike,0) or PutPayoff=max(strike-spot,0).")
    option_maturity_date: datetime = Field(..., alias="optionMaturityDate", description="The maturity date of the option.")
    option_settlement_date: datetime = Field(..., alias="optionSettlementDate", description="The settlement date of the option.")
    payout_style: Optional[StrictStr] = Field(None, alias="payoutStyle", description="PayoutStyle for touch options.                For options without touch optionality, payoutStyle should not be set.  For options with touch optionality (where the touches data has been set), payoutStyle must be defined and cannot be None.    Supported string (enumeration) values are: [Deferred, Immediate].")
    premium: Optional[Premium] = None
    touches: Optional[conlist(Touch)] = Field(None, description="For a touch option the list should not be empty. Up to two touches are supported.  An option cannot be at the same time barrier- and touch-option.  One (or both) of the lists must be empty.")
    instrument_type: StrictStr = Field(..., alias="instrumentType", description="The available values are: QuotedSecurity, InterestRateSwap, FxForward, Future, ExoticInstrument, FxOption, CreditDefaultSwap, InterestRateSwaption, Bond, EquityOption, FixedLeg, FloatingLeg, BespokeCashFlowsLeg, Unknown, TermDeposit, ContractForDifference, EquitySwap, CashPerpetual, CapFloor, CashSettled, CdsIndex, Basket, FundingLeg, FxSwap, ForwardRateAgreement, SimpleInstrument, Repo, Equity, ExchangeTradedOption, ReferenceInstrument, ComplexBond, InflationLinkedBond, InflationSwap, SimpleCashFlowLoan, TotalReturnSwap, InflationLeg, FundShareClass, FlexibleLoan")
    additional_properties: Dict[str, Any] = {}
    __properties = ["instrumentType", "startDate", "domCcy", "domAmount", "fgnCcy", "fgnAmount", "strike", "barriers", "exerciseType", "isCallNotPut", "isDeliveryNotCash", "isPayoffDigital", "optionMaturityDate", "optionSettlementDate", "payoutStyle", "premium", "touches"]

    @validator('instrument_type')
    def instrument_type_validate_enum(cls, value):
        """Validates the enum"""
        if value not in ('QuotedSecurity', 'InterestRateSwap', 'FxForward', 'Future', 'ExoticInstrument', 'FxOption', 'CreditDefaultSwap', 'InterestRateSwaption', 'Bond', 'EquityOption', 'FixedLeg', 'FloatingLeg', 'BespokeCashFlowsLeg', 'Unknown', 'TermDeposit', 'ContractForDifference', 'EquitySwap', 'CashPerpetual', 'CapFloor', 'CashSettled', 'CdsIndex', 'Basket', 'FundingLeg', 'FxSwap', 'ForwardRateAgreement', 'SimpleInstrument', 'Repo', 'Equity', 'ExchangeTradedOption', 'ReferenceInstrument', 'ComplexBond', 'InflationLinkedBond', 'InflationSwap', 'SimpleCashFlowLoan', 'TotalReturnSwap', 'InflationLeg', 'FundShareClass', 'FlexibleLoan'):
            raise ValueError("must be one of enum values ('QuotedSecurity', 'InterestRateSwap', 'FxForward', 'Future', 'ExoticInstrument', 'FxOption', 'CreditDefaultSwap', 'InterestRateSwaption', 'Bond', 'EquityOption', 'FixedLeg', 'FloatingLeg', 'BespokeCashFlowsLeg', 'Unknown', 'TermDeposit', 'ContractForDifference', 'EquitySwap', 'CashPerpetual', 'CapFloor', 'CashSettled', 'CdsIndex', 'Basket', 'FundingLeg', 'FxSwap', 'ForwardRateAgreement', 'SimpleInstrument', 'Repo', 'Equity', 'ExchangeTradedOption', 'ReferenceInstrument', 'ComplexBond', 'InflationLinkedBond', 'InflationSwap', 'SimpleCashFlowLoan', 'TotalReturnSwap', 'InflationLeg', 'FundShareClass', 'FlexibleLoan')")
        return value

    class Config:
        """Pydantic configuration"""
        allow_population_by_field_name = True
        validate_assignment = True

    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.dict(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> FxOption:
        """Create an instance of FxOption from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self):
        """Returns the dictionary representation of the model using alias"""
        _dict = self.dict(by_alias=True,
                          exclude={
                            "additional_properties"
                          },
                          exclude_none=True)
        # override the default output from pydantic by calling `to_dict()` of each item in barriers (list)
        _items = []
        if self.barriers:
            for _item in self.barriers:
                if _item:
                    _items.append(_item.to_dict())
            _dict['barriers'] = _items
        # override the default output from pydantic by calling `to_dict()` of premium
        if self.premium:
            _dict['premium'] = self.premium.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in touches (list)
        _items = []
        if self.touches:
            for _item in self.touches:
                if _item:
                    _items.append(_item.to_dict())
            _dict['touches'] = _items
        # puts key-value pairs in additional_properties in the top level
        if self.additional_properties is not None:
            for _key, _value in self.additional_properties.items():
                _dict[_key] = _value

        # set to None if dom_amount (nullable) is None
        # and __fields_set__ contains the field
        if self.dom_amount is None and "dom_amount" in self.__fields_set__:
            _dict['domAmount'] = None

        # set to None if fgn_amount (nullable) is None
        # and __fields_set__ contains the field
        if self.fgn_amount is None and "fgn_amount" in self.__fields_set__:
            _dict['fgnAmount'] = None

        # set to None if strike (nullable) is None
        # and __fields_set__ contains the field
        if self.strike is None and "strike" in self.__fields_set__:
            _dict['strike'] = None

        # set to None if barriers (nullable) is None
        # and __fields_set__ contains the field
        if self.barriers is None and "barriers" in self.__fields_set__:
            _dict['barriers'] = None

        # set to None if exercise_type (nullable) is None
        # and __fields_set__ contains the field
        if self.exercise_type is None and "exercise_type" in self.__fields_set__:
            _dict['exerciseType'] = None

        # set to None if payout_style (nullable) is None
        # and __fields_set__ contains the field
        if self.payout_style is None and "payout_style" in self.__fields_set__:
            _dict['payoutStyle'] = None

        # set to None if touches (nullable) is None
        # and __fields_set__ contains the field
        if self.touches is None and "touches" in self.__fields_set__:
            _dict['touches'] = None

        return _dict

    @classmethod
    def from_dict(cls, obj: dict) -> FxOption:
        """Create an instance of FxOption from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return FxOption.parse_obj(obj)

        _obj = FxOption.parse_obj({
            "instrument_type": obj.get("instrumentType"),
            "start_date": obj.get("startDate"),
            "dom_ccy": obj.get("domCcy"),
            "dom_amount": obj.get("domAmount"),
            "fgn_ccy": obj.get("fgnCcy"),
            "fgn_amount": obj.get("fgnAmount"),
            "strike": obj.get("strike"),
            "barriers": [Barrier.from_dict(_item) for _item in obj.get("barriers")] if obj.get("barriers") is not None else None,
            "exercise_type": obj.get("exerciseType"),
            "is_call_not_put": obj.get("isCallNotPut"),
            "is_delivery_not_cash": obj.get("isDeliveryNotCash"),
            "is_payoff_digital": obj.get("isPayoffDigital"),
            "option_maturity_date": obj.get("optionMaturityDate"),
            "option_settlement_date": obj.get("optionSettlementDate"),
            "payout_style": obj.get("payoutStyle"),
            "premium": Premium.from_dict(obj.get("premium")) if obj.get("premium") is not None else None,
            "touches": [Touch.from_dict(_item) for _item in obj.get("touches")] if obj.get("touches") is not None else None
        })
        # store additional fields in additional_properties
        for _key in obj.keys():
            if _key not in cls.__properties:
                _obj.additional_properties[_key] = obj.get(_key)

        return _obj
