Function NDW_HTTP_Logs_Events(CtrlEntId, Event, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15)
/***********************************************************************************************************************

    This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
    permission from SRP Computer Solutions, Inc.

    Name        :   NDW_HTTP_Logs_Events

    Description :   This function acts as a commuter module for all events related to this window.

    Notes       :   Commuter Modules are automatically called from the Promoted_Events function which is called by the
                    application-specific promoted event handler. This makes it possible to add QuickEvents that need to
                    execute Basic+ logic without having use the Form Designer to make the association, although this is
                    limited to the events which are currently promoted.

                    If the form needs to call the commuter module directly then the QuickEvent parameters should be
                    formatted like this:                                        

                        '@SELF','@EVENT',['@PARAM1','@PARAMx']

    Parameters  :
        CtrlEntId   [in] -- The fully qualified name of the control calling the promoted event
        Event       [in] -- The event being executed. See the Notes section regarding "PRE" events
        Param1-15   [in] -- Additional event parameter holders
        EventFlow  [out] -- Set to 1 or 0 so the calling event knows whether or not to chain forward. See comments in
                            EVENT_SETUP insert

    History (Date, Initials, Notes)
        06/18/19    jwh     [SRPFW-277] Created NDW_HTTP_LOGFILES_Events.
        06/20/19    jwh     [SRPFW-277] Got Report Table working with logs from 'C:\RevSoft\OInsight\httpLogs\'
        06/21/19    jwh     [SRPFW-277] Got hyperlinks to work from table to open actual log files
        07/17/19    dmb     [SRPFW-277] Rename to NDW_HTTP_Logs.
        07/17/19    dmb     [SRPFW-277] Replace harcoded reference to 'C:\RevSoft\OInsight\httpLogs\' with a call to the
                            GetCapturePath service.
        07/18/19    dmb     [SRPFW-277] Implement caching and filtering.
        07/19/19    dmb     [SRPFW-277] Prompt user to select the log file folder first, defaulting to the configured
                            capture path. Allow user to select a different folder if there are no files. Save the last
                            known location in cache.
        07/19/19    dmb     [SRPFW-277] Clear log count display when the list is being refreshed. Update the log count
                            display for every 100 logs found.
        07/19/19    dmb     [SRPFW-277] Change the default sort order of the log list to be descending for date, time,
                            process ID, and sequence.
        08/11/19    dmb     [SRPFW-278] Retrofit to use log indexes rather than the log files themselves. This speeds up
                            the process of populating the ReportTable.
        08/11/19    dmb     [SRPFW-278] Add ability to rebuild log indexes. Modify the Action Bar so the Refresh Logs
                            are now in the new Log Actions group.
        08/11/19    dmb     [SRPFW-278] Update the EDL_SEARCH_TEXT.CHAR event handler to avoid refreshing the list of
                            of logs if the user is backspacing in an empty control.
        08/13/19    dmb     [SRPFW-278] Revamp the search feature so that text searching no longer filters the list but
                            locates the next row with the match. Add support for Next and Previous search requests.
        09/23/19    dmb     [SRPFW-278] Various changes to add support for a Remote Address / Execute Time column.
        09/23/19    dmb     [SRPFW-278] Add support to archive logs.
        09/24/19    dmb     [SRPFW-278] Update archive log feature to display a success or error message as needed.
        09/24/19    dmb     [SRPFW-278] Hide ReportTable and display Picture control with caption when archiving logs
                            to help indicate a process is running.
        09/24/19    dmb     [SRPFW-278] Clear the log count indicator when the capture path is empty.
        09/25/19    dmb     [SRPFW-278] Update the RefreshLogTable internal gosub so the log count will report 0 instead
                            of empty if the UDP has not yet been set.
        09/28/19    dmb     [SRPFW-278] Fix VNAV in the OLE_ACTION_BAR.OnClick when archiving logs and cancelling the
                            CHOOSE folder dialog.
        11/14/19    dmb     [SRPFW-296] Replace the Utility("RUNWIN") service with ShellExecute in the
                            OLE_RPT_LOGTABLE.OnItemHyperlink event handler since not all systems work properly with the
                            RUNWIN service.

***********************************************************************************************************************/

#pragma precomp SRP_PreCompiler
#window NDW_HTTP_LOGS

$insert LOGICAL
$insert MSG_EQUATES
$insert HTTP_FRAMEWORK_SETUP_EQUATES

Equ CR$             to \0D\
Equ CRLF$           to \0D0A\
Equ BACKSPACE$      to \08\
Equ TAB$            to \09\
Equ NEXT$           to 1
Equ PREVIOUS$       to 2

Declare subroutine  Set_Property, Send_Event, Send_Message, PlaceDialog, Utility, SRP_JSON, Logging_Services, Yield
Declare subroutine  SRP_Run_Command, ShellExecute
Declare function    Get_Property, DirList, Dialog_Box, HTTP_Services, SRP_Path, Send_Message, SRP_Decode, SRP_JSON
Declare function    RTI_OS_Directory, Logging_Services, SRP_Array, Database_Services, GetTickCount

// Get the design time name of the window in case this is a multi-instance window.
Window = @Window[1, 'F*']

// Always get the CtrlClassID since we are not passing it through the event parameters.
CtrlClassId = Get_Property(CtrlEntId, 'TYPE')

// Get the name of the control on the window based on the CtrlClassId.
Begin Case
    Case CtrlClassId EQ 'WINDOW'
        Control = Window
    Case CtrlClassId EQ 'RADIOBUTTON'
        Control = Field(CtrlEntId, '.', 2, 2)
    Case CtrlClassId EQ 'MENU'
        Control = CtrlEntId[-1, 'B.']
    Case 1
        Control = Field(CtrlEntId, '.', 2, 1)
End Case

If Event EQ 'OLE' then GoSub TransferParams
GoToEvent Event for CtrlEntID
If Event EQ 'OLE' then GoSub RestoreParams

Return EventFlow OR EVENT_CONTINUE$


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Event WINDOW.CREATE(CreateParam)
    
    GoSub SetupOLEControls

    PlaceDialog(-2, -2)

    GoSub RefreshLogTable

