// Copyright (c) 2010-2012, MarketFactory Inc. All rights reserved.
#include <API/Clients/CPP/AbstractMFHandler.h>
#include <API/Clients/CPP/MFClient.h>
#include <API/Clients/CPP/MFInit.h>
#include <API/Clients/CPP/Proto/APIAll.h>

#include <cstdlib>

#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
#include <unistd.h>
#endif

#include <iostream>
#include <map>

using namespace MFAPI;
namespace {

#define TEST_LOG(X) std::cout << X << std::endl;

// A very basic sample handler that subscribes to some market data and prints
// the updates.

class Handler : public AbstractMFHandler {
 private:
  typedef std::map<int, int> FeedSeqIDMap;
  // The last received sequenceID from a given feed.
  // It would be best to persist this data on disk.
  FeedSeqIDMap _feedSeqIDMap;

  // Update the last sequence ID for a given feed.
  void updateLastSequenceID(int feedID, int sequenceID) {
    FeedSeqIDMap::iterator it = _feedSeqIDMap.find(feedID);
    // Update only if this message has bigger sequenceID than the last
    // sequenceID.
    if (it == _feedSeqIDMap.end() || sequenceID > it->second)
      _feedSeqIDMap[feedID] = sequenceID;
  }

 public:
  virtual void onStart(MFClient& client) {
    TEST_LOG("onStart() callback");

    // Without a subscription orders will be immediately rejected
    const APIFeed* feed = client.getFeedByName("HOTSPOT");

    if (!feed) {
      TEST_LOG("Could not get HOTSPOT feed");
      client.stop();
      return;
    }

    // Subscribe to the corresponding order flow feed.
    client.subscribeOF(feed->feedID);
  }

  virtual void onStop(MFClient& client) {}

  virtual void onDisconnect(MFClient& client) {}

  virtual void onConnect(MFClient& client) {}

  virtual void onHeartbeat(const Heartbeat& heartbeat, MFClient& client) {
    static unsigned int numActiveFeeds = 0;
    static int numHeartbeats = 0;
    if (numHeartbeats++ % 1000 == 0) {
      TEST_LOG("HEARTBEATS = " << numHeartbeats);
    }
    if (numActiveFeeds != heartbeat.activeFeeds.size()) {
      numActiveFeeds = heartbeat.activeFeeds.size();
      TEST_LOG("HEARTBEAT = " << heartbeat);
    }
  }

  virtual void onSubscriptionEvent(const SubscriptionEvent& subscriptionEvent,
                                   MFClient& client) {
    TEST_LOG("onSubscriptionEvent()");
    TEST_LOG(subscriptionEvent);

    if (subscriptionEvent.status == SubscriptionStatus::ENABLED) {
      const APIFeed* hotspot = client.getFeedByName("HOTSPOT");
      // assert subscriptionEvent.sub.feedID == hotspot.feedID;

      // Check for missed messages.
      int lastSeqID = client.getSequenceID(hotspot->feedID);
      FeedSeqIDMap::iterator it = _feedSeqIDMap.find(hotspot->feedID);
      if (it != _feedSeqIDMap.end() && it->second < lastSeqID) {
        TEST_LOG("Missed messages. Requesting replay..");
        // Send replay request to the server.
        client.replay(hotspot->feedID, it->second + 1, -1);
      } else {
        const APIMarket* m = client.getMarketByName(*hotspot, "USD/JPY");
        if (m) {
#if MF_VERSION_AS_NUMBER == 3130
          MFFloat amount("10000000");
          MFFloat price("117.82");
          Parameters parameters;
          parameters.push_back(KeyValue("OnTerm", "JPY"));
          parameters.push_back(KeyValue("MinAmount", "1000000"));
#else
          MFFloat amount("100000");
          MFFloat price("78.123");
#endif
          std::pair<APIResult::Type, long> res = client.submitOrder(
              "TestOrder", hotspot->feedID, m->marketID, Side::BID, amount,
#if MF_VERSION_AS_NUMBER == 3130
              price, MFFloat::NA, MFFloat::NA, OrderType::LIMIT,
              TimeInForce::GTC, &parameters);

#else
              price, OrderType::LIMIT, TimeInForce::GTC);
#endif
          TEST_LOG("submitted order ID:" << res.second);
        }
      }
    }
  }

