#include <string>
#include "sessionstatus.h"
#include "responselistener.h"
#include <iostream>
#include "stdafx.h"

class OrderCreationParam
{
public:
    std::string mOfferID;
    std::string mAccountID;
    std::string mBuySell;
    int mBaseAmount;

    OrderCreationParam()
    {
        mOfferID = "";
        mAccountID = "";
        mBuySell = "";
        mBaseAmount = 0;
    }
};

OrderCreationParam *PrepareOrderParams(IO2GSession *session, std::string instrument);
bool Login(IO2GSession *session, SessionStatusListener *statusListener, std::string user,
           std::string password, std::string url, std::string connection);
bool OpenPosition(IO2GSession *session, OrderCreationParam *orderParam, ResponseListener *responseListener);
bool GetLastOrderUpdateByOrderID(IO2GSession *session, ResponseListener *responseListener, std::string sOrderID, std::string sAccountName);
std::string GetAccountName(IO2GSession *session);
void Logout(IO2GSession *session, SessionStatusListener *listener);

int main(int argc, char *argv[])
{
    std::string user = "";
    std::string password = "";
    std::string url = "";
    std::string connection = "";
    std::string subsession = "";
    std::string pin = "";

    std::string instrument = "EUR/USD";

    if (argc < 3)
    {
        printf("usage: GetLastOrderUpdate.exe user password url connection [subsession] [pin]\n");
        return -1;
    }

    user = argv[1];
    password = argv[2];
    if (argc >= 4)
        url = argv[3];
    else
        url = "http://www.fxcorporate.com/Hosts.jsp";
    if (argc >= 5)
        connection = argv[4];
    else
        connection = "Demo";
    if (argc >= 6)
        subsession = argv[5];
    if (argc >= 7)
        pin = argv[6];

    IO2GSession *session = CO2GTransport::createSession();
    SessionStatusListener *listener = new SessionStatusListener(session, true, subsession.c_str(), pin.c_str());
    session->subscribeSessionStatus(listener);
    listener->resetError();

    bool bConnected = Login(session, listener, user, password, url, connection);

    if (bConnected)
    {
        OrderCreationParam *orderParam = PrepareOrderParams(session, instrument);
        orderParam->mBuySell = O2G2::Buy;
        O2G2Ptr<ResponseListener> responseListener = new ResponseListener(session);
        session->subscribeResponse(responseListener);

        OpenPosition(session, orderParam, responseListener);

        if (responseListener->getRequestSuccess())
        {
            std::string sAccountName = GetAccountName(session);
            std::string sOrderID = responseListener->getOrderID();
            GetLastOrderUpdateByOrderID(session, responseListener, sOrderID, sAccountName);
        }

        session->unsubscribeResponse(responseListener);

        Logout(session, listener);
    }

    session->unsubscribeSessionStatus(listener);
    listener->release();
    session->release();
}

bool Login(IO2GSession *session, SessionStatusListener *statusListener, std::string user,
           std::string password, std::string url, std::string connection)
{
    session->login(user.c_str(), password.c_str(), url.c_str(), connection.c_str());

    bool bConnected = false;

    while (true)
    {
        uni::WaitForSingleObject(statusListener->getEvent(), 250);
        if (statusListener->getStatusCode() == IO2GSessionStatus::Connected)
        {
            bConnected = true;
            break;
        }
        if (statusListener->wasError())
        {
            printf("Connection failed: %s", statusListener->getError());
            break;
        }
    }
    return bConnected;
}

