//////////////////////////////////////////////////////////////////////
// Last online scanner -- see when given avatars were last online
// By Antony Fairport.
//
// Revision history.
// =================
//
// 2011-05-07
// Initial version.

//////////////////////////////////////////////////////////////////////
// Constants.
float  REFRESH_INTERVAL = 10.0;
string KEY_FILE         = "Keys";

//////////////////////////////////////////////////////////////////////
// Globals.
integer g_iMaxTracking;
integer g_iCurrent;
list    g_lTracking;
list    g_lUserNames;
list    g_lDisplayNames;
list    g_lLastSeen;
key     g_keyRequest;
key     g_keyKey;
integer g_iKey;

//////////////////////////////////////////////////////////////////////
// Return a string that is the report of last online times.
string LastOnlineReport()
{
    string s = "";
    integer i;

    for ( i = 0; i < g_iMaxTracking; i++ )
    {
        string sUN   = llList2String( g_lUserNames, i );
        string sDN   = llList2String( g_lDisplayNames, i );
        string sName = sDN;
        
        if ( sUN != sDN )
        {
            sName = sDN + " (" + sUN + ")";
        }
    
        s += sName + " : " + llList2String( g_lLastSeen, i ) + "\n";
    }
    
    return s;
}

//////////////////////////////////////////////////////////////////////
// Format a llGetTimestamp()
string FormatTime( string sTime )
{
    list l = llParseString2List( sTime, [ "T", "." ], [] );
    
    return llList2String( l, 0 ) + " " + llList2String( l, 1 ) + " UTC";
}

//////////////////////////////////////////////////////////////////////
// Default state.
default
{
    //////////////////////////////////////////////////////////////////
    // State entry.
    state_entry()
    {
        // Simply jump to the setup state.
        state Setup;
    }
}

