#ifndef ibposixclient_h__INCLUDED
#define ibposixclient_h__INCLUDED

#include "import/client/EWrapper.h"
#include "import/client/EReaderOSSignal.h"
#include "import/client/EReader.h"

#include <string>
#include <memory>

#include <queue>
#include <utility>
#include "import/client/Contract.h"
#include "import/client/Order.h"
#include "import/client/OrderState.h"
#include "import/client/Execution.h"
#include "import/client/CommissionReport.h"
#include "import/libjson/_internal/Source/JSONNode.h"

class EClientSocket;

struct ExecutionFilter;
struct ScannerSubscription;

class IbPosixClient : public EWrapper {
public:
    IbPosixClient();
    ~IbPosixClient();

    void processMessages();

    // Events
    bool connect( const char * host, unsigned int port, int clientId = 0,
                  bool extraAuth = false );
    void disconnect() const;
    bool isConnected() const;

    // TODO implement isInBufferEmpty
    bool isInBufferEmpty();

    // TODO why is server version deprecated?
    // int serverVersion();
    std::string TwsConnectionTime();

    // TODO check all function signatures
    void reqMktData( TickerId id, const Contract &contract,
                     const std::string &genericTicks, bool snapshot,
                     const TagValueListSPtr& mktDataOptions );
    void cancelMktData( TickerId id );
    void placeOrder( OrderId id, const Contract &contract,
                     const Order &order );
    void cancelOrder( OrderId id );
    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, const TagValueListSPtr& mktDepthOptions );
    void cancelMktDepth( TickerId tickerId );
    void reqNewsBulletins( bool allMsgs );
    void cancelNewsBulletins();
    void setServerLogLevel( int level );
    void reqAutoOpenOrders( bool bAutoBind );
    void reqAllOpenOrders();
    void reqManagedAccts();
    void requestFA( faDataType pFaDataType );
    void replaceFA( 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,
                            const TagValueListSPtr& chartOptions );
    void exerciseOptions( TickerId tickerId, const Contract &contract,
                          int exerciseAction, int exerciseQuantity,
                          const std::string &account, int override );
    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 );
    void reqCurrentTime();
    void reqFundamentalData( TickerId reqId, const Contract &contract,
                             const std::string &reportType );
    void cancelFundamentalData( TickerId reqId );
    void calculateImpliedVolatility( TickerId reqId, const Contract &contract,
                                     double optionPrice, double underPrice );
    void calculateOptionPrice( TickerId reqId, const Contract &contract,
                               double volatility, double underPrice );
    void cancelCalculateImpliedVolatility( TickerId reqId );
    void cancelCalculateOptionPrice( TickerId reqId );
    void reqGlobalCancel();
    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 queryDisplayGroups( int reqId);
    void subscribeToGroupEvents( int reqId, int groupId);
    void updateDisplayGroup( int reqId, const std::string& contractInfo);
    void unsubscribeFromGroupEvents( int reqId);

    // TODO implement
    void reqPositionsMulti( int reqId, const std::string& account,
                            const std::string& modelCode);
    void cancelPositionsMulti( int reqId);
    void reqAccountUpdatessMulti( 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);

    // Events
    void tickPrice( TickerId tickerId, TickType field, double price,
                    int canAutoExecute );
    void tickSize( TickerId tickerId, TickType field, int size );
    void tickOptionComputation( TickerId tickerId, TickType tickType,
                                double impliedVol, double delta,
                                double optPrice, double pvDividend,
                                double gamma, double vega, double theta,
                                double undPrice );
    void tickGeneric( TickerId tickerId, TickType tickType, double value );
    void tickString( TickerId tickerId, TickType tickType,
                     const std::string& value );
    void tickEFP( TickerId tickerId, TickType tickType, double basisPoints,
                  const std::string& formattedBasisPoints, double totalDividends,
                  int holdDays, const std::string& futureExpiry,
                  double dividendImpact, double dividendsToExpiry );
    void orderStatus( OrderId orderId, const std::string& status,
                      double filled, double remaining,
                      double avgFillPrice, int permId, int parentId,
	                    double lastFillPrice, int clientId,
                      const std::string& whyHeld);
    void openOrder( OrderId orderId, const Contract&, const Order&,
                    const OrderState& );
    void openOrderEnd();
    void winError( const std::string &str, int lastError );
    void connectionClosed();
    void updateAccountValue( const std::string& key, const std::string& val,
                             const std::string& currency,
                             const std::string& accountName );
    void updatePortfolio( const Contract& contract, double position,
                          double marketPrice, double marketValue,
                          double averageCost, double unrealizedPNL,
                          double realizedPNL, const std::string& accountName);
    void updateAccountTime( const std::string& timeStamp );
    void accountDownloadEnd( const std::string& accountName );
    void nextValidId( OrderId orderId );
    void contractDetails( int reqId, const ContractDetails& contractDetails );
    void bondContractDetails( int reqId,
                              const ContractDetails& contractDetails );
    void contractDetailsEnd( int reqId );
    void execDetails( int reqId, const Contract& contract, 
                      const Execution& execution );
    void execDetailsEnd( int reqId );
    void error( const int id, const int errorCode, const std::string errorString );
    void updateMktDepth( TickerId id, int position, int operation, int side,
                         double price, int size );
    void updateMktDepthL2( TickerId id, int position, std::string marketMaker,
                           int operation, int side, double price, int size );
    void updateNewsBulletin( int msgId, int msgType,
                             const std::string& newsMessage,
                             const std::string& originExch );
    void managedAccounts( const std::string& accountsList );
    void receiveFA( faDataType pFaDataType, const std::string& cxml );
    void historicalData( TickerId reqId, const std::string& date, double open,
                         double high, double low, double close, int volume,
                         int barCount, double WAP, int hasGaps );
    void scannerParameters( const std::string &xml );
    void scannerData( int reqId, int rank,
                      const ContractDetails &contractDetails,
                      const std::string &distance, const std::string &benchmark,
                      const std::string &projection, const std::string &legsStr );
    void scannerDataEnd( int reqId );
    void realtimeBar( TickerId reqId, long time, double open, double high,
                      double low, double close, long volume, double wap,
                      int count );
    void currentTime( long time );
    void fundamentalData( TickerId reqId, const std::string& data );
    void deltaNeutralValidation( int reqId, const UnderComp& underComp );
    void tickSnapshotEnd( int reqId );
    void marketDataType( TickerId reqId, int marketDataType );
    void commissionReport( const CommissionReport& commissionReport );
    void position( const std::string& account, const Contract& contract,
                   double position, double avgCost);
    void positionEnd();
    void accountSummary( int reqId, const std::string& account,
                         const std::string& tag, const std::string& value,
                         const std::string& curency );
    void accountSummaryEnd( int reqId );
    void verifyMessageAPI( const std::string& apiData);
    void verifyCompleted( bool isSuccessful, const std::string& errorText);
    void displayGroupList( int reqId, const std::string& groups);
    void displayGroupUpdated( int reqId, const std::string& contractInfo);

    void verifyAndAuthMessageAPI( const std::string& apiData,
                                  const std::string& xyzChallange );
    void verifyAndAuthCompleted( bool isSuccessful,
                                 const std::string& errorText);
    void connectAck();
    void positionMulti( int reqId, const std::string& account,
                        const std::string& modelCode, const Contract& contract,
                        double pos, double avgCost );
    void positionMultiEnd( int reqId );
    void accountUpdateMulti( int reqId, const std::string& account,
                             const std::string& modelCode,
                             const std::string& key, const std::string& value,
                             const std::string& currency );
    void accountUpdateMultiEnd( int reqId );
    void securityDefinitionOptionalParameter( int reqId,
                                              const std::string& exchange,
                                              int underlyingConId,
                                              const std::string& tradingClass,
                                              const std::string& multiplier,
                                              std::set<std::string> expirations,
                                              std::set<double> strikes );
    void securityDefinitionOptionalParameterEnd( int reqId );
    void softDollarTiers( int reqId, const std::vector<SoftDollarTier> &tiers );

// accessors
    JSONNode getInboundMsg();

private:
	  //! [socket_declare]
    EReaderOSSignal m_osSignal;
    EClientSocket * const m_pClient;
    //! [socket_declare]
    time_t m_sleepDeadline;

    OrderId m_orderId;
    EReader *m_pReader;
    bool m_extraAuth;

///// node.js accessible
    std::queue< JSONNode > m_inboundMsgs;

//// helper methods
    JSONNode jsonifyContract(std::string name, const Contract& contract);
    JSONNode jsonifyUnderComp(const UnderComp& underComp);
    JSONNode jsonifyContractDetails(const ContractDetails& cd);
    JSONNode jsonifyOrder(const Order& order);
    JSONNode jsonifyOrderState(const OrderState& ostate);
    JSONNode jsonifyExecution(const Execution& execution);
    JSONNode jsonifyCommissionReport(const CommissionReport& cr);
    JSONNode jsonifySoftDollarTier(const SoftDollarTier& sdt);
};

#endif