end event


Event OLE_ACTION_BAR.OnClick(Group, Item, Point, Button, Shift, Ctrl)

    Begin Case
        Case Group EQ 1
            // Form Actions
            Begin Case
                Case Item EQ 1
                    // Clear Filters
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'INVALUE', '')
                    Set_Property(@Window : '.EDL_START_DATE', 'INVALUE', '')
                    Set_Property(@Window : '.EDL_END_DATE', 'INVALUE', '')
                    Set_Property(@Window : '.EDL_START_TIME', 'INVALUE', '')
                    Set_Property(@Window : '.EDL_END_TIME', 'INVALUE', '')
                    GoSub RefreshLogTable
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'FOCUS', True$)
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'SELECTION', 1)
                    Set_Property(@Window : '.EDL_START_DATE', 'GOTFOCUS_VALUE', '')
                Case Item EQ 2
                    // Close Form
                    Send_Event(@Window, 'CLOSE')
            End Case

        Case Group EQ 2
            // Log Actions
            Begin Case
                Case Item EQ 1
                    // Refresh Logs
                    Set_Property(@Window, '@FULL_LOG_LIST', '')
                    Set_Property(@Window, '@FULL_RECORD_COLORS', '')
                    Set_Property(@Window, '@FULL_LOG_COUNT', 0)
                    Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.List', '')
                    Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.RecordColors', '')
                    GoSub RefreshLogTable
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'FOCUS', True$)
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'SELECTION', 1)
                Case Item EQ 2
                    GoSub RebuildLogIndex
                    // Refresh Logs
                    Set_Property(@Window, '@FULL_LOG_LIST', '')
                    Set_Property(@Window, '@FULL_RECORD_COLORS', '')
                    Set_Property(@Window, '@FULL_LOG_COUNT', 0)
                    Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.List', '')
                    Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.RecordColors', '')
                    GoSub RefreshLogTable
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'FOCUS', True$)
                    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'SELECTION', 1)
                Case Item EQ 3
                    // Archive Logs
                    ArchiveDate = Dialog_Box('NDW_HTTP_LOGS_ARCHIVE_DATE', @Window)
                    If ArchiveDate NE '' then
                        Utility('CURSOR', 'H')
                        Set_Property(@Window : '.OLE_PIC_MESSAGE', 'OLE.Caption', 'Please wait while logs are archived.' : @TM : @TM : 'This might take several minutes.')
                        Set_Property(@Window : '.OLE_PIC_MESSAGE', 'VISIBLE', True$)
                        Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'VISIBLE', False$)
                        LogCapturePath      = Get_Property(@Window, '@LOG_CAPTURE_PATH')
                        ArchivePathExists   = False$    ; // Assume false for now.
                        // If the log capture path has not been identified, allow the user to pick one.
                        If LogCapturePath EQ '' then
                            // Default to the configured log capture folder.
                            LogCapturePath  = HTTP_Services('GetCapturePath')
                            LogCapturePath  = RTI_OS_Directory('CHOOSE', LogCapturePath, 'Path to the HTTP log files.')
                        end
                        If LogCapturePath NE '' then
                            LogArchivePath  = SRP_Path('Combine', LogCapturePath, 'archive')
                            If RTI_OS_Directory('EXISTS', LogArchivePath) EQ -1 then
                                ArchivePathExists   = RTI_OS_Directory('CREATE', LogArchivePath)
                            end else
                                ArchivePathExists   = True$
                            end
                        end
                        If ArchivePathExists EQ True$ then
                            // Both the log path and the log archive path are confirmed.
                            ArchiveDate         = Oconv(ArchiveDate, 'DJS-')
                            ArchiveScript       = Database_Services('ReadDataRow', SetupTable$, ArchiveScriptKeyID$)
                            Swap @FM with CRLF$ in ArchiveScript
                            WindowsTempPath = RTI_OS_Directory('GetWindowsTempPath')
                            ScriptFilePath  = SRP_Path('Combine', WindowsTempPath, 'psoi ' : GetTickCount() : '.ps1')
                            Swap '{{ArchiveOlderThanDate}}' with ArchiveDate in ArchiveScript
                            Swap '{{ArchiveStorageFolder}}' with LogArchivePath in ArchiveScript
                            Swap '{{ArchiveLogsFromFolder}}' with LogCapturePath in ArchiveScript
                            OSWrite ArchiveScript to ScriptFilePath
                            // Create a command line to run the script.
                            ArchiveCommand  = 'PowerShell.exe -noprofile -ExecutionPolicy ByPass -File "' : ScriptFilePath : '"'
                            Results         = 'VAR'
                            SRP_Run_Command(ArchiveCommand, Results, WindowsTempPath)
                            Convert \0D0A\ to '' in Results
                            OSDelete ScriptFilePath
                            If Results[1, 2] EQ 'OK' then
                                Message = 'Logs have been successfully archived.'
                                Icon    = '*'
                            end else
                                Error   = Results[6, Len(Results)]
                                Message = 'There was an error archiving the logs: ' : Error
                                Icon    = '!'
                            end
                            MsgStruct               = ''
                            MsgStruct<MTEXT$>       = Message
                            MsgStruct<MICON$>       = Icon
                            MsgStruct<MDEFBTN$>     = 2
                            MsgStruct<MJUST$>       = 'L'
                            MsgStruct<MCAPTION$>    = 'HTTP Logs'
                            MsgStruct<MFONT$>       = 'Segoe UI' : @SVM : -12 : @SVM : 400 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 34 : @SVM : 0 : @SVM : 3 : @SVM : 2 : @SVM : 1 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0
                            Msg(@Window, MsgStruct)
                        end

                        Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'VISIBLE', True$)
                        Set_Property(@Window : '.OLE_PIC_MESSAGE', 'VISIBLE', False$)
                        Set_Property(@Window : '.OLE_PIC_MESSAGE', 'OLE.Caption', '')
                        Utility('CURSOR', 'A')
                    end
            End Case
    End Case
    
end event