  virtual void onMarketData(const MarketView& marketData, MFClient& client) {
    TEST_LOG("MD = " << marketData);
  }

  virtual void onEventReport(const EventReport& eventReport, MFClient& client) {
    TEST_LOG("EV = " << eventReport);
  }

  virtual void onDateRollover(const DateRollover& dateRollover,
                              MFClient& client) {
    TEST_LOG("DR = " << dateRollover);
  }

  virtual void onBatchComplete(int batchDefID, MFClient& client) {
    TEST_LOG("BA = " << batchDefID);
  }

  virtual void onOrderReceived(const OrderReceived& orderReceived,
                               MFClient& client) {
    TEST_LOG("OR = " << orderReceived);
    updateLastSequenceID(orderReceived.feedID, orderReceived.sequenceID);
  }

  virtual void onOrderRejected(const OrderRejected& orderRejected,
                               MFClient& client) {
    TEST_LOG("OR = " << orderRejected);
    updateLastSequenceID(orderRejected.feedID, orderRejected.sequenceID);
  }

  virtual void onOrderSubmitted(const OrderSubmitted& orderSubmitted,
                                MFClient& client) {
    TEST_LOG("OR = " << orderSubmitted);
    updateLastSequenceID(orderSubmitted.feedID, orderSubmitted.sequenceID);
  }

  virtual void onOrderCanceled(const OrderCanceled& orderCanceled,
                               MFClient& client) {
    TEST_LOG("OR = " << orderCanceled);
    updateLastSequenceID(orderCanceled.feedID, orderCanceled.sequenceID);
  }

  virtual void onOrderCancelRejected(
      const OrderCancelRejected& orderCancelRejected, MFClient& client) {
    TEST_LOG("OR = " << orderCancelRejected);
    updateLastSequenceID(orderCancelRejected.feedID,
                         orderCancelRejected.sequenceID);
  }

  virtual void onTradeCapture(const TradeCapture& tradeCapture,
                              MFClient& client) {
    TEST_LOG("TC = " << tradeCapture);
    updateLastSequenceID(tradeCapture.feedID, tradeCapture.sequenceID);
  }

  virtual void onOrderDone(const OrderDone& orderDone, MFClient& client) {
    TEST_LOG("OR = " << orderDone);
    updateLastSequenceID(orderDone.feedID, orderDone.sequenceID);
  }

  virtual void onDropCopy(const DropCopy& dropCopy, MFClient& client) {
    TEST_LOG("DC = " << dropCopy);
    updateLastSequenceID(dropCopy.feedID, dropCopy.sequenceID);
  }
};
}

int main(int argc, char** argv) {
  // Initialize the library.
  MFInit::initialize();

  try {

    const char* hostname = "demo.marketfactory.com";
#if MF_VERSION_AS_NUMBER == 3130
    int port = 10033;
#else
    int port = 80;
#endif

    if (argc >= 2) {
      hostname = argv[1];
    }

    if (argc >= 3) {
      port = atoi(argv[2]);
    }

    TEST_LOG("host: " << hostname);
    TEST_LOG("port: " << port);

    // Connect a client.
    MFClient* client = MFClient::open(
        "CHANGE_TO_YOUR_MARKET_FACTORY_PORTAL_USERNAME",
        "CHANGE_TO_YOUR_MARKET_FACTORY_PORTAL_PASSWORD", hostname, port);
    if (client == 0) {
      std::cerr << "Could not connect." << std::endl;
      return 1;
    }

    // Create a handler instance.
    Handler handler;

    // Dispatch the messages on it (returns when stop() is called).
    APIResult::Type status = client->run(&handler);

#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
    ::sleep(10);
#else
    sleep(10);
#endif

    // Reconnect.
    status = client->connect();
    status = client->run(&handler);

    TEST_LOG("status = " << status);
  }
  catch (const std::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl;
  }

  // Finalize the library.
  MFInit::finalize();
  return 0;
}
