/* Copyright (C) 2025 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. */

#pragma once
#ifndef TWS_API_CLIENT_ECLIENT_H
#define TWS_API_CLIENT_ECLIENT_H

#include <memory>
#include <string>
#include <vector>
#include <ostream>
#include <set>
#include "platformspecific.h"
#include "CommonDefs.h"
#include "TagValue.h"
#include "Contract.h"
#include "WshEventData.h"
#include "OrderCancel.h"
#include "EDecoder.h"
#include "ExecutionRequest.pb.h"
#include "PlaceOrderRequest.pb.h"
#include "CancelOrderRequest.pb.h"
#include "GlobalCancelRequest.pb.h"

namespace ibapi {
namespace client_constants {

/////////////////////////////////////////////////////////////////////////////////
// SOCKET CLIENT VERSION CHANGE LOG : Incremented when the format of incoming
//                                    server responses change
/////////////////////////////////////////////////////////////////////////////////
// constants
// 6 = Added parentId to orderStatus
// 7 = The new execDetails event returned for an order filled status and reqExecDetails
//     Also added market depth support.
// 8 = Added 'lastFillPrice' to orderStatus and 'permId' to execDetails
// 9 = Added 'avgCost', 'unrealizedPNL', and 'unrealizedPNL' to updatePortfolio event
// 10 = Added 'serverId' to the 'open order' & 'order status' events.
//      We send back all the API open orders upon connection.
//      Added new methods reqAllOpenOrders, reqAutoOpenOrders()
//      Added FA support - reqExecution has filter.
//                       - reqAccountUpdates takes acct code.
// 11 = Added permId to openOrder event.
// 12 = Added IgnoreRth, hidden, and discretionaryAmt
// 13 = Added GoodAfterTime
// 14 = always send size on bid/ask/last tick
// 15 = send allocation string with open order
// 16 = can receive account name in account and portfolio updates, and fa params in openOrder
// 17 = can receive liquidation field in exec reports, and notAutoAvailable field in mkt data
// 18 = can receive good till date field in open order messages, and send backfill requests
// 19 = can receive new extended order attributes in OPEN_ORDER
// 20 = expects TWS time string on connection after server version >= 20, and parentId in open order
// 21 = can receive bond contract details.
// 22 = can receive price magnifier in contract details
// 23 = support for scanner
// 24 = can receive volatility order parameters in open order messages
// 25 = can receive HMDS query start and end times
// 26 = can receive option vols in option market data messages
// 27 = can receive delta neutral order type and delta neutral aux price
// 28 = can receive option model computation ticks
// 29 = can receive trail stop limit price in open order and can place them: API 8.91
// 30 = can receive extended bond contract def, new ticks, and trade count in bars
// 31 = can receive EFP extensions to scanner and market data, and combo legs on open orders
//    ; can receive RT bars
// 32 = can receive TickType.LAST_TIMESTAMP
// 33 = can receive ScaleNumComponents and ScaleComponentSize is open order messages
// 34 = can receive whatIf orders / order state
// 35 = can receive contId field for Contract objects
// 36 = can receive outsideRth field for Order objects
// 37 = can receive clearingAccount and clearingIntent for Order objects
// 38 = can receive multiplier and primaryExchange in portfolio updates
//    ; can receive cumQty and avgPrice in execution
//    ; can receive fundamental data
//    ; can receive deltaNeutralContract for Contract objects
//    ; can receive reqId and end marker in contractDetails/bondContractDetails
//    ; can receive ScaleInitComponentSize and ScaleSubsComponentSize for Order objects
// 39 = can receive underConId in contractDetails
// 40 = can receive algoStrategy/algoParams in openOrder
// 41 = can receive end marker for openOrder
//    ; can receive end marker for account download
//    ; can receive end marker for executions download
// 42 = can receive deltaNeutralValidation
// 43 = can receive longName(companyName)
//    ; can receive listingExchange
//    ; can receive RTVolume tick
// 44 = can receive end market for ticker snapshot
// 45 = can receive notHeld field in openOrder
// 46 = can receive contractMonth, industry, category, subcategory fields in contractDetails
//    ; can receive timeZoneId, tradingHours, liquidHours fields in contractDetails
// 47 = can receive gamma, vega, theta, undPrice fields in TICK_OPTION_COMPUTATION
// 48 = can receive exemptCode in openOrder
// 49 = can receive hedgeType and hedgeParam in openOrder
// 50 = can receive optOutSmartRouting field in openOrder
// 51 = can receive smartComboRoutingParams in openOrder
// 52 = can receive deltaNeutralConId, deltaNeutralSettlingFirm, deltaNeutralClearingAccount and deltaNeutralClearingIntent in openOrder
// 53 = can receive orderRef in execution
// 54 = can receive scale order fields (PriceAdjustValue, PriceAdjustInterval, ProfitOffset, AutoReset,
//      InitPosition, InitFillQty and RandomPercent) in openOrder
// 55 = can receive orderComboLegs (price) in openOrder
// 56 = can receive trailingPercent in openOrder
// 57 = can receive commissionAndFeesReport message
// 58 = can receive CUSIP/ISIN/etc. in contractDescription/bondContractDescription
// 59 = can receive evRule, evMultiplier in contractDescription/bondContractDescription/executionDetails
//      can receive multiplier in executionDetails
// 60 = can receive deltaNeutralOpenClose, deltaNeutralShortSale, deltaNeutralShortSaleSlot
//      and deltaNeutralDesignatedLocation in openOrder
//      can receive position, positionEnd, accountSummary and accountSummaryEnd
// 61 = can receive multiplier in openOrder
//      can receive tradingClass in openOrder, updatePortfolio, execDetails and position
// 62 = can receive avgCost in position message
// 63 = can receive verifyMessageAPI, verifyCompleted, displayGroupList and displayGroupUpdated messages
// 64 = can receive solicited attrib in openOrder message
// 65 = can receive verifyAndAuthMessageAPI and verifyAndAuthCompleted messages
// 66 = can receive randomize size and randomize price order fields

const int CLIENT_VERSION    = 66;


// outgoing msg id's
const int REQ_MKT_DATA                  = 1;
const int CANCEL_MKT_DATA               = 2;
const int PLACE_ORDER                   = 3;
const int CANCEL_ORDER                  = 4;
const int REQ_OPEN_ORDERS               = 5;
const int REQ_ACCT_DATA                 = 6;
const int REQ_EXECUTIONS                = 7;
const int REQ_IDS                       = 8;
const int REQ_CONTRACT_DATA             = 9;
const int REQ_MKT_DEPTH                 = 10;
const int CANCEL_MKT_DEPTH              = 11;
const int REQ_NEWS_BULLETINS            = 12;
const int CANCEL_NEWS_BULLETINS         = 13;
const int SET_SERVER_LOGLEVEL           = 14;
const int REQ_AUTO_OPEN_ORDERS          = 15;
const int REQ_ALL_OPEN_ORDERS           = 16;
const int REQ_MANAGED_ACCTS             = 17;
const int REQ_FA                        = 18;
const int REPLACE_FA                    = 19;
const int REQ_HISTORICAL_DATA           = 20;
const int EXERCISE_OPTIONS              = 21;
const int REQ_SCANNER_SUBSCRIPTION      = 22;
const int CANCEL_SCANNER_SUBSCRIPTION   = 23;
const int REQ_SCANNER_PARAMETERS        = 24;
const int CANCEL_HISTORICAL_DATA        = 25;
const int REQ_CURRENT_TIME              = 49;
const int REQ_REAL_TIME_BARS            = 50;
const int CANCEL_REAL_TIME_BARS         = 51;
const int REQ_FUNDAMENTAL_DATA          = 52;
const int CANCEL_FUNDAMENTAL_DATA       = 53;
const int REQ_CALC_IMPLIED_VOLAT        = 54;
const int REQ_CALC_OPTION_PRICE         = 55;
const int CANCEL_CALC_IMPLIED_VOLAT     = 56;
const int CANCEL_CALC_OPTION_PRICE      = 57;
const int REQ_GLOBAL_CANCEL             = 58;
const int REQ_MARKET_DATA_TYPE          = 59;
const int REQ_POSITIONS                 = 61;
const int REQ_ACCOUNT_SUMMARY           = 62;
const int CANCEL_ACCOUNT_SUMMARY        = 63;
const int CANCEL_POSITIONS              = 64;
const int VERIFY_REQUEST                = 65;
const int VERIFY_MESSAGE                = 66;
const int QUERY_DISPLAY_GROUPS          = 67;
const int SUBSCRIBE_TO_GROUP_EVENTS     = 68;
const int UPDATE_DISPLAY_GROUP          = 69;
const int UNSUBSCRIBE_FROM_GROUP_EVENTS = 70;
const int START_API                     = 71;
const int VERIFY_AND_AUTH_REQUEST       = 72;
const int VERIFY_AND_AUTH_MESSAGE       = 73;
const int REQ_POSITIONS_MULTI           = 74;
const int CANCEL_POSITIONS_MULTI        = 75;
const int REQ_ACCOUNT_UPDATES_MULTI     = 76;
const int CANCEL_ACCOUNT_UPDATES_MULTI  = 77;
const int REQ_SEC_DEF_OPT_PARAMS        = 78;
const int REQ_SOFT_DOLLAR_TIERS         = 79;
const int REQ_FAMILY_CODES              = 80;
const int REQ_MATCHING_SYMBOLS          = 81;
const int REQ_MKT_DEPTH_EXCHANGES       = 82;
const int REQ_SMART_COMPONENTS          = 83;
const int REQ_NEWS_ARTICLE              = 84;
const int REQ_NEWS_PROVIDERS            = 85;
const int REQ_HISTORICAL_NEWS           = 86;
const int REQ_HEAD_TIMESTAMP            = 87;
const int REQ_HISTOGRAM_DATA            = 88;
const int CANCEL_HISTOGRAM_DATA         = 89;
const int CANCEL_HEAD_TIMESTAMP         = 90;
const int REQ_MARKET_RULE               = 91;
const int REQ_PNL                       = 92;
const int CANCEL_PNL                    = 93;
const int REQ_PNL_SINGLE                = 94;
const int CANCEL_PNL_SINGLE             = 95;
const int REQ_HISTORICAL_TICKS          = 96;
const int REQ_TICK_BY_TICK_DATA         = 97;
const int CANCEL_TICK_BY_TICK_DATA      = 98;
const int REQ_COMPLETED_ORDERS          = 99;
const int REQ_WSH_META_DATA				= 100;
const int CANCEL_WSH_META_DATA			= 101;
const int REQ_WSH_EVENT_DATA			= 102;
const int CANCEL_WSH_EVENT_DATA			= 103;
const int REQ_USER_INFO                 = 104;
const int REQ_CURRENT_TIME_IN_MILLIS    = 105;

// TWS New Bulletins constants
const int NEWS_MSG              = 1;    // standard IB news bulleting message
const int EXCHANGE_AVAIL_MSG    = 2;    // control message specifying that an exchange is available for trading
const int EXCHANGE_UNAVAIL_MSG  = 3;    // control message specifying that an exchange is unavailable for trading

const int PROTOBUF_MSG_ID = 200;
const std::map<int, int> PROTOBUF_MSG_IDS = { 
	{REQ_EXECUTIONS, MIN_SERVER_VER_PROTOBUF},
	{PLACE_ORDER, MIN_SERVER_VER_PROTOBUF_PLACE_ORDER},
	{CANCEL_ORDER, MIN_SERVER_VER_PROTOBUF_PLACE_ORDER},
	{REQ_GLOBAL_CANCEL, MIN_SERVER_VER_PROTOBUF_PLACE_ORDER},
};

} // namespace client_constants
} // namespace ibapi