Event EDL_SEARCH_TEXT.LOSTFOCUS(Flag, FocusID)

    If Flag EQ 1 then
        // Only trigger the search if moving to another control on the form.
        Send_Event(@Window : '.OLE_PUB_FIND_NEXT', 'OLE', 'OnClick')
    end

end event


Event EDL_START_DATE.OPTIONS()
    
    CurrentDate = Get_Property(@Window : '.EDL_START_DATE', 'INVALUE')
    NewDate     = Dialog_Box('NDW_HTTP_DATEPICKER', @Window)
    If CurrentDate NE NewDate then
        Set_Property(CtrlEntId, 'INVALUE', NewDate)
        GoSub RefreshLogTable
    end

end event


Event EDL_END_DATE.OPTIONS()

    CurrentDate = Get_Property(CtrlEntId, 'INVALUE')
    NewDate     = Dialog_Box('NDW_HTTP_DATEPICKER', @Window)
    If CurrentDate NE NewDate then
        Set_Property(CtrlEntId, 'INVALUE', NewDate)
        GoSub RefreshLogTable
    end

end event


Event EDL_START_DATE.LOSTFOCUS(Flag, FocusID)

    GotFocusID  = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    StartDate   = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusID NE StartDate then
        GoSub RefreshLogTable
    end

end event


Event EDL_END_DATE.LOSTFOCUS(Flag, FocusID)

    GotFocusID  = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    EndDate     = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusID NE EndDate then
        GoSub RefreshLogTable
    end

end event


Event EDL_START_TIME.LOSTFOCUS(Flag, FocusID)

    GotFocusID  = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    StartTime   = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusID NE StartTime then
        GoSub RefreshLogTable
    end

end event


Event EDL_END_TIME.LOSTFOCUS(Flag, FocusID)

    GotFocusID  = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    EndTime     = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusID NE EndTime then
        GoSub RefreshLogTable
    end

end event


Event OLE_PUB_FIND_NEXT.OnClick(Point, Button, Shift, Ctrl)

    Direction   = NEXT$
    GoSub FindMatch

end event


Event OLE_PUB_FIND_PREVIOUS.OnClick(Point, Button, Shift, Ctrl)

    Direction   = PREVIOUS$
    GoSub FindMatch

end event


Event OLE_RPT_LOGTABLE.OnItemHyperlink(Row, Col, Text)

    // Get the path to the log file from the hidden column and display it in the default .log viewer.
    Record      = Send_Message(CtrlEntId, 'OLE.RowToRecord', Row)
    LogFilePath = Get_Property(CtrlEntId, 'OLE.ItemText[11;' : Record : ']')
    ShellExecute(0, 'open' : \00\, LogFilePath : \00\, '', '', 5)

end event


Event OLE_SUBCLASS.OnOptionClick(CtrlId)

    Send_Event(CtrlId, 'OPTIONS')

end event


Event MENU.FILTER_TEXT.MENU()

    Set_Property(@Window : '.EDL_SEARCH_TEXT', 'FOCUS', True$)

end event


Event MENU.FIND_NEXT.MENU()

    Send_Event(@Window : '.OLE_PUB_FIND_NEXT', 'OLE', 'OnClick')

end event


Event MENU.FIND_PREVIOUS.MENU()

    Send_Event(@Window : '.OLE_PUB_FIND_PREVIOUS', 'OLE', 'OnClick')

end event


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal Gosubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

