///////////////////////////////////////////////////////////////////////////////////////////
//
// Microsoft WMIOLE DB Provider
// (C) Copyright 1999 Microsoft Corporation. All Rights Reserved.
//
// IRowsetIdentity interface implementation
//
///////////////////////////////////////////////////////////////////////////////////////////

#include "headers.h"


///////////////////////////////////////////////////////////////////////////////////////////
//
// Compares two row handles to see if they refer to the same row instance.
//
// HRESULT
//      S_FALSE                Method Succeeded but did not refer to the same row
//      S_OK				   Method Succeeded
//      DB_E_BADROWHANDLE      Invalid row handle given
//      OTHER                  Other HRESULTs returned by called functions
//
///////////////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CImplIRowsetRefresh::GetLastVisibleData(HROW hRow,HACCESSOR hAccessor,void * pData)
{
	//============================================================
	// Call this function of RowFetch Object to fetch data
	//============================================================

	HRESULT hr = S_OK;
    CSetStructuredExceptionHandler seh;

	TRY_BLOCK;
		
	// Seriliaze the object
	CAutoBlock cab(ROWSET->GetCriticalSection());

	// Clear Error information
	g_pCError->ClearErrorInfo();

	if(m_pObj->IsZoombie())
	{
		hr = E_UNEXPECTED;
	}
	else
	{
		hr =  m_pObj->m_pRowFetchObj->FetchData(m_pObj,hRow,hAccessor,pData);
	}

	hr = hr == S_OK ? hr :g_pCError->PostHResult(hr,&IID_IRowsetRefresh);

	CATCH_BLOCK_HRESULT(hr,L"IRowsetRefresh::GetLastVisibleData");
	return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////
//
// Compares two row handles to see if they refer to the same row instance.
//
// HRESULT
//      S_FALSE                Method Succeeded but did not refer to the same row
//      S_OK				   Method Succeeded
//      DB_E_BADROWHANDLE      Invalid row handle given
//		DB_E_BADCHAPTER		   Bad HCHAPTER
//		E_INVALIDARG		   One of the parameters is invalid
//      OTHER                  Other HRESULTs returned by called functions
//
// NTRaid 111830 (Raid about CRowset::GetData Found that this function is not returning
// the proper error while looking into this
// NTRaid 147095 
///////////////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CImplIRowsetRefresh::RefreshVisibleData(HCHAPTER        hChapter,
													DBCOUNTITEM      cRows,
													const HROW       rghRows[],
													BOOL             fOverwrite,
													DBCOUNTITEM *    pcRowsRefreshed,
													HROW **          prghRowsRefreshed,
													DBROWSTATUS **   prgRowStatus)
{
    HRESULT			hr					= S_OK;
	DBCOUNTITEM		lRowsRefreshed		= 0;
	DBROWSTATUS *	rgRowStatus;
	HROW *			prghRows			= NULL;
	BOOL			bGetAllActiveRows	= FALSE;
	BSTR			strBookMark			= Wmioledb_SysAllocString(NULL);

    CSetStructuredExceptionHandler seh;

	TRY_BLOCK;

	// Seriliaze the object
	CAutoBlock cab(ROWSET->GetCriticalSection());

	// Clear Error information
	g_pCError->ClearErrorInfo();

	*prgRowStatus =		NULL;

	if(m_pObj->IsZoombie())
	{
		hr = E_UNEXPECTED;
	}
	else
    //========================================================================
    //  Check the HChapter is of the current rowset if it is non zero
    //========================================================================
	if((LONG_PTR) cRows < 0 || ( cRows > 0 && rghRows == NULL) || 
		(pcRowsRefreshed == NULL && prghRowsRefreshed != NULL) )
	{
		hr = E_INVALIDARG;
	}
	else
    //========================================================================
    //  Check the HChapter is of the current rowset is valid
    //========================================================================
	if((m_pObj->m_bIsChildRs == TRUE && hChapter == DB_NULL_HCHAPTER) || (LONG_PTR)hChapter < 0  || 
		m_pObj->m_bIsChildRs == FALSE && hChapter != 0)
	{
		hr = DB_E_BADCHAPTER;
	}
	else
	{
		try
		{
			if(prghRowsRefreshed)
			{
				*prghRowsRefreshed = NULL;
			}
			if(pcRowsRefreshed)
			{
				*pcRowsRefreshed = 0;
			}
			if(prgRowStatus)
			{
				*prgRowStatus = NULL;
			}

			// If cRows is zero get all the active rows
			if(cRows == 0)
			{
				bGetAllActiveRows = TRUE;
				//=================================================
				// if the rowset is child rowset get the list 
				// open rowset from the chapter manager
				//=================================================
				if(m_pObj->m_bIsChildRs)
				{
					hr = m_pObj->m_pChapterMgr->GetAllHROWs(prghRows,cRows);

				}
				//=========================================
				// else get it from the instance manager
				//=========================================
				else
				{
					hr = m_pObj->m_InstMgr->GetAllHROWs(prghRows,cRows);
				}
			}
			else
			{
				prghRows = (HROW *)&rghRows[0];
			}

			// allocate memory for the row status
			rgRowStatus = (DBROWSTATUS *)g_pIMalloc->Alloc(sizeof(DBROWSTATUS) * cRows);
			if(S_OK == CheckIfRowsExists(cRows,prghRows,rgRowStatus))
			{
				// Navigate thru each  row 
				for( ULONG nIndex = 0 ; nIndex < cRows ; nIndex++)
				{
					// If the status of the row is ok then refresh the instance
					if(rgRowStatus[nIndex] == DBROWSTATUS_S_OK)
					{
						// call this function to refresh the instance pointer
						m_pObj->RefreshInstance(m_pObj->GetInstancePtr(prghRows[nIndex]));

						if(m_pObj->m_uRsType == PROPERTYQUALIFIER || 
							m_pObj->m_uRsType == CLASSQUALIFIER ) 
						{
								m_pObj->GetQualiferName(prghRows[nIndex],strBookMark);
						}

						// Release the previous row data
						hr = m_pObj->ReleaseRowData(prghRows[nIndex],FALSE); // don't release the slots
						
						if (FAILED(hr))
						{		
							rgRowStatus[nIndex] = DBROWSTATUS_E_FAIL;
						}
						//===================================================================
						// if data otherupdatedelete property is false then get the data
						// for the row, 
						// Get the data even if rowset is pointing to a qualifer rowset
						//===================================================================
						// Get the data for the current row
						hr = m_pObj->GetData(prghRows[nIndex],strBookMark);
						
						if(FAILED(hr))
						{
							rgRowStatus[nIndex] = DBROWSTATUS_E_FAIL;
						}
						else
						{
							m_pObj->m_ulLastFetchedRow = prghRows[nIndex];
							lRowsRefreshed++;
							rgRowStatus[nIndex] = DBROWSTATUS_S_OK;
						}
					}
					if(strBookMark != NULL)
					{
						SysFreeString(strBookMark);
						strBookMark = NULL;
					}
					hr = S_OK;
				}
				
				// Allocate memory for output HROW and ROWSTATUS
				if(pcRowsRefreshed)
				{
					*prghRowsRefreshed = (HROW *)g_pIMalloc->Alloc(sizeof(HROW) * cRows);
					if(*prghRowsRefreshed)
					{
						memcpy( *prghRowsRefreshed,prghRows,sizeof(HROW) * cRows);
						*pcRowsRefreshed = lRowsRefreshed;
					}
					else
					{
						hr = E_OUTOFMEMORY;
					}

					if(SUCCEEDED(hr) && prgRowStatus)
					{
						*prgRowStatus = (DBROWSTATUS *)g_pIMalloc->Alloc(sizeof(DBROWSTATUS) * cRows);
						if(*prgRowStatus)
						{
							memcpy( *prgRowStatus,rgRowStatus,sizeof(DBROWSTATUS) * cRows);
						}
						else
						{
							if(*prghRowsRefreshed != NULL)
							{
								g_pIMalloc->Free(*prghRowsRefreshed);
							}

							*prghRowsRefreshed = NULL;
							*pcRowsRefreshed = 0;

							hr = E_OUTOFMEMORY;
						}
					}
				}
								
				// free the temporarily allocated memory
				g_pIMalloc->Free(rgRowStatus);

				// free the memory if allocated by GetAllHROWs
				// which is called when cRows is 0 
				if(prghRows != NULL && bGetAllActiveRows == TRUE)
				{
					SAFE_DELETE_ARRAY(prghRows);
				}

			}
			else
			{
				hr = E_FAIL;
			}
		}
		catch(...)
		{
			if(rgRowStatus != NULL)
				g_pIMalloc->Free(rgRowStatus);
			
			if(*prgRowStatus != NULL)
				g_pIMalloc->Free(*prgRowStatus);
			
			if(*prghRowsRefreshed != NULL)
			g_pIMalloc->Free(*prghRowsRefreshed);

			if(prghRows != NULL && rghRows == NULL)
			{
				SAFE_DELETE_ARRAY(prghRows);
			}

			throw;
		}
	}

	if(SUCCEEDED(hr))
	{
		if(lRowsRefreshed == 0 && cRows)
		{
			hr = DB_E_ERRORSOCCURRED;
		}
		else
		{
			hr = cRows>lRowsRefreshed ? DB_S_ERRORSOCCURRED : S_OK;
		}
	}

	hr = hr == S_OK ? hr :g_pCError->PostHResult(hr,&IID_IRowsetRefresh);

	CATCH_BLOCK_HRESULT(hr,L"IRowsetRefresh::RefreshVisibleData");
	return hr;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Function to check the row handles and set the status of the row handles
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT CImplIRowsetRefresh::CheckIfRowsExists(DBCOUNTITEM cRows,const HROW rghRows[],DBROWSTATUS *   prgRowStatus)
{
	HRESULT hr = S_OK;
	DBSTATUS dwStatus = 0;

	if (!m_pObj->BitArrayInitialized()){

        //=================================================================================
		// This is the case when the method was called even before the rowset got fully
		// intialized, hence no rows could have been fetched yet, therefore there do not 
		// exist valid row handles, and all row handles provided must be invalid.
        //=================================================================================
		hr = DB_E_BADROWHANDLE;		
	}
	else
	{
		for ( ULONG nIndex = 0 ; nIndex < cRows ; nIndex++)
		{
        //=================================================================================
		// Check validity of input handles
        //=================================================================================
			if(TRUE == m_pObj->IsRowExists(rghRows[nIndex]) )
			{
				dwStatus = m_pObj->GetRowStatus(rghRows[nIndex]);
					if( dwStatus != DBROWSTATUS_S_OK )
					{
						if(dwStatus == DBROWSTATUS_E_DELETED || 
							dwStatus == DBROWSTATUS_E_DELETED)
								hr = DB_E_DELETEDROW;
							else
								prgRowStatus[nIndex] = DBROWSTATUS_E_FAIL;
					}
					else
						prgRowStatus[nIndex] = DBROWSTATUS_S_OK;

			}
			else
			{
				prgRowStatus[nIndex] = DBROWSTATUS_E_INVALID;
			}
		}
	}

	return hr;

}