struct Contract;
struct Order;
struct ExecutionFilter;
struct ScannerSubscription;
struct ETransport;

class EWrapper;


class TWSAPIDLLEXP EClient
{
    friend class ContractCondition;
    friend class ExecutionCondition;
    friend class OperatorCondition;
    friend class OrderCondition;
    friend class PriceCondition;

public:

	explicit EClient(EWrapper *ptr, ETransport *pTransport);
	virtual ~EClient();

	virtual void eDisconnect(bool resetState) = 0;

	int clientId() const { return m_clientId; }

	const std::string& optionalCapabilities() const;
	void setOptionalCapabilities(const std::string& optCapts);

	void setConnectOptions(const std::string& connectOptions);
	void disableUseV100Plus();
	bool usingV100Plus();
	bool useProtoBuf(int msgId);

protected:

	void eConnectBase();
	void eDisconnectBase();

public:

	enum ConnState {
		CS_DISCONNECTED,
		CS_CONNECTING,
		CS_CONNECTED,
		CS_REDIRECT
	};

	// connection state
	ConnState connState() const;
	bool isConnected() const;

	const std::string& host() const { return m_host; }
	int port() const { return m_port; }
	void validateInvalidSymbols(const std::string& host);

public:

	// access to protected variables
	EWrapper * getWrapper() const;
protected:
	void setClientId(int clientId);
	void setExtraAuth(bool extraAuth);
	void setHost(const std::string& host);
	void setPort(int port);

public:

