//=-- DailySummaryPlugin.cpp - Provides a summary of the days hits -----------=
//
//  PLUGIN DESCRIPTION:
//    The DailySummaryPlugin keeps track of the number of errors, the number
//    of hits to a web site, the percentage of hits on the server to this site
//    and a number of other generally userful stats.
//
//  PLUGIN AUTHOR:
//    Chris Lattner, Author of Magicstats  (sabre@magic.hurrah.com)
//
//  PLUGIN HOMEPAGE:
//     http://magic.hurrah.com/~sabre/MagicStats/
//
//  PLUGIN VERSION / COMPATIBILITY:
//     DailySummaryPlugin v1.0 / MagicStats 2.0+
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-1998 Chris Lattner
//=---------------------------------------------------------------------------=

#include "DailySummaryPlugin.h"
#include "FilterManager.h"
#include "Serialize.h"
#include "Table.h"

INIT_PLUGIN(DailySummaryPlugin);

DailySummaryPlugin::DailySummaryPlugin(int &CE) {
  Flags = FClear;
  ResetState();            // Reset variables
  CE = 0;
}

void DailySummaryPlugin::LoadSettings(VarTable *List) {
  
  // Load the SectionFilter...
  SectionFilter = FilterManager::GetFilter(List, "SectionFilter");
}

// The full name consists of all of the data settings that alter the data 
// that is kept track of and stored.
//
void DailySummaryPlugin::GetFullName(VarTable &Params, String &FullName, 
                                     int UpdateFreq) {
  FullName = String::IntToStr(UpdateFreq) + Name + String('\t');

  String Filt;
  if (Params["Filter"] != 0) {
    Filt = Params["Filter"]->GetStringValue();
  } else {
    Filt = DefFilter.ToParamStr();
  }

  FullName += Filt + String('\t');

  if (Params["SectionFilter"] != 0) {
    Filt = Params["SectionFilter"]->GetStringValue();
  } else {
    Filt = SectionFilter.ToParamStr();
  }
  FullName += Filt;
}

void DailySummaryPlugin::ProcessAccess(AccessFormatPlugin &A) {
  if (FirstAccess) {
    FirstDate = A.GetDate();
    FirstAccess = 0;
  }

  LastDate = A.GetDate();

  if (A.GetStatusCode() >= 400) {         // Catch Errors First
    if (SectionFilter.Match(A)) 
      NumErrors++;
    return;
  }
  
  NumServerHits++;

  if (SectionFilter.Match(A)) {
    BytesTransfered += A.GetLength();
    NumSectionHits++;
  }

  if (DefFilter.Match(A))
    NumHits++;
}

static String FileSize(int Bytes) {
  String Unit;
  double f = Bytes;

  if (Bytes < 1024*2) {
    Unit = " byte";
  } else if (Bytes < 1024*1024*2) {
    f = f / 1024;
    Unit = " kilobyte";
  } else if (Bytes < 1024*1024*1023*2) {
    f = f / 1024 / 1024;
    Unit = " megabyte";
  } else {
    f = f / 1024 / 1024 / 1024;
    Unit = " gigabyte";
  }

  if (f != 1) Unit += "s";
  
  return String::DoubleToStr(f, 4) + Unit;
}

static String Plural(int Exp, const char *NonPlural, const char *Plural = 0) {
  if (Exp == 1) {
    return NonPlural;
  } else {
    if (Plural != 0) 
      return Plural;
  }
  return String(NonPlural) + "s";
}

static inline int Divide(int a, double b, int Default) {
  return (int)((b != 0.0) ? a/b : Default);
}

void DailySummaryPlugin::OutputHTML(ostream &O, VarTable &Params) {
  double ElapsedTime = 0, Forecast = 0;

  String WebPageName, TimePeriod, Unit;
  if (Params["PageName"]) 
    WebPageName = Params["PageName"]->GetStringValue();
  else
    WebPageName = "Your Web Page";

  switch (UpdateFreq) {
  case Plugin::UpdateDaily:
    ElapsedTime = LastDate.ElapsedHours(FirstDate);
    Forecast   = Divide(24*NumHits, ElapsedTime, 0);
    TimePeriod = "today";
    Unit       = "hour";
    break;
  case Plugin::UpdateWeekly:
    ElapsedTime = LastDate.ElapsedDays(FirstDate);
    Forecast   = Divide(7*NumHits, ElapsedTime, 0);
    TimePeriod = "this week";
    Unit       = "day";
    break;
  case Plugin::UpdateMonthly:
    ElapsedTime = LastDate.ElapsedDays(FirstDate);
    Forecast   = Divide(30*NumHits, ElapsedTime, 0);
    TimePeriod = "this month";
    Unit       = "day";
    break;
  case Plugin::UpdateYearly:
    ElapsedTime = LastDate.ElapsedMonths(FirstDate);
    Forecast   = Divide(12*NumHits, ElapsedTime, 0);
    TimePeriod = "this year";
    Unit       = "month";
    break;
  }

  O << "There have been a total of <b>" << NumHits << "</b> " 
    << Plural(NumHits, "hit") << " and <b>\n" << NumErrors 
    << "</b> " << Plural(NumErrors, "error") << " related to <b>\n"
    << WebPageName << "</b>. This accounts for <b>" 
    << Divide(NumSectionHits*100, NumServerHits, 0) << "</b>% \n of total "
    << "server hits, and consists of <b>" << FileSize(BytesTransfered) 
    << "</b> of bandwidth.\n";

  O << " There has been an average of <b>" 
    << Divide(NumHits, ElapsedTime, NumHits) << endl
    << "</b> accesses per " << Unit << ", and at this rate, <b>" 
    << WebPageName << endl << "</b> will get <b>" << Forecast
    << "</b> accesses " << TimePeriod << ".<p>\n";
}

void DailySummaryPlugin::ResetState() {
  FirstAccess = 1;
  NumHits = NumServerHits = NumSectionHits = NumErrors = 0;
  BytesTransfered = 0;
}

void DailySummaryPlugin::LoadState(Serialize &I) {
  unsigned int Version = 0;

  I >> Version;

  // Check version number...
  switch (Version) {
  case 100:                  // Version 1.00
    I >> FirstAccess 
      >> NumHits
      >> NumErrors
      >> NumSectionHits
      >> NumServerHits 
      >> BytesTransfered;
    
    I >> FirstDate >> LastDate;

    break;
    
  default:
    cout << "Serialized " << Name << " Page plugin is of a version that cannot "
         << "be deserialized\nwith this version of the plugin.  Reseting state.\n";
    break;
  }

}

void DailySummaryPlugin::SaveState(Serialize &O) {
  O << GetCurrentVersion();  // Serialize the Version number

  O << FirstAccess
    << NumHits
    << NumErrors
    << NumSectionHits
    << NumServerHits
    << BytesTransfered;
  
  O << FirstDate << LastDate;
}


