#include "stdafx.h"
#include "source.h"
#include "lcsource.h"
#include "regkey.h"
#include "source.h"
#include "utils.h"
#include "globals.h"
#include "busy.h"
#include "trapreg.h"




/////////////////////////////////////////////////////////////////////////////
// CLcSource

CLcSource::CLcSource()
{
}

CLcSource::~CLcSource()
{
}




//***************************************************************************
//
//  CLcSource::AddMessage
//
//  Add a message to the list control. This sets the text for each column in
//  the list view and sets the lParam field of the list-view item to pMessage.
//  
//
//  Parameters:
//		CMessage* pMessage
//
//  Returns:
//		Nothing.
//
//  Status:
//      
//***************************************************************************
void CLcSource::AddMessage(CXMessage* pMessage)
{
	CString sText;
    pMessage->GetShortId(sText);


    // Insert a new item into this list control.
    LV_ITEM lvitem;
    lvitem.mask = LVIF_TEXT | LVIF_PARAM;
    lvitem.iSubItem = ICOL_LcSource_EVENTID;
    lvitem.cchTextMax = MAX_STRING;
    lvitem.lParam = (LPARAM)pMessage;
    lvitem.pszText = (LPTSTR)(LPCTSTR)sText;
    int nItem = InsertItem(&lvitem);

    if (nItem >= 0)
    {
        CXEventSource* pEventSource = pMessage->m_pEventSource;

        // Now set the string value for each sub-item.
		pMessage->GetSeverity(sText); 
		SetItemText(nItem, ICOL_LcSource_SEVERITY, (LPTSTR)(LPCTSTR) sText);

		pMessage->IsTrapping(sText);
        SetItemText(nItem, ICOL_LcSource_TRAPPING, (LPTSTR)(LPCTSTR)sText);
        SetItemText(nItem, ICOL_LcSource_DESCRIPTION, (LPTSTR)(LPCTSTR) pMessage->m_sText);
    }
}


//*******************************************************************
// CXMessageArray::SetDescriptionWidth
//
// Set the width of the description field so that it is wide enough to
// hold the widest message.
//
// Parameters:
//      CXMessageArray& aMessages
//          The message array that will be used to fill the list control.
//
// Returns:
//      Nothing.
//
//*******************************************************************
void CLcSource::SetDescriptionWidth(CXMessageArray& aMessages)
{
    LONG cxWidestMessage = CX_DEFAULT_DESCRIPTION_WIDTH;
    LONG nMessages = aMessages.GetSize();
    for (LONG iMessage = 0; iMessage < nMessages; ++iMessage) {
        CXMessage* pMessage = aMessages[iMessage];
        int cx = GetStringWidth(pMessage->m_sText);
        if (cx > cxWidestMessage) {
            cxWidestMessage = cx;
        }
    }

    // Set the column width to the width of the widest string plus a little extra
    // space for slop and to make it obvious to the user that the complete string
    // is displayed.
    SetColumnWidth(ICOL_LcSource_DESCRIPTION, cxWidestMessage + CX_DESCRIPTION_SLOP);
}



//***************************************************************************
//
//  CLcSource::LoadMessages
//
//  Load the messages from the message library module and insert them into
//  this list control.
//
//  Parameters:
//		CMessage* pMessage
//
//  Returns:
//		Nothing.
//
//  Status:
//      
//***************************************************************************
SCODE CLcSource::SetEventSource(CXEventSource* pEventSource)
{
    CBusy busy;

	DeleteAllItems();

    if (pEventSource == NULL) {
        return S_OK;
    }


    UpdateWindow();
    
    //!!!CR: Should do something with the return code in case the
    //!!!CR: messages weren't loaded.
    SCODE sc = pEventSource->LoadMessages();


	// Iterate through each of the messages and insert them into
	// the list control.
    CXMessageArray& aMessages = pEventSource->m_aMessages;

    // Set the width of the description field so that it is wide enough to contain
    // the widest message.
    SetDescriptionWidth(aMessages);

	LONG nMessages = aMessages.GetSize();
	for (LONG iMessage=0; iMessage < nMessages; ++iMessage) {
        if ((iMessage < 40 && (iMessage % 10 == 9)) ||
            (iMessage % 100 == 99)) {
            // Update the window often for the first few messages and less frequently
            // thereafter for a good response time.
            UpdateWindow();
        }

		AddMessage(aMessages[iMessage]);
	}


    SortItems(ICOL_LcSource_EVENTID);
    SetRedraw(TRUE);
    UpdateWindow();
    EnsureVisible(0, FALSE);

	if (GetSize())
		SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);

	return S_OK;
}