	bool isInBufferEmpty() const;

	// override virtual funcs from EClient
	int serverVersion();
	std::string TwsConnectionTime();
	void reqMktData(TickerId id, const Contract& contract,
		const std::string& genericTicks, bool snapshot, bool regulatorySnaphsot, const TagValueListSPtr& mktDataOptions);
	void cancelMktData(TickerId id);
	void placeOrder(OrderId id, const Contract& contract, const Order& order);
	void cancelOrder(OrderId id, const OrderCancel& orderCancel);
	void reqOpenOrders();
	void reqAccountUpdates(bool subscribe, const std::string& acctCode);
	void reqExecutions(int reqId, const ExecutionFilter& filter);
	void reqIds(int numIds);
	void reqContractDetails(int reqId, const Contract& contract);
	void reqMktDepth(TickerId tickerId, const Contract& contract, int numRows, bool isSmartDepth, const TagValueListSPtr& mktDepthOptions);
	void cancelMktDepth(TickerId tickerId, bool isSmartDepth);
	void reqNewsBulletins(bool allMsgs);
	void cancelNewsBulletins();
	void setServerLogLevel(int level);
	void reqAutoOpenOrders(bool bAutoBind);
	void reqAllOpenOrders();
	void reqManagedAccts();
	void requestFA(faDataType pFaDataType);
	void replaceFA(int reqId, faDataType pFaDataType, const std::string& cxml);
	void reqHistoricalData(TickerId id, const Contract& contract,
		const std::string& endDateTime, const std::string& durationStr,
		const std::string&  barSizeSetting, const std::string& whatToShow,
		int useRTH, int formatDate, bool keepUpToDate, const TagValueListSPtr& chartOptions);
	void exerciseOptions(TickerId tickerId, const Contract& contract,
		int exerciseAction, int exerciseQuantity,
		const std::string& account, int override, const std::string& manualOrderTime, const std::string& customerAccount, bool professionalCustomer);
	void cancelHistoricalData(TickerId tickerId );
	void reqRealTimeBars(TickerId id, const Contract& contract, int barSize,
		const std::string& whatToShow, bool useRTH, const TagValueListSPtr& realTimeBarsOptions);
	void cancelRealTimeBars(TickerId tickerId );
	void cancelScannerSubscription(int tickerId);
	void reqScannerParameters();
	void reqScannerSubscription(int tickerId, const ScannerSubscription& subscription, const TagValueListSPtr& scannerSubscriptionOptions, const TagValueListSPtr& scannerSubscriptionFilterOptions);
	void reqCurrentTime();
	void reqFundamentalData(TickerId reqId, const Contract&, const std::string& reportType,
                                 //reserved for future use, must be blank
                                 const TagValueListSPtr& fundamentalDataOptions);
	void cancelFundamentalData(TickerId reqId);
	void calculateImpliedVolatility(TickerId reqId, const Contract& contract, double optionPrice, double underPrice, 
        //reserved for future use, must be blank
        const TagValueListSPtr& miscOptions);
	void calculateOptionPrice(TickerId reqId, const Contract& contract, double volatility, double underPrice, 
        //reserved for future use, must be blank
        const TagValueListSPtr& miscOptions);
	void cancelCalculateImpliedVolatility(TickerId reqId);
	void cancelCalculateOptionPrice(TickerId reqId);
	void reqGlobalCancel(const OrderCancel& orderCancel);
	void reqMarketDataType(int marketDataType);
	void reqPositions();
	void cancelPositions();
	void reqAccountSummary(int reqId, const std::string& groupName, const std::string& tags);
	void cancelAccountSummary(int reqId);
	void verifyRequest(const std::string& apiName, const std::string& apiVersion);
	void verifyMessage(const std::string& apiData);
	void verifyAndAuthRequest(const std::string& apiName, const std::string& apiVersion, const std::string& opaqueIsvKey);
	void verifyAndAuthMessage(const std::string& apiData, const std::string& xyzResponse);
	void queryDisplayGroups(int reqId);
	void subscribeToGroupEvents(int reqId, int groupId);
	void updateDisplayGroup(int reqId, const std::string& contractInfo);
	void unsubscribeFromGroupEvents(int reqId);
	void reqPositionsMulti(int reqId, const std::string& account, const std::string& modelCode);
	void cancelPositionsMulti(int reqId);
	void reqAccountUpdatesMulti(int reqId, const std::string& account, const std::string& modelCode, bool ledgerAndNLV);
	void cancelAccountUpdatesMulti(int reqId);
	void reqSecDefOptParams(int reqId, const std::string& underlyingSymbol, const std::string& futFopExchange, const std::string& underlyingSecType, int underlyingConId);
	void reqSoftDollarTiers(int reqId);
	void reqFamilyCodes();
	void reqMatchingSymbols(int reqId, const std::string& pattern);
	void reqMktDepthExchanges();
	void reqSmartComponents(int reqId, std::string bboExchange);
	void reqNewsProviders();
	void reqNewsArticle(int requestId, const std::string& providerCode, const std::string& articleId, const TagValueListSPtr& newsArticleOptions);
	void reqHistoricalNews(int requestId, int conId, const std::string& providerCodes, const std::string& startDateTime, const std::string& endDateTime, int totalResults, 
		const TagValueListSPtr& historicalNewsOptions);
	void reqHeadTimestamp(int tickerId, const Contract &contract, const std::string& whatToShow, int useRTH, int formatDate);
	void cancelHeadTimestamp(int tickerId);
	void reqHistogramData(int reqId, const Contract &contract, bool useRTH, const std::string& timePeriod);
	void cancelHistogramData(int reqId);
	void reqMarketRule(int marketRuleId);

