/*
*	©Copyright 2002,2003, Microsoft Corporation
*	Microsoft Confidential.
*	Written by Sergey Vasilevskiy, 2002-2003.
*/

/*
*	Market Feed database class that uses stored procedures for individual frames.
*/

#include "stdafx.h"

using namespace TPCE;

CMarketFeedDB::CMarketFeedDB(CDBConnection *pDBConn)
: CTxnHarnessDBBase(pDBConn)
, m_array_size (	sizeof(Frame1Input.Entries)
					/ sizeof(Frame1Input.Entries[0]))
, m_ret_ind(sizeof(long))
{
	int i, j;	

	//Allocate one handle per stored procedure (frame)
	if (   SQLAllocHandle(SQL_HANDLE_STMT, m_hdbc, &m_hstmtFrame1) != SQL_SUCCESS
		|| SQLAllocHandle(SQL_HANDLE_DESC, m_hdbc, &m_descFrame1Col1) != SQL_SUCCESS
		|| SQLAllocHandle(SQL_HANDLE_DESC, m_hdbc, &m_descFrame1Col2) != SQL_SUCCESS
		)
		ThrowError(CODBCERR::eAllocHandle, SQL_HANDLE_STMT, m_hdbc);

	//Set statement descriptor for the parameters and the first rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col1, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1);

	//Frame1 input
	i = 0;
	if (   //SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &Frame1Output.status, 0, &m_ret_ind)!=SQL_SUCCESS //sp return code
		   SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, cST_ID_len, 0, &Frame1Input.StatusAndTradeType.status_submitted, cST_ID_len+1, NULL)!=SQL_SUCCESS
		|| SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, cTT_ID_len, 0, &Frame1Input.StatusAndTradeType.type_limit_buy, cTT_ID_len+1, NULL)!=SQL_SUCCESS
		|| SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, cTT_ID_len, 0, &Frame1Input.StatusAndTradeType.type_limit_sell, cTT_ID_len+1, NULL)!=SQL_SUCCESS
		|| SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, cTT_ID_len, 0, &Frame1Input.StatusAndTradeType.type_stop_loss, cTT_ID_len+1, NULL)!=SQL_SUCCESS )		
		ThrowError(CODBCERR::eBindParam, SQL_HANDLE_STMT, m_hstmtFrame1);
	for (j = 0; j < m_array_size; ++j)
		if (   SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &Frame1Input.Entries[j].trade_qty, 0, NULL)!=SQL_SUCCESS
			|| SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, &Frame1Input.Entries[j].price_quote, 0, NULL)!=SQL_SUCCESS
			|| SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, cSYMBOL_len+1, 0, &Frame1Input.Entries[j].symbol, cSYMBOL_len+1, NULL)!=SQL_SUCCESS
			)
			ThrowError(CODBCERR::eBindParam, SQL_HANDLE_STMT, m_hstmtFrame1);
		
	//Although it is the default, but still set Bind-by-Column mode
	//SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
	//Set output row set size
	//SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROW_ARRAY_SIZE, SQLPOINTER(m_array_size), SQL_IS_INTEGER);
	//SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROWS_FETCHED_PTR, &m_rows_ret, SQL_IS_POINTER);

	//Frame1 output	
	i = 0;
	if (   SQLBindCol(m_hstmtFrame1, ++i, SQL_C_IDENT, &m_TriggeredOrder.trade_id, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_DOUBLE, &m_TriggeredOrder.price_quote, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_LONG, &m_TriggeredOrder.trade_qty, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, &m_TriggeredOrder.symbol, sizeof(m_TriggeredOrder.symbol), NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, &m_TriggeredOrder.trade_type_id, sizeof(m_TriggeredOrder.trade_type_id), NULL) != SQL_SUCCESS		
		)
		ThrowError(CODBCERR::eBindCol, SQL_HANDLE_STMT, m_hstmtFrame1);
	
	//Set statement descriptor for the second rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col2, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1);

	i = 0;
	if (
		   SQLBindCol(m_hstmtFrame1, ++i, SQL_C_SLONG, &m_len, 0, NULL) != SQL_SUCCESS		
		)
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1);
};