BEGIN_MESSAGE_MAP(CLcSource, CListCtrl)
	//{{AFX_MSG_MAP(CLcSource)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CLcSource message handlers



//***************************************************************************
//
//  CLcSource::CreateWindowEpilogue()
//
//  This method is called after a window has been created for this list
//  control.  Final initialization is done here.
//
//  Parameters:
//		None.
//
//  Returns:
//		SCODE
//			S_OK if the initialization was successful, otherwise E_FAIL.
//
//  Status:
//      
//***************************************************************************
SCODE CLcSource::CreateWindowEpilogue()
{
	ListView_SetExtendedListViewStyle(m_hWnd, LVS_EX_FULLROWSELECT);
	SetColumnHeadings();
	return S_OK;
}

//***************************************************************************
//
//  CLcSource::SetColumnHeadings
//
//  Define's the columns for this list control.  The column title, width, and
//  order is defined here.
//
//  Parameters:
//		None.
//
//  Returns:
//		Nothing.
//
//  Status:
//      
//***************************************************************************
void CLcSource::SetColumnHeadings()
{
 	static UINT auiResColumnTitle[ICOL_LcSource_MAX] = {
		IDS_LcSource_TITLE_EVENT_ID,
		IDS_LcSource_TITLE_SEVERITY,
		IDS_LcSource_TITLE_TRAPPING,
		IDS_LcSource_TITLE_DESCRIPTION
	};

	static int aiColWidth[ICOL_LcSource_MAX] = {60, 75, 60, CX_DEFAULT_DESCRIPTION_WIDTH};

 
    // Build the columns in the AllEventsList control.
    LV_COLUMN lvcol; 
    lvcol.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    
    for (int iCol=0; iCol<ICOL_LcSource_MAX; ++iCol)
    {
		CString sColTitle;
		sColTitle.LoadString(auiResColumnTitle[iCol]);

        lvcol.pszText = sColTitle.GetBuffer(sColTitle.GetLength());
        lvcol.iSubItem = iCol;
        lvcol.cx = aiColWidth[iCol];
        InsertColumn(iCol, &lvcol);
		sColTitle.ReleaseBuffer();
    }
}




//******************************************************************
// CLcSource::Find
//
// Find the specified event source in this list control.
//
// Parameters:
//		CString& sText
//			A string containing the text to search for.
//
//		BOOL bWholeWord
//			TRUE if this is a "whole word" search.  False if it
//			is OK to match a partial word.
//
//		BOOL bMatchCase
//			TRUE if a case-sensitive comparison should be used.
//
// Returns:
//		BOOL
//			TRUE if the string was found, FALSE otherwise.  If the specified
//			text is found, then the selection is set on the corresponding
//			list control item, the item is scrolled into view and the focus
//			is set on the item.
//
//******************************************************************
BOOL CLcSource::Find(CString sText, BOOL bWholeWord, BOOL bMatchCase)
{
    // Don't do anything if the list is empty.
	if (GetSize() == 0) 
		return FALSE;

	if (!bMatchCase) 
		sText.MakeUpper();

    // Get the selected item.
    LONG iItem = GetNextItem(-1, LVNI_SELECTED);    

    // Nothing selected; start from the top of the list.
    if (iItem == -1)
        iItem = 0;


    // Iterate through all of the items starting at one item past
    // the currently selected item. 
	CXMessage* pMessage;
	CString sDescription;
    BOOL bFound = FALSE;
	LONG nItems = GetSize();
    LONG iItemStart = iItem;
	for (long i=0; !bFound && i<nItems; ++i) {
        // Bump the item index to the next one and wrap it if its past the
        // last item.
		iItem = (iItem + 1) % nItems;

        // Get the message description for this item.
		pMessage = GetAt(iItem);
        sDescription = pMessage->m_sText;

		if (!bMatchCase) 
			sDescription.MakeUpper();
        
        if (bWholeWord)	{
            // Compare the whole word.
			bFound = (FindWholeWord(sText, sDescription) != -1);
        }
        else {
	        // Look for a substring.
            if (sDescription.Find(sText) >= 0)  
                bFound = TRUE;
        } 
    }

    // Found a match.
    if (bFound)
    {
        // Unselect the selected item and select the found item.        
        SetItemState(iItemStart, 0, LVIS_SELECTED | LVIS_FOCUSED);
        SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
        EnsureVisible(iItem, FALSE);
        return TRUE;
    }

    return FALSE;
}






