//=-- PPPathBreakout.cpp - Path Breakout Usage Page Plugin... ----------------=
//
//  PLUGIN DESCRIPTION:
//    This plugin 
//
//  PLUGIN AUTHOR:
//    Chris Lattner, Author of Magicstats  (sabre@magic.hurrah.com)
//
//  PLUGIN HOMEPAGE:
//     http://www.nondot.org/MagicStats/Plugins/PathBreakout/
//
//  PLUGIN VERSION / COMPATIBILITY:
//     PathBreakout v1.0 / MagicStats 2.0+
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner and stuff.
//=---------------------------------------------------------------------------=

#include "PagePlugin.h"
#include "SerializeEx.h"
#include "Table.h"
#include "PPPathBreakout.h"
#include "BreakoutRenderer.h"

INIT_PLUGIN(PPPathBreakout);

PPPathBreakout::PPPathBreakout(int &CE) { 
  Flags = FAutoFilter | FIgnoreErrors;
  ResetState();
  CE = 0;
}

// The full name consists of all of the data settings that alter the data that
// is kept track of and stored.
void PPPathBreakout::GetFullName(VarTable &Params, String &fullName, 
                                 int updateFreq) {
  String Filt;
  if (Params["Filter"] != 0) {
    Filt = Params["Filter"]->GetStringValue();
  } else {
    Filt = DefFilter.ToParamStr();
  }

  fullName = String::IntToStr(updateFreq) + Name + Filt;
}

void PPPathBreakout::ProcessAccess(AccessFormatPlugin &A) {
  int URLMeat = 0;
  String URL = A.GetURL();
  TotalHits++;
  TotalBW += A.GetLength();

  while (URL[URL.Length()-1] == '/' && URL.Length() > 1)
    URL.Left(URL.Length()-1);  // Strip off trailing /'s

  if (!URL.nCompare("http://", 7))
    URLMeat = 7;

  // Keep track of Total hits and bandwidth
  while (URL.Length() > URLMeat) {
    HitBWRec &R = CountTable[URL];
    R.P++;
    R.S += A.GetLength();
  
    int Idx = URL.strrchr('/');
    URL.Left(Idx > 0 ? Idx : 0);        
  }
}

void PPPathBreakout::ResetState() {
  CountTable.Clear();        // Free all of the data in the linked list...
  CountTable.SetDefault(HitBWRec(0, 0));
  TotalBW = TotalHits = 0;
}

void PPPathBreakout::OutputHTML(ostream &O, VarTable &Params) {
  BreakoutRenderer Renderer(O, Params, Descriptor);
  int CutOff = 3, Percent = 1;   // Default to 3%

  if (Params.InTable("CutoffValue")) {
    String Val = Params["CutoffValue"]->GetStringValue();
    if (Val.Length() > 0) {
      CutOff = Val.atoi();

      if (Val[Val.Length()-1] != '%')   // Not Percentage?
        Percent = 0;
    }
  }

  // Set up the Caption Field...
  LinkedList<String> Caption;
  Caption.AddToTail(String("URL (Top ") + 
                    String::IntToStr(CutOff) + (Percent ? "%):" : " hits):"));
  Caption.AddToTail("Percent:");
  Caption.AddToTail("Hits:");
  Caption.AddToTail("B/W:");

  if (Percent)    // When converting to percentage, ROUND UP.
    CutOff *= (TotalHits+99) / 100;

  // Copy the data from the table into the linked list...
  LinkedList<ListPair> List;
  CountTable.ConvertToList(List);
  
  List.Sort();  // Sort the data by URL.
  
  // Only output top 3%...
  LinkedList<ListPair>::Iterator I = List.GetIterator();
  while (I) {
    if (I->S.P < CutOff)
      I.Remove();  // Advances pointer...
    else
      I++;
  }
  
  BreakoutRenderer::Tree TheTree;  // LinkedList of TreeRows

  I = List.GetIterator();
  while (I) {
    LinkedList<String> Fields;

    String &URL = I->P;
    int SlashIdx = URL.strrchr('/');
    String TopDir = URL;  TopDir.Right(TopDir.Length()-SlashIdx-1);
    if (URL.Length() == 1) TopDir = URL;
    
    int Depth = BreakoutRenderer::CalculateURLDepth(URL);
    
    Fields.AddToTail(String("<a href='") + URL + "'>" + TopDir + "</a>");
    Fields.AddToTail(String::DoubleToStr(1000*I->S.P/TotalHits/10.0,1)+"%");
    Fields.AddToTail(String::IntToStr(I->S.P));
    Fields.AddToTail(String::IntToStr(I->S.S));
    TheTree.AddToTail(DataPair<int,LinkedList<String> >(Depth, Fields));
    
    I++;
  }

  LinkedList<String> ColumnSettings;
  ColumnSettings.AddToTail("");
  ColumnSettings.AddToTail("align=right");
  ColumnSettings.AddToTail("align=right");
  ColumnSettings.AddToTail("align=right");

  Renderer.OutputBreakout(Caption, TheTree, ColumnSettings);
}

void PPPathBreakout::LoadState(Serialize &I) {
  unsigned int Version = 0;
  String Page;
  
  ResetState();
  I >> Version;
  
  // Check version number...
  switch (Version) {
  case 100:                  // Version 1.00
    I >> TotalHits >> TotalBW;
    I >> CountTable;
    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 PPPathBreakout::SaveState(Serialize &O) {
  O << GetCurrentVersion();  // Serialize the Version number
  
  O << TotalHits << TotalBW;
  O << CountTable;
}