//Destructor
CMarketFeedDB::~CMarketFeedDB()
{
	SQLFreeHandle(SQL_HANDLE_STMT, m_hstmtFrame1);
}

void CMarketFeedDB::DoMarketFeedFrame1(const TMarketFeedFrame1Input	*pFrame1Input, 
									   PMarketFeedFrame1Output	pFrame1Output,
									   CSendToMarketInterface*	pSendToMarket)
{
	int			rc;
	int			i, j;
	SQLSMALLINT iNumCols;
	bool		bRet;

	//Copy input parameters to the bound location
	Frame1Input = *pFrame1Input;	//invoke default assignment operator
	
	//Set statement descriptor for the parameters and the first rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col1, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1");

	rc = SQLExecDirect(m_hstmtFrame1,
		/*(SQLCHAR*)"{call MarketFeedFrame1(?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}", SQL_NTS);*/
		(SQLCHAR*)"{call MarketFeedFrame1(?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}", SQL_NTS);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		ThrowError(CODBCERR::eExecDirect, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1");

	//find out if any result rows are returned
	rc = SQLNumResultCols(m_hstmtFrame1, &iNumCols);
	if (rc != SQL_SUCCESS)
		ThrowError(CODBCERR::eNumResultCols, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1");

	if (iNumCols > 1)
	{
		//Frame 1 may return several one-row result sets
		i = 0;
		do
		{
			if ( SQLFetch(m_hstmtFrame1) == SQL_ERROR)
				ThrowError(CODBCERR::eFetch, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1");

			// need to trim trailing spaces because MEE doesn't expect them 
			// (and may not find appropriate CMEESecurity instance)
			//
			for(j = 0; m_TriggeredOrder.symbol[j] != '\0'; ++j)
			{
				if (m_TriggeredOrder.symbol[j] == ' ')
				{	// found first space character => put NULL in its place
					m_TriggeredOrder.symbol[j] = '\0';
					break;	// exit the loop
				}
			}

			// Send to MEE from inside the frame.
			// That way no triggered orders are lost.
			//
			bRet = pSendToMarket->SendToMarketFromFrame(m_TriggeredOrder);
			if (bRet)
			{
				++i;
			}

			rc = SQLMoreResults(m_hstmtFrame1);	//returns SQL_NO_DATA if there are no more result sets
			if (rc == SQL_ERROR)
				ThrowError(CODBCERR::eMoreResults, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1");

			// find out if it's still the limit orders (more than one column)
			if ( SQLNumResultCols(m_hstmtFrame1, &iNumCols) != SQL_SUCCESS )
				ThrowError(CODBCERR::eNumResultCols, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1");

		} while (rc == SQL_SUCCESS && iNumCols > 1);

		pFrame1Output->send_len = i;	
	}
	else
	{
		pFrame1Output->send_len = 0;	//no results returned
	}

	//Set statement descriptor for the second rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col2, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1 (2nd rowset)");

	if ( (rc = SQLFetch(m_hstmtFrame1)) == SQL_ERROR)
		ThrowError(CODBCERR::eFetch, SQL_HANDLE_STMT, m_hstmtFrame1, "DoMarketFeedFrame1 (2nd rowset)");

	// Status code change introduced in EGen 1.6.0.
	//
	// Status is set in the stored procedure and returned as the second rowset.
	//
	pFrame1Output->num_updated = m_len;

	// Need two SQLFreeStmt calls for some reason to clean the statement
	// handle properly after the server sent "Transaction count incorrect after batch" message
	// with the result set.
	//
	rc = SQLFreeStmt(m_hstmtFrame1, SQL_CLOSE);
	rc = SQLFreeStmt(m_hstmtFrame1, SQL_CLOSE);
};

void CMarketFeedDB::Cleanup(void* pException)
{
	SQLCloseCursor(m_hstmtFrame1);
}