bool OpenPosition(IO2GSession *session, OrderCreationParam *orderParam, ResponseListener *responseListener)
{
    O2G2Ptr<IO2GRequestFactory> requestFactory = session->getRequestFactory();
    O2G2Ptr<IO2GValueMap> valuemap = requestFactory->createValueMap();

    valuemap->setString(Command, O2G2::Commands::CreateOrder);
    valuemap->setString(OrderType, O2G2::Order::TrueMarketOpen);
    valuemap->setString(AccountID, orderParam->mAccountID.c_str());
    valuemap->setString(OfferID, orderParam->mOfferID.c_str());
    valuemap->setString(BuySell, orderParam->mBuySell.c_str());
    valuemap->setInt(Amount, orderParam->mBaseAmount);

    O2G2Ptr<IO2GRequest> request = requestFactory->createOrderRequest(valuemap);

    responseListener->setRequest(request->getRequestID());

    session->sendRequest(request);
    HANDLE aEvents[2];
    aEvents[0] = responseListener->getResponseEvent();
    aEvents[1] = responseListener->getUpdateEvent();
    long rc = uni::WaitForMultipleObjects(2, aEvents, TRUE, INFINITE);
    return (rc != WAIT_TIMEOUT);
}

OrderCreationParam *PrepareOrderParams(IO2GSession *session, std::string instrument)
{
    OrderCreationParam *orderParams = new OrderCreationParam();

    O2G2Ptr<IO2GLoginRules> loginRules = session->getLoginRules();
    O2G2Ptr<IO2GResponseReaderFactory> factory = session->getResponseReaderFactory();

    // Gets first account from login.
    O2G2Ptr<IO2GResponse> accountsResponse = loginRules->getTableRefeshResponse(Accounts);
    O2G2Ptr<IO2GAccountsTableResponseReader> accountsReader = factory->createAccountsTableReader(accountsResponse);
    O2G2Ptr<IO2GAccountRow> account = accountsReader->getRow(0);

    orderParams->mAccountID = account->getAccountID();
    orderParams->mBaseAmount = account->getBaseUnitSize();

    // Find offer ID
    O2G2Ptr<IO2GResponse> offerResponse = loginRules->getTableRefeshResponse(Offers);
    O2G2Ptr<IO2GOffersTableResponseReader> offersReader = factory->createOffersTableReader(offerResponse);
    for (int i = 0; i < offersReader->size(); i++)
    {
        O2G2Ptr<IO2GOfferRow> offer = offersReader->getRow(i);
        if (instrument == offer->getInstrument())
        {
            orderParams->mOfferID = offer->getOfferID();
            break;
        }
    }
    return orderParams;
}

bool GetLastOrderUpdateByOrderID(IO2GSession *session, ResponseListener *responseListener, std::string sOrderID, std::string sAccountName)
{
    O2G2Ptr<IO2GRequestFactory> requestFactory = session->getRequestFactory();
    O2G2Ptr<IO2GValueMap> valuemap = requestFactory->createValueMap();

    valuemap->setString(Command, O2G2::Commands::GetLastOrderUpdate);
    valuemap->setString(AccountName, sAccountName.c_str());
    valuemap->setString(Key, O2G2::KeyType::OrderID);
    valuemap->setString(Id, sOrderID.c_str());

    O2G2Ptr<IO2GRequest> request = requestFactory->createOrderRequest(valuemap);

    responseListener->setRequest(request->getRequestID());
    session->sendRequest(request);

    long rc = uni::WaitForSingleObject(responseListener->getResponseEvent(), INFINITE);
    return (rc == WAIT_OBJECT_0);
}

std::string GetAccountName(IO2GSession *session)
{
    std::string sAccountName = "";

    O2G2Ptr<IO2GLoginRules> loginRules = session->getLoginRules();
    O2G2Ptr<IO2GResponseReaderFactory> factory = session->getResponseReaderFactory();

    // Gets first account from login.
    O2G2Ptr<IO2GResponse> accountsResponse = loginRules->getTableRefeshResponse(Accounts);
    O2G2Ptr<IO2GAccountsTableResponseReader> accountsReader = factory->createAccountsTableReader(accountsResponse);
    O2G2Ptr<IO2GAccountRow> account = accountsReader->getRow(0);

    sAccountName = account->getAccountName();

    return sAccountName;
}

void Logout(IO2GSession *session, SessionStatusListener *listener)
{
    session->logout();
    while (true)
    {
        uni::WaitForSingleObject(listener->getEvent(), 250);
        if (listener->getStatusCode() == IO2GSessionStatus::Disconnected)
            break;
    }
}
