// CImpIColumnsInfo::GetColumnInfo -------------------------------------------
//
// @mfunc Returns the column metadata needed by most consumers.
//
// @rdesc HRESULT
//      @flag S_OK              | The method succeeded
//      @flag E_OUTOFMEMORY     | Out of memory
//      @flag E_INVALIDARG      | pcColumns or prginfo or ppStringsbuffer was NULL
//
STDMETHODIMP CImpIColumnsInfo::GetColumnInfo
(
    DBORDINAL*      pcColumns,      //@parm OUT | Number of columns in rowset
    DBCOLUMNINFO**  prgInfo,        //@parm OUT | Array of DBCOLUMNINFO Structures
    WCHAR**         ppStringsBuffer //@parm OUT | Storage for all string values
)
{
	HRESULT			hr = S_OK;
	DBORDINAL		cCols = 0;
	DBORDINAL		cExtraCols = 0;
	DBORDINAL		cbHeapUsed = 0;
	DBORDINAL		cIter = 0;
	BYTE *			pbHeap = NULL;
	WCHAR *			pwstrBuffer = NULL;
	DBCOLUMNINFO *	rgdbcolinfo = NULL;
    DBCOLUMNINFO *	rgdbInternalcolinfo = NULL;
	DBCOLUMNINFO *	rgdbExtracolinfo = NULL;
	CRowset *		pCRowset = NULL;
	CFileIO	*		pFileio = NULL;

	//
	// Asserts
    //
	assert(m_pObj);

    //
    // Check in-params and NULL out-params in case of error
    //
	if( pcColumns )
		*pcColumns = 0;

	if( prgInfo )
		*prgInfo = NULL;
	
	if( ppStringsBuffer )
		*ppStringsBuffer = NULL;

    if( !pcColumns || !prgInfo || !ppStringsBuffer )
        return E_INVALIDARG;
	
	//
	// Get the Column Information off of the Command or Rowset
	//
	if( m_pObj->GetBaseObjectType() == BOT_COMMAND )
	{
		//
		// Asserts
		//
		assert(((CCommand *) m_pObj)->m_pCSession);
		assert(((CCommand *) m_pObj)->m_pCSession->m_pCDataSource);

		//
		// Check that a command has been set
		//
		if( !((CCommand *) m_pObj)->IsCommandSet() )
			return DB_E_NOCOMMAND;

		//
		// Try to open the file...
		//
		hr = ((CCommand *) m_pObj)->m_pCSession->m_pCDataSource->OpenFile(
						   ((CCommand *) m_pObj)->GetCommandText(), &pFileio);
		if( FAILED(hr) )
			return hr;
	}
	else 
	{
		if( m_pObj->GetBaseObjectType() == BOT_ROWSET )
			pFileio = ((CRowset *) m_pObj)->GetFileObj();
		else
		{
			pFileio = ((CRow *) m_pObj)->GetFileObj();

			cExtraCols = ((CRow *) m_pObj)->GetExtraColCount();
			rgdbExtracolinfo = ((CRow *) m_pObj)->GetExtraColumnInfo();
		}
	}

	//
	// Get the column count and delete unneeded info
	//
	cCols				= pFileio->GetColumnCnt();
	pbHeap				= pFileio->GetColNameHeap();		
	cbHeapUsed			= pFileio->GetColNameHeapSize();
	rgdbInternalcolinfo = pFileio->GetColInfo();		

	//
	// Return the column information
	//
	SAFE_ALLOC(rgdbcolinfo, DBCOLUMNINFO, (cCols + cExtraCols) * sizeof(DBCOLUMNINFO));
	SAFE_ALLOC(pwstrBuffer, WCHAR, cbHeapUsed);

	memcpy(rgdbcolinfo, &(rgdbInternalcolinfo[1]), cCols*sizeof(DBCOLUMNINFO));
	memcpy(rgdbcolinfo+cCols, rgdbExtracolinfo, cExtraCols*sizeof(DBCOLUMNINFO));
	
	//
	// Need to fix up column ordinals for extra columns
	//
	for (cIter=cCols; cIter < cCols+cExtraCols; cIter++)
		rgdbcolinfo[cIter].iOrdinal = cIter+1;

	//
	// Copy the heap for column names.
	//
	if( cbHeapUsed )
	{
		ptrdiff_t dp;
		
		memcpy(pwstrBuffer, pbHeap, cbHeapUsed);
		dp = (DBBYTEOFFSET)pwstrBuffer - (DBBYTEOFFSET)(pbHeap);
		dp >>= 1;

		// Loop through columns and adjust pointers to column names.
		for (ULONG icol=0; icol < cCols; icol++)
		{
			if( rgdbcolinfo[icol].pwszName )
				rgdbcolinfo[icol].pwszName += dp;
		}
	}
	
    //
	// Assign in the values
	//
    *pcColumns       = cCols + cExtraCols;
	*prgInfo         = rgdbcolinfo;
    *ppStringsBuffer = pwstrBuffer;

CLEANUP:

	//
	// Cleanup the File Information
	//
	if( m_pObj->GetBaseObjectType() == BOT_COMMAND )
		SAFE_DELETE(pFileio);

	if( FAILED(hr) )
	{
		SAFE_DELETE(rgdbcolinfo);
		SAFE_DELETE(pwstrBuffer);
	}

    return hr;
}