SetupOLEControls:
    
    // All OLE controls can use this qualify configuration.
    Qualify     = ''
    Qualify<1>  = 1
    Qualify<3>  = ''
    Qualify<4>  = 0

    //------------------------------------------------------------------------------------------------------------------
    //
    //  SRP Subclass Control
    //
    //------------------------------------------------------------------------------------------------------------------
    Ctrl        = @Window : '.OLE_SUBCLASS'
    EditCtrls   = 'EDL_START_DATE,EDL_END_DATE'
    NumCtrls    = DCount(EditCtrls, ',')
    For EditCnt = 1 to NumCtrls
        EditCtrl    = Field(EditCtrls, ',', EditCnt, 1)
        Handle      = Get_Property(@Window : '.' : EditCtrl, 'HANDLE')
        Send_Message(Ctrl, 'OLE.Subclass', Handle, @Window : '.' : EditCtrl)
        Set_Property(Ctrl, 'OLE.OptionButton[' : @Window : ';' : EditCtrl : ']', True$)
        Set_Property(Ctrl, 'OLE.OptionImage[' : @Window : ';' : EditCtrl : ']', 'BMPS\SRPHTTPDateField.png')
        Set_Property(Ctrl, 'OLE.Prompt[' : @Window : ';' : EditCtrl : ']', 'YYYY-MM-DD' : @FM : @FM : 'Center' : @FM : 'Center' : @FM : 'Segoe UI' : @SVM : 9 : @SVM : 400 : @VM : 0)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnOptionClick', Qualify)
    Next EditCnt

    EditCtrls   = 'EDL_START_TIME,EDL_END_TIME'
    NumCtrls    = DCount(EditCtrls, ',')
    For EditCnt = 1 to NumCtrls
        EditCtrl    = Field(EditCtrls, ',', EditCnt, 1)
        Handle      = Get_Property(@Window : '.' : EditCtrl, 'HANDLE')
        Send_Message(Ctrl, 'OLE.Subclass', Handle, @Window : '.' : EditCtrl)
        Set_Property(Ctrl, 'OLE.Prompt[' : @Window : ';' : EditCtrl : ']', 'hh:mm:ss AM/PM' : @FM : @FM : 'Center' : @FM : 'Center' : @FM : 'Segoe UI' : @SVM : 9 : @SVM : 400 : @VM : 0)
    Next EditCnt

    EditCtrls   = 'EDL_SEARCH_TEXT'
    NumCtrls    = DCount(EditCtrls, ',')
    For EditCnt = 1 to NumCtrls
        EditCtrl    = Field(EditCtrls, ',', EditCnt, 1)
        Handle      = Get_Property(@Window : '.' : EditCtrl, 'HANDLE')
        Send_Message(Ctrl, 'OLE.Subclass', Handle, @Window : '.' : EditCtrl)
        Set_Property(Ctrl, 'OLE.Prompt[' : @Window : ';' : EditCtrl : ']', 'Search anything...(Ctrl+F)' : @FM : @FM : 'Center' : @FM : 'Center' : @FM : 'Segoe UI' : @SVM : 9 : @SVM : 400 : @VM : 0)
    Next EditCnt


    //------------------------------------------------------------------------------------------------------------------
    //
    //  SRP Button Control
    //
    //------------------------------------------------------------------------------------------------------------------
    ButtonCtrls = 'OLE_PUB_FIND_NEXT,OLE_PUB_FIND_PREVIOUS'
    NumCtrls    = DCount(ButtonCtrls, ',')
    For ButtonCnt = 1 to NumCtrls
        Ctrl    = @Window : '.' : Field(ButtonCtrls, ',', ButtonCnt, 1)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnClick', Qualify)
    Next ButtonCnt


    //------------------------------------------------------------------------------------------------------------------
    //
    //  SRP ReportTable Control
    //
    //------------------------------------------------------------------------------------------------------------------
    Ctrl        = @Window : '.OLE_RPT_LOGTABLE'
    ColumnList  = ''
    ColumnList := 'Date'                                : @VM : 'Date'   : @VM : '100' : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Left'   : @VM : @VM : 'YYYY-MM-DD'  : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Time'                                : @VM : 'Time'   : @VM : '100' : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Left'   : @VM : @VM : 'hh:mm:ss AA' : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Process ID'                          : @VM : 'Number' : @VM : '80'  : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Right'  : @VM : @VM : '0'           : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Sequence'                            : @VM : 'Number' : @VM : '80'  : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Center' : @VM : @VM : '000000'      : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Log Type'                            : @VM : 'Text'   : @VM : '80'  : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Left'   : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Method'                              : @VM : 'Text'   : @VM : '80'  : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Left'   : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Home URL / Status Code'              : @VM : 'Text'   : @VM : '150' : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Left'   : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'API Endpoint / Status Phrase'        : @VM : 'Text'   : @VM : '150' : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Left'   : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'Remote Address / Execute Time'       : @VM : 'Text'   : @VM : '150' : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Right'  : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @FM
    ColumnList := 'View'                                : @VM : 'Text'   : @VM : '75'  : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : True$  : @VM : @VM : 'Center' : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9' : @VM : '' : @VM : '' : @VM : True$ : @FM
    ColumnList := 'Log File'                            : @VM : 'Text'   : @VM : '350' : @VM : True$ : @VM : True$ : @VM : @VM : True$ : @VM : False$ : @VM : @VM : 'Left'   : @VM : @VM : ''            : @VM : False$ : @VM : 'Segoe UI' : @SVM : '9'
    Set_Property(Ctrl, 'OLE.ColumnList', ColumnList)
    Set_Property(Ctrl, 'OLE.SortOrder', 1 : @VM : 'D' : @FM : 2 : @VM : 'D' : @FM : 3 : @VM : 'D' : @FM : 4 : @VM : 'D')
    Set_Property(Ctrl, 'OLE.AlwaysShowSelection', True$)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnItemHyperlink', Qualify)


    //------------------------------------------------------------------------------------------------------------------
    //
    //  SRP Picture Control
    //
    //------------------------------------------------------------------------------------------------------------------
    Ctrl        = @Window : '.OLE_PIC_MESSAGE'
    Set_Property(Ctrl, 'OLE.Border', 'XP Flat')
    Set_Property(Ctrl, 'OLE.Font', 'Segoe UI' : @SVM : '24')
    Set_Property(Ctrl, 'OLE.BackgroundColor', '3DFace')
    Set_Property(Ctrl, 'OLE.CaptionAlignment', 'Center' : @FM : 'Center')


    //------------------------------------------------------------------------------------------------------------------
    //
    //  SRP ShortcutBar Control
    //
    //------------------------------------------------------------------------------------------------------------------
    Ctrl        = @Window : '.OLE_ACTION_BAR'
    Set_Property(Ctrl, 'OLE.Border', 'XP Flat')
    Set_Property(Ctrl, 'OLE.Animation', 'Never')
    Set_Property(Ctrl, 'OLE.Theme', 'Office2007Blue')
    Set_Property(Ctrl, 'OLE.GroupFont', 'Segoe UI' : @SVM : 11 : @SVM : 400)
    Set_Property(Ctrl, 'OLE.ItemFont', 'Segoe UI' : @SVM : 9 : @SVM : 400)
    Set_Property(Ctrl, 'OLE.GroupCount', 2)
    Set_Property(Ctrl, 'OLE.GroupCaption[1]', 'Form Actions')
    Set_Property(Ctrl, 'OLE.GroupItemCount[1]', 2)
    Set_Property(Ctrl, 'OLE.ItemCaption[1;1]', 'Clear Filters')
    Set_Property(Ctrl, 'OLE.ItemCaption[1;2]', 'Close Form')
    Set_Property(Ctrl, 'OLE.GroupCaption[2]', 'Log Actions')
    Set_Property(Ctrl, 'OLE.GroupItemCount[2]', 3)
    Set_Property(Ctrl, 'OLE.ItemCaption[2;1]', 'Refresh Logs')
    Set_Property(Ctrl, 'OLE.ItemCaption[2;2]', 'Rebuild Log Index')
    Set_Property(Ctrl, 'OLE.ItemCaption[2;3]', 'Archive Logs')
    Margins = 0 : @FM : 0 : @FM : 0 : @FM : 0 : @FM : 0
    Set_Property(Ctrl, 'OLE.HotTrackStyle', 'Item')
    Set_Property(Ctrl, 'OLE.ItemBold[All; All]', True$)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnClick', Qualify)
    
return