//////////////////////////////////////////////////////////////////////
// Setup state.
state Setup
{
    //////////////////////////////////////////////////////////////////
    // State entry.
    state_entry()
    {
        // Let the user know what's happening.
        llSetText( "Reading list of keys to track, please wait...", < 1.0, 0.0, 0.0 >, 1.0 );

        // Clear out the lists and get the tracking list length.        
        g_lTracking     = [];
        g_lUserNames    = [];
        g_lDisplayNames = [];
        g_lLastSeen     = [];
        
        // If we have the keys file...
        if ( llGetInventoryType( KEY_FILE ) == INVENTORY_NOTECARD )
        {
            // Start reading.
            g_keyKey = llGetNotecardLine( KEY_FILE, g_iKey = 0 );
        }
    }
    
    //////////////////////////////////////////////////////////////////
    // Handle data server responses.
    dataserver( key queryid, string data )
    {
        // If this is our query...
        if ( queryid == g_keyKey )
        {
            // If this isn't the end of the file...
            if ( data != EOF )
            {
                // If the data looks like a key...
                if ( ( data != "" ) && ( ( (key) data ) != NULL_KEY ) )
                {
                    // Extend the lists.
                    g_lTracking     += [ data ];
                    g_lUserNames    += [ "" ];
                    g_lDisplayNames += [ "" ];
                    g_lLastSeen     += [ "No login" ];
                }
                
                // Next key...
                g_keyKey = llGetNotecardLine( KEY_FILE, ++g_iKey );
            }
            else
            {
                // Record how many we're tracking.
                g_iMaxTracking  = llGetListLength( g_lTracking );

                // Start by getting user names...
                state SetupUserNames;        
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////
// User name setup state.
state SetupUserNames
{
    //////////////////////////////////////////////////////////////////
    // State entry.
    state_entry()
    {
        // Let the user know what's happening.
        llSetText( "Getting user names, plase wait...", < 1.0, 0.0, 0.0 >, 1.0 );
        
        // Starting with the first entry in the tracking list...
        g_iCurrent = 0;
        
        // Start getting user names...
        g_keyRequest = llRequestAgentData( llList2Key( g_lTracking, g_iCurrent ), DATA_NAME );
    }
    
    //////////////////////////////////////////////////////////////////
    // Handle data server responses.
    dataserver( key queryid, string data )
    {
        // If this is our query...
        if ( queryid == g_keyRequest )
        {
            // Put the name into the list.
            g_lUserNames = llListReplaceList( g_lUserNames, [ data ], g_iCurrent, g_iCurrent );
            
            // Move on.
            g_iCurrent++;
            
            // If we've got some names left to get...
            if ( g_iCurrent < g_iMaxTracking )
            {
                // Get the next name...
                g_keyRequest = llRequestAgentData( llList2Key( g_lTracking, g_iCurrent ), DATA_NAME );
            }
            else
            {
                // Now we get display names.
                state SetupDisplayNames;
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////
// Display name setup state.
state SetupDisplayNames
{
    //////////////////////////////////////////////////////////////////
    // State entry.
    state_entry()
    {
        // Let the user know what's happening.
        llSetText( "Getting display names, please wait...", < 1.0, 0.0, 0.0 >, 1.0 );
        
        // Starting with the first entry in the tracking list...
        g_iCurrent = 0;
        
        // Start getting display names...
        g_keyRequest = llRequestDisplayName( llList2Key( g_lTracking, g_iCurrent ) );
    }
    
    //////////////////////////////////////////////////////////////////
    // Handle data server responses.
    dataserver( key queryid, string data )
    {
        // If this is our query...
        if ( queryid == g_keyRequest )
        {
            // Put the name into the list.
            g_lDisplayNames = llListReplaceList( g_lDisplayNames, [ data ], g_iCurrent, g_iCurrent );
            
            // Move on.
            g_iCurrent++;
            
            // If we've got some names left to get...
            if ( g_iCurrent < g_iMaxTracking )
            {
                // Get the next name...
                g_keyRequest = llRequestDisplayName( llList2Key( g_lTracking, g_iCurrent ) );
            }
            else
            {
                llSetText( "Starting online scanner, please wait...", < 0.0, 1.0, 0.0 >, 1.0 );
                
                // Now we can start online scanning.
                state OnlineScanner;
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////
// The online scanner state.
state OnlineScanner
{
    //////////////////////////////////////////////////////////////////
    // State entry.
    state_entry()
    {
        // Starting with the first entry in the tracking list...
        g_iCurrent = 0;
        
        // Start getting the online status.
        g_keyRequest = llRequestAgentData( llList2Key( g_lTracking, g_iCurrent ), DATA_ONLINE );
    }

    //////////////////////////////////////////////////////////////////
    // Handle data server responses.
    dataserver( key queryid, string data )
    {
        // If this is our query...
        if ( queryid == g_keyRequest )
        {
            // If the avatar is online...
            if ( data == "1" )
            {
                // Update their last seen value.
                g_lLastSeen = llListReplaceList( g_lLastSeen, [ FormatTime( llGetTimestamp() ) ], g_iCurrent, g_iCurrent );
            }
            
            // Move on.
            g_iCurrent++;
            
            // If we've got some avatars left to check...
            if ( g_iCurrent < g_iMaxTracking )
            {
                // Get the next status...
                g_keyRequest = llRequestAgentData( llList2Key( g_lTracking, g_iCurrent ), DATA_ONLINE );
            }
            else
            {
                // Update the display.
                llSetText( LastOnlineReport(), < 1.0, 1.0, 1.0 >, 1.0 );
                
                // Pause for a while.
                state ScannerPause;
            }
        }
    }
    
    //////////////////////////////////////////////////////////////////
    // Look for changes.
    changed( integer change )
    {
        // Did the inventory of the object change?
        if ( change & CHANGED_INVENTORY )
        {
            // Yes. Most likely the key list was changed. Start over.
            llResetScript();
        }
    }
}

//////////////////////////////////////////////////////////////////////
// Scan pause state.
state ScannerPause
{
    //////////////////////////////////////////////////////////////////
    // State entry.
    state_entry()
    {
        // Set up the timer for the pause.
        llSetTimerEvent( REFRESH_INTERVAL );
    }
    
    //////////////////////////////////////////////////////////////////
    // Timer event.
    timer()
    {
        // Kill the timer.
        llSetTimerEvent( 0.0 );
        
        // Go back and scan again.
        state OnlineScanner;
    }

    //////////////////////////////////////////////////////////////////
    // Look for changes.
    changed( integer change )
    {
        // Did the inventory of the object change?
        if ( change & CHANGED_INVENTORY )
        {
            // Yes. Most likely the key list was changed. Start over.
            llResetScript();
        }
    }
}