"""
Copyright (C) 2019 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
 and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
"""

from ibapi.common import UNSET_INTEGER, UNSET_DOUBLE, UNSET_DECIMAL, DOUBLE_INFINITY
from ibapi.object_implem import Object
from ibapi.softdollartier import SoftDollarTier
from ibapi.utils import decimalMaxString
from ibapi.utils import intMaxString
from ibapi.utils import floatMaxString

# enum Origin
(CUSTOMER, FIRM, UNKNOWN) = range(3)

# enum AuctionStrategy
(AUCTION_UNSET, AUCTION_MATCH,
 AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT) = range(4)

COMPETE_AGAINST_BEST_OFFSET_UP_TO_MID = DOUBLE_INFINITY

class OrderComboLeg(Object):
    def __init__(self):
        self.price = UNSET_DOUBLE  # type: float

    def __str__(self):
        return "%s" % floatMaxString(self.price)


class Order(Object):
    def __init__(self):
        self.softDollarTier = SoftDollarTier("", "", "")
        # order identifier
        self.orderId  = 0
        self.clientId = 0
        self.permId   = 0

        # main order fields
        self.action = ""
        self.totalQuantity = UNSET_DECIMAL
        self.orderType = ""
        self.lmtPrice      = UNSET_DOUBLE
        self.auxPrice      = UNSET_DOUBLE

        # extended order fields
        self.tif = ""                # "Time in Force" - DAY, GTC, etc.
        self.activeStartTime = ""   # for GTC orders
        self.activeStopTime = ""    # for GTC orders
        self.ocaGroup = ""          # one cancels all group name
        self.ocaType        = 0     # 1 = CANCEL_WITH_BLOCK, 2 = REDUCE_WITH_BLOCK, 3 = REDUCE_NON_BLOCK
        self.orderRef       = ""
        self.transmit       = True  # if false, order will be created but not transmited
        self.parentId       = 0     # Parent order Id, to associate Auto STP or TRAIL orders with the original order.
        self.blockOrder     = False
        self.sweepToFill    = False
        self.displaySize    = 0
        self.triggerMethod  = 0     # 0=Default, 1=Double_Bid_Ask, 2=Last, 3=Double_Last, 4=Bid_Ask, 7=Last_or_Bid_Ask, 8=Mid-point
        self.outsideRth     = False
        self.hidden         = False
        self.goodAfterTime       = ""   # Format: 20060505 08:00:00 {time zone}
        self.goodTillDate        = ""   # Format: 20060505 08:00:00 {time zone}
        self.rule80A             = ""   # Individual = 'I', Agency = 'A', AgentOtherMember = 'W', IndividualPTIA = 'J', AgencyPTIA = 'U', AgentOtherMemberPTIA = 'M', IndividualPT = 'K', AgencyPT = 'Y', AgentOtherMemberPT = 'N'
        self.allOrNone      = False
        self.minQty         = UNSET_INTEGER  # type: int
        self.percentOffset  = UNSET_DOUBLE  # type: float  # REL orders only
        self.overridePercentageConstraints = False
        self.trailStopPrice = UNSET_DOUBLE  # type: float
        self.trailingPercent = UNSET_DOUBLE # type: float  # TRAILLIMIT orders only

        # financial advisors only
        self.faGroup              = ""
        self.faProfile            = ""
        self.faMethod             = ""
        self.faPercentage         = ""

        # institutional (ie non-cleared) only
        self.designatedLocation = "" #used only when shortSaleSlot=2
        self.openClose     = ""     # O=Open, C=Close
        self.origin        = CUSTOMER  # 0=Customer, 1=Firm
        self.shortSaleSlot = 0         # type: int  # 1 if you hold the shares, 2 if they will be delivered from elsewhere.  Only for Action=SSHORT
        self.exemptCode    = -1

        # SMART routing only
        self.discretionaryAmt = 0
        self.optOutSmartRouting = False

        # BOX exchange orders only
        self.auctionStrategy = AUCTION_UNSET # type: int  # AUCTION_MATCH, AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT
        self.startingPrice   = UNSET_DOUBLE   # type: float
        self.stockRefPrice   = UNSET_DOUBLE   # type: float
        self.delta           = UNSET_DOUBLE   # type: float

        # pegged to stock and VOL orders only
        self.stockRangeLower = UNSET_DOUBLE   # type: float
        self.stockRangeUpper = UNSET_DOUBLE   # type: float

        self.randomizePrice = False
        self.randomizeSize = False

        # VOLATILITY ORDERS ONLY
        self.volatility            = UNSET_DOUBLE  # type: float
        self.volatilityType        = UNSET_INTEGER  # type: int  # 1=daily, 2=annual
        self.deltaNeutralOrderType = ""
        self.deltaNeutralAuxPrice  = UNSET_DOUBLE  # type: float
        self.deltaNeutralConId     = 0
        self.deltaNeutralSettlingFirm = ""
        self.deltaNeutralClearingAccount = ""
        self.deltaNeutralClearingIntent = ""
        self.deltaNeutralOpenClose = ""
        self.deltaNeutralShortSale = False
        self.deltaNeutralShortSaleSlot = 0
        self.deltaNeutralDesignatedLocation = ""
        self.continuousUpdate      = False
        self.referencePriceType    = UNSET_INTEGER  # type: int  # 1=Average, 2 = BidOrAsk

        # COMBO ORDERS ONLY
        self.basisPoints     = UNSET_DOUBLE  # type: float  # EFP orders only
        self.basisPointsType = UNSET_INTEGER  # type: int  # EFP orders only

        # SCALE ORDERS ONLY
        self.scaleInitLevelSize  = UNSET_INTEGER  # type: int
        self.scaleSubsLevelSize  = UNSET_INTEGER  # type: int
        self.scalePriceIncrement = UNSET_DOUBLE  # type: float
        self.scalePriceAdjustValue = UNSET_DOUBLE  # type: float
        self.scalePriceAdjustInterval = UNSET_INTEGER  # type: int
        self.scaleProfitOffset = UNSET_DOUBLE  # type: float
        self.scaleAutoReset = False
        self.scaleInitPosition = UNSET_INTEGER   # type: int
        self.scaleInitFillQty = UNSET_INTEGER    # type: int
        self.scaleRandomPercent = False
        self.scaleTable = ""

        # HEDGE ORDERS
        self.hedgeType             = "" # 'D' - delta, 'B' - beta, 'F' - FX, 'P' - pair
        self.hedgeParam            = "" # 'beta=X' value for beta hedge, 'ratio=Y' for pair hedge

        # Clearing info
        self.account               = "" # IB account
        self.settlingFirm          = ""
        self.clearingAccount       = ""   #True beneficiary of the order
        self.clearingIntent        = "" # "" (Default), "IB", "Away", "PTA" (PostTrade)

        # ALGO ORDERS ONLY
        self.algoStrategy          = ""

        self.algoParams            = None    #TagValueList
        self.smartComboRoutingParams = None  #TagValueList

        self.algoId = ""

        # What-if
        self.whatIf = False

        # Not Held
        self.notHeld = False
        self.solicited = False

        # models
        self.modelCode = ""

        # order combo legs

        self.orderComboLegs = None  # OrderComboLegListSPtr

        self.orderMiscOptions = None  # TagValueList

        # VER PEG2BENCH fields:
        self.referenceContractId = 0
        self.peggedChangeAmount = 0.
        self.isPeggedChangeAmountDecrease = False
        self.referenceChangeAmount = 0.
        self.referenceExchangeId = ""
        self.adjustedOrderType = ""

        self.triggerPrice = UNSET_DOUBLE
        self.adjustedStopPrice = UNSET_DOUBLE
        self.adjustedStopLimitPrice = UNSET_DOUBLE
        self.adjustedTrailingAmount = UNSET_DOUBLE
        self.adjustableTrailingUnit = 0
        self.lmtPriceOffset = UNSET_DOUBLE

        self.conditions = []  # std::vector<std::shared_ptr<OrderCondition>>
        self.conditionsCancelOrder = False
        self.conditionsIgnoreRth = False

        # ext operator
        self.extOperator = ""

        # native cash quantity
        self.cashQty = UNSET_DOUBLE

        self.mifid2DecisionMaker = ""
        self.mifid2DecisionAlgo = ""
        self.mifid2ExecutionTrader = ""
        self.mifid2ExecutionAlgo = ""

        self.dontUseAutoPriceForHedge = False

        self.isOmsContainer = False

        self.discretionaryUpToLimitPrice = False

        self.autoCancelDate = ""
        self.filledQuantity = UNSET_DECIMAL
        self.refFuturesConId = 0
        self.autoCancelParent = False
        self.shareholder = ""
        self.imbalanceOnly = False
        self.routeMarketableToBbo = False
        self.parentPermId = 0

        self.usePriceMgmtAlgo = None
        self.duration = UNSET_INTEGER
        self.postToAts = UNSET_INTEGER
        self.advancedErrorOverride = ""
        self.manualOrderTime = ""
        self.minTradeQty = UNSET_INTEGER
        self.minCompeteSize = UNSET_INTEGER
        self.competeAgainstBestOffset = UNSET_DOUBLE
        self.midOffsetAtWhole = UNSET_DOUBLE
        self.midOffsetAtHalf = UNSET_DOUBLE

    def __str__(self):
        s = "%s,%s,%s:" % (intMaxString(self.orderId), intMaxString(self.clientId), intMaxString(self.permId))

        s += " %s %s %s@%s" % (
            self.orderType,
            self.action,
            decimalMaxString(self.totalQuantity),
            floatMaxString(self.lmtPrice))

        s += " %s" % self.tif

        if self.orderComboLegs:
            s += " CMB("
            for leg in self.orderComboLegs:
                s += str(leg) + ","
            s += ")"

        if self.conditions:
            s += " COND("
            for cond in self.conditions:
                s += str(cond) + ","
            s += ")"

        return s
