// ==========================================================================
// Author:  Yee Hsu
// Date:    9/30/2013
// File:    FixString.cpp
//
// Desc:    An implementation of the FIX protocol. The FixString class allows
//      you to create a FIX string that is exchange compatible according
//      to the standards as describe in the FIX protocol. FIX is use to
//      communicate between institutions, brokers/dealers, and exchange
//      for financial interchange to help facilitate market orders. This
//      particular implementation is very fast and is suitable to plug
//      into any FIX engine for Ultra-Low Latency Direct-to-Market Orders
// ==========================================================================

#include "StdAfx.h"
#include "FixString.h"

namespace FixEngine
{
    FixString::FixString()
        : m_fixvec(), m_fixmap()
    {
        memset(m_fixstr, 0, sizeof(m_fixstr));
    }

    FixString::~FixString(void)
    {}

    void FixString::BeginFix( const String& msgtype, const String& senderCompId, const String& targetCompId, const String& applVerId )
    {
        m_fixvec.clear();
        m_fixmap.clear();

        SetTag(TAG_BEGIN_STRING, FIX_VERSION_5_0);
        SetTag(TAG_BODY_LENGTH, 0);
        SetTag(TAG_MESSAGE_TYPE, msgtype);
        SetTag(TAG_SENDER_COMP_ID, senderCompId);
        SetTag(TAG_TARGET_COMP_ID, targetCompId);

        if (!applVerId.empty())
            SetTag(TAG_APPLICATION_VERSION_ID, applVerId);
    }

    void FixString::SetTag( FixTag tag, const String& value )
    {
        if (m_fixmap.find(tag) == m_fixmap.end())
        {
            m_fixmap.insert(std::make_pair(tag, value));
            m_fixvec.push_back(tag);
        }
        else
        {
            m_fixmap[tag] = value;
        }
    }

    void FixString::SetTag( FixTag tag, int value )
    {
        char buffer[32] = {0};
        sprintf(buffer, "%d", value);
        SetTag(tag, buffer);
    }

    void FixString::SetTag( FixTag tag, float value )
    {
        char buffer[32] = {0};
        sprintf(buffer, "%f", value);
        SetTag(tag, buffer);
    }

    void FixString::EndFix()
    {
        int offset = 0;

        // calculate and set body length
        // 2nd element is always tag/value pair after TAG_BODY_LENGTH
        // assume TAG_CHECKSUM has not been added into the struct, so we can sum up the length
        char body[FIX_MAX_STR_LEN] = {0};

        for (size_t i = 2; i < m_fixvec.size(); i++)
        {
            String str;
            FixTag tag = m_fixvec[i];

            if (GetFixValuebyTag(tag, str))
                offset += sprintf(body + offset, "%d=%s%c", tag, str.c_str(), FIX_SOH_DELIMITER);    
        }
        int len = strlen(body);
        SetTag(TAG_BODY_LENGTH, len);

        // combine head + body with the calculated length
        memset(m_fixstr, 0, sizeof(m_fixstr));
        offset = sprintf(m_fixstr, "8=%s%c9=%d%c%s", 
            GetFixValuebyTagFast(TAG_BEGIN_STRING), FIX_SOH_DELIMITER,
            len, FIX_SOH_DELIMITER, body);
        long buflen = strlen(m_fixstr);

        // calculate and set checksum
        unsigned int cks = 0;

        for( int idx = 0; idx < buflen; cks += (unsigned int) m_fixstr[idx++] );    
        sprintf( m_fixstr + offset, "10=%03d%c", (unsigned int)( cks % 256 ), FIX_SOH_DELIMITER);
        SetTag(TAG_CHECKSUM, (int)( cks % 256 ));
    }

    bool FixString::GetFixValuebyTag( FixTag tag, String& value )
    {
        value.clear();

        if (m_fixmap.find(tag) != m_fixmap.end())
        {
            value = m_fixmap[tag];
            return true;
        }
        return false;
    }

    const char* FixString::GetFixValuebyTagFast( FixTag tag )
    {
        return m_fixmap[tag].c_str();
    }
}

// Belongs in another file...

int _tmain(int argc, _TCHAR* argv[])
{
    FixString fs;
    /*
    // 8=FIX.4.2|9=42|35=0|49=A|56=B|34=12|52=20100304-07:59:30|10=185|
    fs.BeginFix("0", "A", "B", "");
    fs.SetTag(TAG_MESSAGE_SEQ_NUM,    12);
    fs.SetTag(TAG_SENDING_TIME,      "20100304-07:59:30");
    fs.EndFix();
    */
    // 8=FIX.4.2|9=65|35=A|49=SERVER|56=CLIENT|34=177|52=20090107-18:15:16|98=0|108=30|10=062|
    fs.BeginFix("A", "SERVER", "CLIENT", "");
    fs.SetTag(TAG_MESSAGE_SEQ_NUM,    177);
    fs.SetTag(TAG_SENDING_TIME,      "20090107-18:15:16");
    fs.SetTag(TAG_ENCRYPTION_METHOD,  0);
    fs.SetTag(TAG_HEARTBEAT_INTERVAL,  30);
    fs.EndFix();
    /*
    // 8=FIX.4.2 | 9=178 | 35=8 | 49=PHLX | 56=PERS | 52=20071123-05:30:00.000 | 11=ATOMNOCCC9990900 | 20=3 | 150=E | 39=E | 55=MSFT | 167=CS | 54=1 | 38=15 | 40=2 | 44=15 | 58=PHLX EQUITY TESTING | 59=0 | 47=C | 32=0 | 31=0 | 151=15 | 14=0 | 6=0 | 10=128 |
    fs.BeginFix("8", "PHLX", "PERS", "");
    fs.SetTag(TAG_SENDING_TIME,          "20071123-05:30:00.000");
    fs.SetTag(TAG_CLIENT_ORDERID,        "ATOMNOCCC9990900");
    fs.SetTag(TAG_EXECUTION_TRANSACTION_TYPE,  3);
    fs.SetTag(TAG_EXECUTION_TYPE,        "E");
    fs.SetTag(TAG_ORDER_STATUS,          "E");
    fs.SetTag(TAG_SYMBOL,            "MSFT");
    fs.SetTag(TAG_SECURITY_TYPE,        "CS");
    fs.SetTag(TAG_SIDE,              1);
    fs.SetTag(TAG_ORDER_QTY,          15);
    fs.SetTag(TAG_ORDER_TYPE,          2);
    fs.SetTag(TAG_PRICE,            15);
    fs.SetTag(TAG_FREE_TEXT,          "PHLX EQUITY TESTING");
    fs.SetTag(TAG_TIME_IN_FORCE,        0);
    fs.SetTag(TAG_RULE_80A,            "C");
    fs.SetTag(TAG_LAST_QTY,            0);
    fs.SetTag(TAG_LAST_PRICE,          0);
    fs.SetTag(TAG_LEAVES_QTY,          15);
    fs.SetTag(TAG_CUMMULATIVE_QTY,        0);
    fs.SetTag(TAG_AVERAGE_PRICE,        0);
    fs.EndFix();
    */
    // In Linux, you can replace SOH char with pipe, display using
    // $ ./a.out | tr '\1' '|'
    //printf("%s", fs.GetFixStr());

    // printf in human readable format
    FixVec& fv = fs.GetFixVec();

    printf("FixString:\n");
    for (FixVec::iterator iter = fv.begin(); iter != fv.end(); ++iter)
        printf("%5d : [%s]\n", *iter, fs.GetFixValuebyTagFast(*iter));

    return 0;
}