	void reqPnL(int reqId, const std::string& account, const std::string& modelCode);
	void cancelPnL(int reqId);
	void reqPnLSingle(int reqId, const std::string& account, const std::string& modelCode, int conId);
	void cancelPnLSingle(int reqId);
    void reqHistoricalTicks(int reqId, const Contract &contract, const std::string& startDateTime,
            const std::string& endDateTime, int numberOfTicks, const std::string& whatToShow, int useRth, bool ignoreSize, const TagValueListSPtr& miscOptions);
    void reqTickByTickData(int reqId, const Contract &contract, const std::string& tickType, int numberOfTicks, bool ignoreSize);
    void cancelTickByTickData(int reqId);
    void reqCompletedOrders(bool apiOnly);
	void reqWshMetaData(int reqId);
	void reqWshEventData(int reqId, const WshEventData &wshEventData);
	void cancelWshMetaData(int reqId);
	void cancelWshEventData(int reqid);
    void reqUserInfo(int reqId);
	void reqCurrentTimeInMillis();

public:
	void reqExecutionsProtoBuf(const protobuf::ExecutionRequest& executionRequestProto);
	void placeOrderProtoBuf(const protobuf::PlaceOrderRequest& placeOrderRequestProto);
	void cancelOrderProtoBuf(const protobuf::CancelOrderRequest& cancelOrderRequestProto);
	void reqGlobalCancelProtoBuf(const protobuf::GlobalCancelRequest & globalCancelRequestProto);

private:

