//================================================ file = idleCollect.c =====
//=  Program to collect idle time data for a Windows PC                     =
//=   - Logs idle/busy/sleep by minute                                      =
//===========================================================================
//=  Notes:                                                                 =
//=    1) Output is to OUT_FILE                                             =
//=    2) Single command-line argument is number of hours to monitor for    =
//=-------------------------------------------------------------------------=
//= Example execution (data from OUT_FILE):                                 =
//=                                                                         =
//=    ----------------------------------------------------------           =
//=    - This is the output file from idleCollect.  Data is     -           =
//=    - collected once per minute. Output data is '0' for no   -           =
//=    - user keyboard or mouse activity in the last minute,    -           =
//=    - '1' for activity, and 'X' for system in sleep mode.    -           =
//=    ----------------------------------------------------------           =
//=    Collecting data for 1 hours                                          =
//=    Program start time         = Sun Jul 15 22:36:32 2007                =
//=    Data collection start time = Sun Jul 15 23:00:00 2007                =
//=    ----------------------------------------------------------           =
//=    11111111111111111111111111XXXXXXXXX10000011XXXXXX11111111111 : Mon   =
//=    Jul 16 00:00:00 2007                                                 =
//=-------------------------------------------------------------------------=
//=  Build: bcc32 idleCollect.c (must use Borland 5.5, 5.2 will not work)   =
//=-------------------------------------------------------------------------=
//=  Execute: idleCollect number_of_days                                    =
//=-------------------------------------------------------------------------=
//=  Author: Ken Christensen                                                =
//=          University of South Florida                                    =
//=          WWW: http://www.csee.usf.edu/~christen                         =
//=          Email: christen@csee.usf.edu                                   =
//=-------------------------------------------------------------------------=
//=  History: KJC (07/22/07) - Genesis                                      =
//=           KJC (02/18/08) - Minor clean-up                               =
//===========================================================================
//----- Include files -------------------------------------------------------
#include <stdio.h>                      // Needed for printf()
#include <stdlib.h>                     // Needed for atoi()
#include <time.h>                       // Needed for time stuff
#include <windows.h>                    // Needed for Sleep()

//----- Defines -------------------------------------------------------------
#define     FALSE                 0     // Boolean false
#define      TRUE                 1     // Boolean true
#define  OUT_FILE     "idleData.txt"    // Output file name

//----- Defines -------------------------------------------------------------
int      NumHoursMonitor;               // Number of hours to monitor for

//----- Function prototypes -------------------------------------------------
int  testBusy(void);
void sleepTime(time_t timeNow, time_t timeLast, int *sampleCount, FILE *fp);

//===========================================================================
//=  Main program                                                           =
//===========================================================================
int main(int argc, char *argv[])
{
  FILE   *fp;                  // File pointer for output file
  struct tm *tblock;           // Structure for time block
  time_t timeNow;              // Current sample time
  time_t timeLast;             // Last sample time
  int    sample;               // Busy/idle sample
  int    sampleCount;          // Counter for samples
  int    i;                    // Loop counter

  // Grab the command line argument for number of hours to monitor for
  if (argc == 2)
  {
    NumHoursMonitor = atoi(argv[1]) * 24;
    if (NumHoursMonitor <= 0)
    {
      printf("*** ERROR - number of days = %d and is invalid  \n",
        atoi(argv[1]));
      return -1;
    }
  }
  else
  {
    printf("Usage: 'idleCollect numHours' where numHours is the number of  \n");
    printf("       hours to monitor for.  Output is in 'idleData.txt'. \n");
    return -1;
  }

  // Open the output file in append mode
  fp = fopen(OUT_FILE, "a");
  if (fp == NULL)
  {
    printf("*** ERROR - fopen() of %s failed \n", OUT_FILE);
    return -1;
  }

  // Output explanatory banner
  fprintf(fp,"---------------------------------------------------------- \n");
  fprintf(fp,"- This is the output file from idleCollect.  Data is     - \n");
  fprintf(fp,"- collected once per minute. Output data is '0' for no   - \n");
  fprintf(fp,"- user keyboard or mouse activity in the last minute,    - \n");
  fprintf(fp,"- '1' for activity, and 'X' for system in sleep mode.    - \n");
  fprintf(fp,"- Output is written to file 'idleData.txt'.              - \n");
  fprintf(fp,"---------------------------------------------------------- \n");
  fprintf(fp,"Collecting data for %d hours \n", NumHoursMonitor);
  fflush(fp);

  // Get and output program start time
  timeNow = time(NULL);
  tblock = localtime(&timeNow);
  fprintf(fp, "Program start time         = %s", asctime(tblock));
  fflush(fp);

  // Synchronize to next top-of-the-hour
  timeNow = time(NULL);
  tblock = localtime(&timeNow);
  while(tblock->tm_min < 59)
  {
    Sleep(500);
    timeNow = time(NULL);
    tblock = localtime(&timeNow);
  }
  while(tblock->tm_min == 59)
  {
    Sleep(10);
    timeNow = time(NULL);
    tblock = localtime(&timeNow);
  }

  // Output data collection start time
  fprintf(fp, "Data collection start time = %s", asctime(tblock));
  fprintf(fp,"---------------------------------------------------------- \n");
  fflush(fp);

  // *** Main loop for data collection ***
  sampleCount = 0;
  timeLast = time(NULL);
  while(NumHoursMonitor > 0)
  {
    // Sleep for 59 seconds
    Sleep(59000);

    // Synchronize to next top-of-the-minute
    timeNow = time(NULL);
    tblock = localtime(&timeNow);
    while (tblock->tm_sec != 0)
    {
      Sleep(10);
      timeNow = time(NULL);
      tblock = localtime(&timeNow);
    }

    // If negative time has passed then something is wrong and we abort
    if (difftime(timeNow, timeLast) < 0.0)
    {
      fprintf(fp, "*** ERROR - negative difftime at = %s \n", asctime(tblock));
      fflush(fp);
      fclose(fp);
      return -1;
    }

    // If more than one minute has passed then we have been sleeping
    if (difftime(timeNow, timeLast) > 60.0)
    {
      sleepTime(timeNow, timeLast, &sampleCount, fp);
      timeLast = timeNow;
      continue;
    }
    timeLast = timeNow;

    // Get busy status, output it, and increment sampleCount
    sample = testBusy();
    fprintf(fp, "%d", sample);
    fflush(fp);
    sampleCount++;

    // After 60 samples clear sampleCount and output timestamp
    if (sampleCount >= 60)
    {
      // Set sampleCount to zero
      sampleCount = 0;

      // Output timestamp
      fprintf(fp, " : %s", asctime(tblock));
      fflush(fp);

      // Decrement NumHoursMonitor
      NumHoursMonitor = NumHoursMonitor - 1;

      // Synchronize to top-of-the-hour if somehow have slipped
      timeNow = time(NULL);
      tblock = localtime(&timeNow);
      while (tblock->tm_min != 0)
      {
        Sleep(100);
        timeNow = time(NULL);
        tblock = localtime(&timeNow);
      }
    }
  }

  // Output completed message, close the file pointer, and return
  fprintf(fp,"--- Monitoring completed --- \n");
  fclose(fp);
  return 1;
}