RefreshLogTable:

    OrigLogList     = Get_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.List')
    FullLogList     = Get_Property(@Window, '@FULL_LOG_LIST')
    RecordColors    = Get_Property(@Window, '@FULL_RECORD_COLORS')
    LogCount        = Get_Property(@Window, '@FULL_LOG_COUNT') + 0
    If LogCount EQ 1 then
        LogFoundText    = LogCount : ' Log Found'
    end else
        LogFoundText    = LogCount : ' Logs Found'
    end
    Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', LogFoundText)
    If FullLogList EQ '' then
        // There is no full log list available so rebuild it.
        GoSub GetLogList
    end
    // Apply any filters that have been set. This will remove log rows that do not match the filter criteria.
    GoSub ApplyFilters
    // Compare the original log list with the new one created by any filters. If they match then there is no needs to
    // refresh the display.
    If OrigLogList NE LogList then
        Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.List', LogList)
        Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.RecordColors', RecordColors)
        If LogCount EQ 1 then
            LogFoundText    = LogCount : ' Log Found'
        end else
            LogFoundText    = LogCount : ' Logs Found'
        end
        Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', LogFoundText)
        // If a search value is entered, find it in the new filtered list.
        Direction   = NEXT$
        GoSub FindMatch
    end

return


TransferParams:

    // ActiveX controls pass their own event names through Param1. Modify the parameter values so they conform to
    // OpenInsight event parameter values. This will allow commuter modules to be structured the same for OpenInsight
    // event and ActiveX (OLE) events.
    Transfer Param1  to Event
    Transfer Param2  to Param1
    Transfer Param3  to Param2
    Transfer Param4  to Param3
    Transfer Param5  to Param4
    Transfer Param6  to Param5
    Transfer Param7  to Param6
    Transfer Param8  to Param7
    Transfer Param9  to Param8
    Transfer Param10 to Param9
    Transfer Param11 to Param10
    Transfer Param12 to Param11
    Transfer Param13 to Param12
    Transfer Param14 to Param13
    Transfer Param15 to Param14

return


RestoreParams:

    // Restore the event parameters so the rest of the event chain will see the parameter values as they were originally
    // created by OpenInsight. This will also prevent the parameter values from being transferred multiple times in case
    // there are multiple OLE promoted event handlers (e.g. APPNAME*..OIWIN* and APPNAME*OLE..OIWIN*).
    Transfer Param14 to Param15
    Transfer Param13 to Param14
    Transfer Param12 to Param13
    Transfer Param11 to Param12
    Transfer Param10 to Param11
    Transfer Param9  to Param10
    Transfer Param8  to Param9 
    Transfer Param7  to Param8 
    Transfer Param6  to Param7
    Transfer Param5  to Param6
    Transfer Param4  to Param5
    Transfer Param3  to Param4
    Transfer Param2  to Param3
    Transfer Param1  to Param2
    Transfer Event   to Param1
    Event             = 'OLE'

return