	virtual int receive(char* buf, size_t sz) = 0;
	static bool isAsciiPrintable(const std::string& s);

protected:

	virtual void prepareBufferImpl(std::ostream&) const = 0;
	virtual void prepareBuffer(std::ostream&) const = 0;
	virtual bool closeAndSend(std::string msg, unsigned offset = 0) = 0;
	virtual int bufferedSend(const std::string& msg);


   	// encoders
	template<class T> static void EncodeField(std::ostream&, T);

public:
	void startApi();



    void EncodeContract(std::ostream& os, const Contract &contract);
    void EncodeTagValueList(std::ostream& os, const TagValueListSPtr &tagValueList);
	void EncodeMsgId(std::ostream& os, int msgId);

	// "max" encoders
	static void EncodeFieldMax(std::ostream& os, int);
	static void EncodeFieldMax(std::ostream& os, double);

	// "raw" encoders
	static void EncodeRawInt(std::ostream& buf, int intValue);

	// socket state
private:
	virtual bool isSocketOK() const = 0;

protected:

	bool isConnecting() const;
	int sendConnectRequest();
    bool extraAuth();

protected:

	EWrapper *m_pEWrapper;
	std::unique_ptr<ETransport> m_transport;

private:
	std::string m_host;
	int m_port;

	int m_clientId;

	ConnState m_connState;
	bool m_extraAuth;

protected:
	int m_serverVersion;
	std::string m_TwsTime;

private:
	std::string m_optionalCapabilities;

	std::string m_connectOptions;

protected:
	bool m_useV100Plus;

};

template<> void EClient::EncodeField<bool>(std::ostream& os, bool);
template<> void EClient::EncodeField<double>(std::ostream& os, double);
template<> void EClient::EncodeField<Decimal>(std::ostream& os, Decimal);
template<> void EClient::EncodeField<std::string> (std::ostream& os, std::string);

#define ENCODE_CONTRACT(x) EClient::EncodeContract(msg, x);
#define ENCODE_TAGVALUELIST(x) EClient::EncodeTagValueList(msg, x);
#define ENCODE_FIELD(x) EClient::EncodeField(msg, x);
#define ENCODE_FIELD_MAX(x) EClient::EncodeFieldMax(msg, x);
#define ENCODE_MSG_ID(x) EClient::EncodeMsgId(msg, x);
#define ENCODE_RAW_INT(x) EClient::EncodeRawInt(msg, x);


#endif