//***************************************************************************
//
//  fnCompareCLcSource
//
//  This the item comparison callback method that is called from CLcSource::SortItems. 
//
//  Parameters:
//		LPARAM lParam1
//			This is the lparam for the first item to compare.  This is a pointer to 
//			the associated CMessage object.
//
//		LPARAM lParam2
//			This is the lparam for the second item to compare.  This is a pointer to 
//			the associated CMessage object.
//
//		LPARAM lColumn
//			This is the second parameter that was passed to CListCtrl::SortItems.  This 
//			happens to be the list control column index.
//
//  Returns:
//		Nothing.
//
//  Status:
//      
//***************************************************************************
int CALLBACK fnCompareCLcSource(LPARAM lParam1, LPARAM lParam2, LPARAM lColumn)
{
    // !!!CR: The LPARAM parameters are not event pointers in all cases because
    // !!!CR: each subitem has its own LPARAM. What should I do?

    CXMessage *pmsg1 = (CXMessage *)lParam1;
    CXMessage *pmsg2 = (CXMessage *)lParam2;

    int nResult = 0;
    CString s1, s2;

    if (pmsg1 && pmsg2)
    {
        switch( lColumn)
        {
        case ICOL_LcSource_EVENTID:
        	nResult = ((LONG) pmsg1->GetShortId()) - ((LONG)pmsg2->GetShortId());			
        	break;
        case ICOL_LcSource_SEVERITY:
         	pmsg1->GetSeverity(s1);
        	pmsg2->GetSeverity(s2);
        	nResult = lstrcmpi(s1, s2);
        	break;
        case ICOL_LcSource_TRAPPING:
        	pmsg1->IsTrapping(s1);
        	pmsg2->IsTrapping(s2);
        	nResult = lstrcmpi(s1, s2);
        	break;
        case ICOL_LcSource_DESCRIPTION:
            nResult = lstrcmpi(pmsg1->m_sText, pmsg2->m_sText);
        	break;
        default:
         	ASSERT(FALSE);
            nResult = 0;
            break;
        }
    }

    if (!g_abLcSourceSortAscending[lColumn]) {
        if (nResult > 0) {
            nResult = -1;
        }
        else if (nResult < 0) {
            nResult = 1;
        }
    }

    return(nResult);
}


//***************************************************************************
//
//  CLcSource::SortItems
//
//  Sort the items in this list control given the column index.  This method
//  hides all details about the sort implementation from this class's clients.
//
//  Parameters:
//		DWORD dwColumn
//			The column to use as the sort key.  
//
//  Returns:
//		Nothing.
//
//  Status:
//      
//***************************************************************************
void CLcSource::SortItems(DWORD dwColumn)
{
    CListCtrl::SortItems(fnCompareCLcSource, dwColumn);
}




//***************************************************************************
//
//  CLcSource::GetSelectedMessages
//
//  Fill a message array with pointers to the messages that correspond to
//  the selected items in this list control.  
//
//  Note: This list control continues to own the returned pointers.  The
//  caller should not delete them.
//
//  Parameters:
//		CMessageArray& amsg
//			The message array where the pointers to the selected messages are
//			returned.
//
//  Returns:
//		The message array is filled with pointers to the selected messages.  Do
//		not delete them, because they are owned by this object.
//
//  Status:
//      
//***************************************************************************
void CLcSource::GetSelectedMessages(CXMessageArray& amsg)
{
	// Clear the message array
	amsg.RemoveAll();

	// Setup the LV_ITEM structure to retrieve the lparam field.  
	// This field contains the CMessage pointer.
    LV_ITEM lvitem;
    lvitem.mask = LVIF_PARAM;
    lvitem.iSubItem = ICOL_LcSource_EVENTID;

	// Loop to find all the selected items.	
	int nItem = -1;
	while (TRUE) {
		nItem = GetNextItem(nItem, LVNI_SELECTED);
		if (nItem == -1) {
			break;
		}

		// Get the CMessage pointer for this item and add it to the
		// array.
        lvitem.iItem = nItem;
        GetItem(&lvitem);
		CXMessage* pmsg = (CXMessage*) (void*) lvitem.lParam;
		amsg.Add(pmsg);
	}
}