GetLogList:

    Utility('CURSOR', 'H')

    // Try to build the log list using the log index files. This is much quicker than parsing each individual log file.
    GoSub GetLogFileIndexes

    // If there are log index files, proceed to build the log list from them.
    If LogFileIndexes NE '' then
        // Save the current log capture path since it is valid.
        Set_Property(@Window, '@LOG_CAPTURE_PATH', LogCapturePath)
        FullLogList     = ''
        RecordColors    = ''
        LogCount        = 0
        Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', '0 Logs Found')
        // Build the log list one log row at a time. This will also allow us to create a custom color scheme for each
        // log row based on the log type and other relevant information.
        For Each LogFileIndex in LogFileIndexes using @FM
            IndexFilePath   = SRP_Path('Combine', LogCapturePath, LogFileIndex)
            OSRead LogFile from IndexFilePath then
                FileRead    = True$
            end else
                FileRead    = False$
            end
            If FileRead EQ True$ then
                // Initialize the variables that might not be used.
                Convert CR$ to '' in LogFile
                Convert TAB$ to @VM in LogFile
                For Each LogRow in LogFile using \0A\
                    LogType         = LogRow<0, 5>
                    HomeURLStatus   = LogRow<0, 7>
                    LogFilePath     = LogRow<0, 11>
                    LogRow<0, 11>   = SRP_Path('Combine', LogCapturePath, LogFilePath[-1, 'B\'])
                    Begin Case
                        Case LogType _EQC 'Response'
                            Begin Case
                                Case HomeURLStatus[1, 1] EQ 1
                                    RecordColor = 'None' : @VM : 'ForestGreen'
                                Case HomeURLStatus[1, 1] EQ 2
                                    RecordColor = 'None' : @VM : 'ForestGreen'
                                Case HomeURLStatus[1, 1] EQ 3
                                    RecordColor = 'None' : @VM : 'ForestGreen'
                                Case HomeURLStatus[1, 1] EQ 4
                                    RecordColor = 'None' : @VM : 'Crimson'
                                Case HomeURLStatus[1, 1] EQ 5
                                    RecordColor = 'None' : @VM : 'FireBrick'
                                Case Otherwise$
                                    RecordColor = 'None' : @VM : 'ForestGreen'
                            End Case
                        Case LogType _EQC 'Aborted'
                            RecordColor = 'None' : @VM : 'Red'
                        Case LogType _EQC 'Debugger'
                            RecordColor = 'None' : @VM : 'Red'
                        Case LogType _EQC 'GetStatus'
                            RecordColor = 'None' : @VM : 'Red'
                        Case Otherwise$
                            // Default colors.
                            RecordColor = 'None' : @VM : 'None'
                    End Case
                    FullLogList    := LogRow : @FM
                    RecordColors   := RecordColor : @FM
                    LogCount       += 1
                    If Mod(LogCount, 100) EQ 0 then
                        Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', LogCount : ' Logs Found')
                        Yield(); Yield(); Yield(); Yield()
                        Utility('CURSOR', 'H')
                    end
                Next LogRow
            end
        Next LogFileName
        FullLogList[-1, 1]  = ''
        RecordColors[-1, 1] = ''
        Set_Property(@Window, '@FULL_LOG_LIST', FullLogList)
        Set_Property(@Window, '@FULL_RECORD_COLORS', RecordColors)
        Set_Property(@Window, '@FULL_LOG_COUNT', LogCount)
    end

    Utility('CURSOR', 'A')

return


GetLogFileIndexes:

    LogFileIndexes  = ''
    // Check first to see if a valid log capture path has already been identified.
    LogCapturePath  = Get_Property(@Window, '@LOG_CAPTURE_PATH')
    // If the log capture path has not been identified, allow the user to pick one.
    If LogCapturePath EQ '' then
        // Default to the configured log capture folder.
        LogCapturePath  = HTTP_Services('GetCapturePath')
        LogCapturePath  = RTI_OS_Directory('CHOOSE', LogCapturePath, 'Path to the HTTP log files.')
    end
    If LogCapturePath NE '' then
        InitPath        = SRP_Path('Combine', LogCapturePath, '*_Index.log')
        Utility('CURSOR', 'H')
        InitDir InitPath
        LogFileIndexes  = DirList()
        If LogFileIndexes EQ '' then
            // No log index files can be found. Check to see if there are detailed log files. If so, then offer to
            // rebuild the index files. If not, then offer to pick another location.
            RebuildIndexes  = False$
            InitPath        = SRP_Path('Combine', LogCapturePath, '*.log')
            Utility('CURSOR', 'H')
            InitDir InitPath
            LogFiles        = DirList()
            If LogFiles NE '' then
                // Detailed log files were found. Offer to rebuild the index files.
                MsgStruct               = ''
                MsgStruct<MTEXT$>       = 'No log indexes could be found but there are detailed log files.' : @TM : @TM : 'Would you like to rebuild the log indexes?'
                MsgStruct<MTYPE$>       = 'BNY'
                MsgStruct<MICON$>       = '?'
                MsgStruct<MDEFBTN$>     = 2
                MsgStruct<MJUST$>       = 'L'
                MsgStruct<MCAPTION$>    = 'HTTP Logs'
                MsgStruct<MFONT$>       = 'Segoe UI' : @SVM : -12 : @SVM : 400 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 34 : @SVM : 0 : @SVM : 3 : @SVM : 2 : @SVM : 1 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0
                RebuildIndexes          = Msg(@Window, MsgStruct)
            end
            If RebuildIndexes EQ True$ then
                Set_Property(@Window, '@LOG_CAPTURE_PATH', LogCapturePath)
                GoSub RebuildLogIndex
                GoSub GetLogFileIndexes
            end else
                // The log files may have been relocated or this is running from a different machine than the OEngineServer
                // is running from. Allow the user an opportunity to pick a different directory.
                MsgStruct               = ''
                MsgStruct<MTEXT$>       = 'No log indexes could be found at this location:' : @TM : @TM : LogCapturePath : @TM : @TM : 'Would you like to select another path?'
                MsgStruct<MTYPE$>       = 'BNY'
                MsgStruct<MICON$>       = '?'
                MsgStruct<MDEFBTN$>     = 2
                MsgStruct<MJUST$>       = 'L'
                MsgStruct<MCAPTION$>    = 'HTTP Logs'
                MsgStruct<MFONT$>       = 'Segoe UI' : @SVM : -12 : @SVM : 400 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 34 : @SVM : 0 : @SVM : 3 : @SVM : 2 : @SVM : 1 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0
                PickAnother             = Msg(@Window, MsgStruct)
                If PickAnother EQ True$ then
                    LogCapturePath  = RTI_OS_Directory('CHOOSE', '', 'Path to the HTTP log files.')
                    InitPath        = SRP_Path('Combine', LogCapturePath, '*_Index.log')
                    Utility('CURSOR', 'H')
                    InitDir InitPath
                    LogFileIndexes  = DirList()
                    If LogFileIndexes NE '' then
                        Set_Property(@Window, '@LOG_CAPTURE_PATH', LogCapturePath)
                    end
                end
            end
        end else
            Set_Property(@Window, '@LOG_CAPTURE_PATH', LogCapturePath)
        end
    end

return


RebuildLogIndex:

    // Remove all current indexes.
    LogCapturePath  = Get_Property(@Window, '@LOG_CAPTURE_PATH')
    InitPath        = SRP_Path('Combine', LogCapturePath, '*_Index.log')
    Utility('CURSOR', 'H')
    InitDir InitPath
    LogFileIndexes  = DirList()
    If LogFileIndexes NE '' then
        For Each LogFileIndex in LogFileIndexes using @FM
            IndexFilePath   = SRP_Path('Combine', LogCapturePath, LogFileIndex)
            OSDelete IndexFilePath
        Next LogFileIndex
    end

    // Get all detail logs and create new indexes.
    GoSub GetLogFiles
    If LogFiles NE '' then
        LogCount    = 0
        For Each LogFileName in LogFiles using @FM
            If IndexC(LogFileName, 'Index', 1) else
                LogFilePath = SRP_Path('Combine', LogCapturePath, LogFileName)
                OSRead LogFile from LogFilePath then
                    FileRead    = True$
                end else
                    FileRead    = False$
                end
                If FileRead EQ True$ then
                    LogDate         = Iconv(LogFileName[1, '_'], 'DJS')
                    LogTime         = Iconv(LogFileName[Col2() + 1, '_'], 'MT')
                    ProcessID       = LogFileName[Col2() + 1, '_']
                    Sequence        = LogFileName[Col2() + 1, '_']
                    LogType         = LogFileName[Col2() + 1, '_'][1, '.']
                    IncludeLogFile  = True$ ; // Assume True for now.
                    // Initialize the variables that might not be used.
                    Method              = ''
                    HomeURLStatus       = ''
                    APIEndpointError    = ''
                    RemoteAddressTime   = ''
                    Begin Case
                        Case LogType _EQC 'Request'
                            If LogFile[1, 7] _EQC 'Request' then
                                GoSub ParseRequestLog
                            end else
                                // If a request encounters a runtime error, it will still produce a request log with
                                // a description of the error. However, this will also be captured in the Aborted Log
                                // so just ignore this redundant log.
                                IncludeLogFile  = False$
                            end
                        Case LogType _EQC 'Response'
                            GoSub ParseResponseLog
                        Case LogType _EQC 'Aborted'
                            GoSub ParseAbortedLog
                        Case LogType _EQC 'Debugger'
                            GoSub ParseDebuggerLog
                        Case LogType _EQC 'GetStatus'
                            GoSub ParseGetStatusLog
                    End Case
                end
                // If the log row is flagged to be included, update the counter and use the AppendLog service to update
                // the log index.
                If IncludeLogFile EQ True$ then
                    LogCount       += 1
                    If Mod(LogCount, 100) EQ 0 then
                        Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', LogCount : ' Logs Found')
                        Yield(); Yield(); Yield(); Yield()
                        Utility('CURSOR', 'H')
                    end
                    objLog          = Logging_Services('NewLog', LogCapturePath, Oconv(LogDate, 'DJS-') : '_Index.log', CRLF$, TAB$, '', '', False$, False$)
                    LogRow          = LogDate : @FM : LogTime : @FM : ProcessID : @FM : Sequence : @FM : LogType : @FM : Method : @FM : HomeURLStatus : @FM : APIEndpointError : @FM : RemoteAddressTime : @FM : 'View...' : @FM : LogFilePath
                    Logging_Services('AppendLog', objLog, LogRow, @RM, @FM, True$)
                end
            end
        Next LogFileName
        Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', LogCount : ' Logs Found')
    end else
        Set_Property(@Window : '.EDL_LOG_COUNT', 'INVALUE', '0 Logs Found')
    end

return


GetLogFiles:

    LogFiles        = ''
    // Check first to see if a valid log capture path has already been identified.
    LogCapturePath  = Get_Property(@Window, '@LOG_CAPTURE_PATH')
    // If the log capture path has not been identified, allow the user to pick one.
    If LogCapturePath EQ '' then
        // Default to the configured log capture folder.
        LogCapturePath  = HTTP_Services('GetCapturePath')
        LogCapturePath  = RTI_OS_Directory('CHOOSE', LogCapturePath, 'Path to the HTTP log files.')
    end
    If LogCapturePath NE '' then
        InitPath    = SRP_Path('Combine', LogCapturePath, '*.log')
        Utility('CURSOR', 'H')
        InitDir InitPath
        LogFiles    = DirList()
        If LogFiles EQ '' then
            // The log files may have been relocated or this is running from a different machine than the OEngineServer is
            // running from. Allow the user an opportunity to pick a different directory.
            MsgStruct               = ''
            MsgStruct<MTEXT$>       = 'No logs could be found at this location:' : @TM : @TM : LogCapturePath : @TM : @TM : 'Would you like to select another path?'
            MsgStruct<MTYPE$>       = 'BNY'
            MsgStruct<MICON$>       = '?'
            MsgStruct<MDEFBTN$>     = 2
            MsgStruct<MJUST$>       = 'L'
            MsgStruct<MCAPTION$>    = 'HTTP Logs'
            MsgStruct<MFONT$>       = 'Segoe UI' : @SVM : -12 : @SVM : 400 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 34 : @SVM : 0 : @SVM : 3 : @SVM : 2 : @SVM : 1 : @SVM : 0 : @SVM : 0 : @SVM : 0 : @SVM : 0
            PickAnother             = Msg(@Window, MsgStruct)
            If PickAnother EQ True$ then
                LogCapturePath  = RTI_OS_Directory('CHOOSE', '', 'Path to the HTTP log files.')
                InitPath        = SRP_Path('Combine', LogCapturePath, '*.log')
                Utility('CURSOR', 'H')
                InitDir InitPath
                LogFiles        = DirList()
                If LogFiles NE '' then
                    Set_Property(@Window, '@LOG_CAPTURE_PATH', LogCapturePath)
                end
            end
        end else
            Set_Property(@Window, '@LOG_CAPTURE_PATH', LogCapturePath)
        end
    end

return


ApplyFilters:

    FilterLogList           = ''
    FilterRecordColors      = ''
    FilterLogCount          = 0
    FilterStartDate         = Get_Property(@Window : '.EDL_START_DATE', 'INVALUE')
    FilterEndDate           = Get_Property(@Window : '.EDL_END_DATE', 'INVALUE')
    FilterStartTime         = Get_Property(@Window : '.EDL_START_TIME', 'INVALUE')
    FilterEndTime           = Get_Property(@Window : '.EDL_END_TIME', 'INVALUE')

    If FilterStartDate : FilterEndDate : FilterStartTime : FilterEndTime NE '' then
        // At least one filter has been set.
        For Each Log in FullLogList using @FM setting LogPos
            LogDate         = Log[1, @VM]
            LogTime         = Log[Col2() + 1, @VM]
            SearchText      = Log[Col2() + 1, @FM]
            IncludeLogFile  = True$ ; // Assume True for now.
            If FilterStartDate NE '' then
                If LogDate LT FilterStartDate then
                    // Date of the log is before the filter start date.
                    IncludeLogFile = False$
                end else
                    If (LogDate EQ FilterStartDate) AND (FilterStartTime NE '') then
                        // Date of the log is the same as the filter start date. Check to see if this date also has
                        // a filter start time.
                        If LogTime LT FilterStartTime then
                            // Time of the log is before the filter start time.
                            IncludeLogFile = False$
                        end
                    end
                end
            end
            If FilterEndDate NE '' then
                If LogDate GT FilterEndDate then
                    // Date of the log is after the filter end date.
                    IncludeLogFile = False$
                end else
                    If (LogDate EQ FilterEndDate) AND (FilterEndTime NE '') then
                        // Date of the log is the same as the filter end date. Check to see if this date also has
                        // a filter end time.
                        If LogTime GT FilterEndTime then
                            // Time of the log is after the filter end time.
                            IncludeLogFile = False$
                        end
                    end
                end
            end
            If IncludeLogFile EQ True$ then
                FilterLogList      := Log : @FM
                FilterRecordColors := RecordColors<LogPos> : @FM
                FilterLogCount     += 1
            end
        Next Log
        FilterLogList[-1, 1]        = ''
        FilterRecordColors[-1, 1]   = ''
        Transfer FilterLogList to LogList
        Transfer FilterRecordColors to RecordColors
        Transfer FilterLogCount to LogCount
    end else
        Transfer FullLogList to LogList
    end

return


FindMatch:

    MatchSearchText = Get_Property(@Window : '.EDL_SEARCH_TEXT', 'INVALUE')
    MatchFound      = False$    ; // Assume false for now.

    If MatchSearchText NE '' then
        CurrentRow      = Get_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.SelPos')
        CurrentLogList  = Get_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.List')
        LogListCount    = DCount(CurrentLogList, @FM)
        If Direction EQ PREVIOUS$ then
            // Search is going backwards. In order to use the same search logic as forward searching, the current
            // log list will be sorted in reverse. This will place all new searches into the remaining list of logs.
            LenLogList  = Len(CurrentLogList)
            TempLogList = Str(\00\, LenLogList)
            NewPos      = LenLogList + 1
            Pos         = 1
            Loop
                Curr                            = CurrentLogList[Pos, @FM]
                LenCurr                         = Len(Curr)
                NewPos                         -= LenCurr
                TempLogList[NewPos, LenCurr]    = Curr
                Pos                            += LenCurr + 1
            While Pos LT LenLogList
                NewPos                         -= 1
                TempLogList[NewPos, 1]          = @FM
            Repeat
            Transfer TempLogList to CurrentLogList
            CurrentRow  = LogListCount - CurrentRow + 1
        end
        PastList            = Field(CurrentLogList, @FM, 1, CurrentRow)
        NewList             = CurrentLogList[Col2() + 1, @RM]
        PastListRowCount    = DCount(PastList, @FM)
        If PastList : NewList NE '' then
            // Search for the string in the new log list.
            If NewList NE '' then
                For Each Log in NewList using @FM setting LogPos
                    LogDate         = Log[1, @VM]
                    LogTime         = Log[Col2() + 1, @VM]
                    SearchText      = Log[Col2() + 1, @FM]
                    SearchText      = Delete(SearchText, 0, 8, 0)
                    If IndexC(SearchText, MatchSearchText, 1) then
                        MatchFound  = True$
                    end
                Until MatchFound
                Next Log
            end

            If MatchFound EQ True$ then
                // The true log position is the total logs in the new list and the position in the past list.
                LogPos += PastListRowCount
            end else
                // No match found in the new log list. Search the past log list before giving up. This will
                // allow a search result to circle back.
                If PastList NE '' then
                    For Each Log in PastList using @FM setting LogPos
                        LogDate         = Log[1, @VM]
                        LogTime         = Log[Col2() + 1, @VM]
                        SearchText      = Log[Col2() + 1, @FM]
                        SearchText      = Delete(SearchText, 0, 8, 0)
                        If IndexC(SearchText, MatchSearchText, 1) then
                            MatchFound  = True$
                        end
                    Until MatchFound
                    Next Log
                end
            end
        end
    end

    If MatchFound EQ True$ then
        If Direction EQ PREVIOUS$ then
            // Since the match was found in a reverse sorted log list, the true log position will need to be
            // calculated.
            LogPos  = LogListCount - LogPos + 1
        end
        Set_Property(@Window : '.OLE_RPT_LOGTABLE', 'OLE.SelPos', LogPos)
    end

return


ParseRequestLog:

    StartChar           = Index(LogFile, 'HTTPRequestMethod', 1)
    Method              = Trim(LogFile[StartChar, 'F' : CR$])
    Method              = Trim(Method[-1, 'B:'])
    StartChar           = Index(LogFile, 'HTTPServerName', 1)
    ServerName          = Trim(LogFile[StartChar, 'F' : CR$])
    ServerName          = Trim(ServerName[-1, 'B:'])
    HomeURLStatus       = ServerName
    StartChar           = Index(LogFile, 'HTTPPathInfo', 1)
    PathInfo            = Trim(LogFile[StartChar, 'F' : CR$])
    PathInfo            = Trim(PathInfo[-1, 'B:'])
    StartChar           = Index(LogFile, 'HTTPRemoteAddr', 1)
    RemoteAddressTime   = Trim(LogFile[StartChar, 'F' : CR$])
    RemoteAddressTime   = Trim(RemoteAddressTime[-1, 'B:'])
    If PathInfo NE '' then
        If PathInfo[1, 1] NE '/' then PathInfo = '/' : PathInfo
    end
    APIEndpointError    = HTTP_Services('GetAPIRootURL', False$) : PathInfo

return


ParseResponseLog:

    StartChar           = Index(LogFile, 'Time to Execute  :', 1)
    RemoteAddressTime   = Trim(LogFile[StartChar, 'F' : CR$])
    RemoteAddressTime   = Trim(RemoteAddressTime[-1, 'B:'])
    StartChar   = Index(LogFile, 'OECGI21', 1)
    If StartChar NE 0 then
        // Response is binary. Decode the body first.
        ResponseBody    = Trim(LogFile[StartChar, 'F' : CR$])
        ResponseBody    = SRP_Decode(ResponseBody, 'HEX')
        Transfer ResponseBody to LogFile
    end
    StartChar           = Index(LogFile, 'Status:', 1)
    Status              = Trim(LogFile[StartChar, 'F' : CR$])
    Status              = Trim(Status[-1, 'B:'])
    HomeURLStatus       = Status[1, 'F ']
    APIEndpointError    = Status[Col2() + 1, 999]

return


ParseAbortedLog:

    Status              = LogFile[-1, 'B' : \0A\]
    HomeURLStatus       = Status[1, 'F:']
    APIEndpointError    = Trim(Status[Col2() + 1, 999])

return


ParseDebuggerLog:

    If SRP_JSON(objLog, 'Parse', LogFile) EQ '' then
        SPStatCode          = SRP_JSON(objLog, 'GetValue', 'SPStatCode')
        HomeURLStatus       = SPStatCode[1, 'F:']
        APIEndpointError    = Trim(SPStatCode[Col2() + 1, 'F:'])
        SRP_JSON(objLog, 'Release')
    end

return


ParseGetStatusLog:

    Status              = Trim(LogFile[1, 'F' : CR$])
    Status              = Trim(Status[-1, 'B:'])
    HomeURLStatus       = Trim(Status[1, 'F-'])
    APIEndpointError    = Trim(Status[Col2() + 1, 999])

return
