//=-- MSEngine.cpp - Main MagicStats engine ----------------------------------=
//
//  PLUGIN DESCRIPTION:
//    This is the main engine class for MagicStats.  MagicStats is capable of
//    having multiple engines, but this plugin defines the most common/only one
//
//  PLUGIN AUTHOR:
//    Chris Lattner, Author of MagicStats (sabre@nondot.org)
//
//  PLUGIN HOMEPAGE:
//     http://www.MagicStats.com/
//
//  PLUGIN VERSION / COMPATIBILITY:
//     MSEngine v2.0 / MagicStats 2.0+
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner
//=---------------------------------------------------------------------------=

#include "MSEngine.h"
#include "Options.h"
#include "Serialize.h"
#include "PlatformAbstraction.h"

//#include <sys/time.h>
//#include <unistd.h>

INIT_PLUGIN(MSEngine);

MSEngine::MSEngine(int &CE) {
  CE = AccLogMgr.init();
  FirstRun = 1;
}

void MSEngine::LoadState(Serialize &I) {
  I >> LastAccessDate;
  AccLogMgr.LoadState(I);
  FirstRun = 0;
}

void MSEngine::SaveState(Serialize &O) {
  O << LastAccessDate;
  AccLogMgr.SaveState(O);
}

int MSEngine::RunEngine() {
  double StartTime = 0.0;
  volatile int StopProcessing = 0;

  PlatformAbstraction::InstallInterruptHandler(&StopProcessing);

  if (MSOptions::Verbose) {  // Record high precision starting time...
    StartTime = PlatformAbstraction::GetSystemTime();
  }

  if (MSOptions::Debug) 
    cout << "Processing Accesses... \n";

  while (1) {
    // Read the access from the access log...
    AccessFormatPlugin *CurAccess = AccLogMgr.ReadAccess();
    if (CurAccess == 0) break; // Last access in logs?
    if (StopProcessing) {  // CTRL-C Pressed?
      cout << "Interrupt character detected... halting hit processing...\n";
      break;
    }

    if (FirstRun) {    // The first access read is the first one in the log file
      LastAccessDate = CurAccess->GetDate();
      FirstRun = 0;
    }

    // If the current day changes...
    if (CurAccess->GetDate() < LastAccessDate) {
      cout << "DATE REGRESSION DETECTED!: LAD = " << LastAccessDate << endl;
      cout << "CurAccessDate = " << CurAccess->GetDate() 
           << "  CA = ";
      CurAccess->print();
      cout << endl;
      continue;     // Discard access...
      
    } else if (CurAccess->GetDate() > LastAccessDate) {
      Date TrailerDate(LastAccessDate);

      // Loop through the days...  it possible that they didn't get any hits 
      // on a particular day, so make sure a blank page gets generated.
      while (CurAccess->GetDate() > LastAccessDate) {
        LastAccessDate = LastAccessDate.Tomorrow();
        OutputMgr.WritePage(TrailerDate, Plugin::UpdateDaily);

        if (LastAccessDate.GetDayOfWeek() == 0)  // Weekly rollover
          OutputMgr.WritePage(TrailerDate, Plugin::UpdateWeekly);
        
        if (TrailerDate.GetMonth() != LastAccessDate.GetMonth())
          OutputMgr.WritePage(TrailerDate, Plugin::UpdateMonthly);
        
        if (TrailerDate.GetYear() != LastAccessDate.GetYear())
          OutputMgr.WritePage(TrailerDate, Plugin::UpdateYearly);
        
        TrailerDate = LastAccessDate;
      }
    }

    // Deal with the access...
    PluginManager::ProcessAccess(*CurAccess);

    // Keep track of wraparound...
    LastAccessDate = CurAccess->GetDate();

    // Display progress...
    if (MSOptions::Progress && (AccLogMgr.GetNumAccesses() % 100 == 0)) 
      cout << "#" << flush;
  }

  // Check for no accesses... to avoid generating HTML pages we don't want
  if (AccLogMgr.GetNumAccesses() == 0) {
    if (MSOptions::Verbose)
      cout << "No new accesses to process.\n";
    return 0;
  }

  OutputMgr.WritePage(LastAccessDate, Plugin::UpdateAll);

  if (MSOptions::Verbose) {
    double NewTime = PlatformAbstraction::GetSystemTime();

    int NumAccesses = AccLogMgr.GetNumAccesses();

    cout << "Processed " << NumAccesses << " accesses";

    if (NewTime != 0.0 && StartTime != 0.0) {
      double Elapsed = NewTime - StartTime+0.000001;
      double NumPerSec = NumAccesses/Elapsed;

      cout << " in "
           << (int)Elapsed << "." << (((int)Elapsed*1000) % 1000) 
           << " seconds: "
           << (int)NumPerSec << "." << (((int)NumPerSec*100) % 100)
           << " Accesses/Second\n";
    }
    cout << endl;
  }

  return 0;
}