//***************************************************************************
//
//  CLcSource::FindItem
//
//  Search through this list-controls's items to find the one with the
//  specified message ID.  
//
//  Parameters:
//		DWORD dwMessageId
//			The message ID to search for.
//
//  Returns:
//		The index of the item with the specified message ID.  If no such message ID
//		was found, -1 is returned.
//
//  Status:
//      
//***************************************************************************
LONG CLcSource::FindItem(DWORD dwMessageId)
{
	LONG nItems = GetItemCount();
	for (LONG iItem = 0; iItem < nItems; ++iItem) {
        CXMessage* pMessage = GetAt(iItem);
        if (pMessage->m_dwId == dwMessageId) {
            return iItem;
        }
	}
	return -1;
}




//***************************************************************************
//
//  CLcSource::RefreshItem
//
//  This method is called when some aspect of the message has changed and
//  the display needs to be updated.  This occurs when the trapping status
//  of an event changes.  
//
//  Parameters:
//		DWORD dwMessageId
//			The message ID to search for.
//
//  Returns:
//		The index of the item with the specified message ID.  If no such message ID
//		was found, -1 is returned.
//
//  Status:
//      
//***************************************************************************
void CLcSource::RefreshItem(LONG iItem)
{
	CXMessage* pMessage = GetAt(iItem);
    CString sText;

	// Now set the text value for each column in the list control.
    pMessage->GetSeverity(sText);
	SetItemText(iItem, ICOL_LcSource_SEVERITY, (LPTSTR)(LPCTSTR) sText);

    // Check if we are trapping this event.
	pMessage->IsTrapping(sText);
    SetItemText(iItem, ICOL_LcSource_TRAPPING, (LPTSTR)(LPCTSTR)sText);

    SetItemText(iItem, ICOL_LcSource_DESCRIPTION, (LPTSTR)(LPCTSTR)pMessage->m_sText);
}



//***************************************************************************
//
//  CLcSource::GetAt
//
//  This method returns the message pointer located at the given item index.
//  This allows CLcSource to be used much as an array.  
//
//  Parameters:
//		LONG iItem
//			The item index.
//
//  Returns:
//		A pointer to the CMessage stored at the specified index.
//
//  Status:
//      
//***************************************************************************
CXMessage* CLcSource::GetAt(LONG iItem) 
{
	// Setup the LV_ITEM structure to retrieve the lparam field.  
	// This field contains the CMessage pointer.
    LV_ITEM lvitem;
    lvitem.mask = LVIF_PARAM;
    lvitem.iSubItem = ICOL_LcSource_EVENTID;	
    lvitem.iItem = iItem;
    GetItem(&lvitem);

	CXMessage* pMessage = (CXMessage*) (void*) lvitem.lParam;
	return pMessage;
}



//***************************************************************************
// CLcSource::NotifyTrappingChange
//
// This method is called when a message's trapping status changes.  A message
// is considered trapped if it appears in the CLcEvents listbox.
//
// Parameters:
//      DWORD dwMessageId
//          The ID of the message who's trapping status is changing.
//
//      BOOL bIsTrapping
//          TRUE if the message is being trapped, FALSE otherwise.
//
// Returns:
//      Nothing.     
//      
//***************************************************************************
void CLcSource::NotifyTrappingChange(DWORD dwMessageId, BOOL bIsTrapping)
{
    LONG iItem = FindItem(dwMessageId);
    ASSERT(iItem != -1);

    if (iItem != -1) {
        CString sTrapping;
    	sTrapping.LoadString(bIsTrapping ? IDS_IS_TRAPPING : IDS_NOT_TRAPPING);
        SetItemText(iItem, ICOL_LcSource_TRAPPING, (LPTSTR)(LPCTSTR)sTrapping);
    }
}