//===========================================================================
//=  Function to test if busy                                               =
//=   - Returns true if keyboard or mouse activity detected since last test =
//===========================================================================
int testBusy(void)
{
  LASTINPUTINFO   lii;           // Structure for Windows time stuff
  static long int newTicks;      // Time value for new ticks
  static long int lastTicks;     // Time value for last ticks
  int             busyFlag;      // Flag for idle since last test

  // Set size of lii.cbsize
  lii.cbSize = sizeof(lii);

  // Get the time of the most recent (new) user activity
  GetLastInputInfo(&lii);
  newTicks = lii.dwTime;

  // Test if any user activity since last test and set busyFlag accordingly
  if (newTicks > lastTicks)
    busyFlag = TRUE;
  else
    busyFlag = FALSE;

  // Assign lastTicks to newTicks
  lastTicks = newTicks;

  // Return busyFlag
  return(busyFlag);
}

//===========================================================================
//=  Function to handle any time detected as sleeping                       =
//=   - Outputs sleep samples and adjusts sampleCount as needed             =
//===========================================================================
void sleepTime(time_t timeNow, time_t timeLast, int *sampleCount, FILE *fp)
{
  int  numMinutes;             // Calculated number of minutes for sleeping
  int  numHours;               // Calculated number of hours for sleeping
  int  fillCount;              // Calculated number of fill minutes
  int  i, j;                   // Loop counters

  // Determine number of minutes that we have been sleeping
  numMinutes = ((int) difftime(timeNow, timeLast)) / 60;

  // Handle simple case of sleep does not cross an hour boundary
  if ((*sampleCount + numMinutes) < 60)
  {
    // Output numMinutes X's
    for (i=0; i<numMinutes; i++)
      fprintf(fp, "X");
    fflush(fp);

    // Add numMinutes to sampleCount
    *sampleCount = *sampleCount + numMinutes;
  }
  else // Handle case of sleep hits or crosses an hour boundary
  {
    // Determine number of minutes of sleep in this hour
    fillCount = 60 - *sampleCount;

    // Output fillCount X's
    for (i=0; i<fillCount; i++)
      fprintf(fp, "X");
    fprintf(fp," : null \n");
    fflush(fp);

    // Decrement NumHoursMonitor for this hour
    NumHoursMonitor = NumHoursMonitor - 1;

    // Determine number of minutes and hours left after this hour
    numMinutes = numMinutes - fillCount;
    numHours = numMinutes / 60;

    // Output numHours of 60 minute rows of X's
    for (i=0; i<numHours; i++)
    {
      for (j=0; j<60; j++)
        fprintf(fp, "X");
      fprintf(fp," : null \n");
      fflush(fp);

      // Decrement NumHours for each hour row output
      NumHoursMonitor = NumHoursMonitor - 1;
    }

    // Determine number of minutes left in last hour
    numMinutes = numMinutes - (60 * numHours);

    // Output numMinutes X's
    for (i=0; i<numMinutes; i++)
      fprintf(fp, "X");
    fflush(fp);

    // Set sampleCount to numMinutes
    *sampleCount = numMinutes;
  }

  // Return nothing
  return;
}

