//=-- Filter.cpp - Filter for filtering URLs, and other strings --------------=
//
//  This class implements a class that is used to filter a string against a 
//    standard unix style Filter.  It also has code to load a Filter from the
//    configuration file.  Example of a filter: "\~sabre\*MagicStats*"
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-1998 Chris Lattner
//=---------------------------------------------------------------------------=

#include "AccessFormatPlugin.h"
#include "MSString.h"
#include "Filter.h"
#include "WildcardMatcher.h"
#include "RegexMatcher.h"

int Filter::FilterCount = 0;

void Filter::AddRef() {
  RefCount++;
}

void Filter::DeleteRef() {
  if (--RefCount == 0)
    delete this;
}


// Wildcard Matchers...
MatcherCache &Filter::GetCachedWMatchers(int Delete) {
  // This cannot be a simple static variable... because you cannot control the
  // order in which statics are created or destroyed.  In this case, the order 
  // it is destroyed in is important, because it has be AFTER the destructor of
  // the FilterHandle cache list is destroyed...
  static MatcherCache *CachedWMatchers = 0;

  if (Delete) {
    delete CachedWMatchers;
    CachedWMatchers = 0;    
  } else if (CachedWMatchers == 0) {
    CachedWMatchers = new MatcherCache();
  }
  return *CachedWMatchers;
}

// Regex Matchers...
MatcherCache &Filter::GetCachedRMatchers(int Delete) {
  static MatcherCache *CachedRMatchers = 0;
  if (Delete) {
    delete CachedRMatchers;
    CachedRMatchers = 0;    
  } else if (CachedRMatchers == 0) {
    CachedRMatchers = new MatcherCache();
  }
  return *CachedRMatchers;
}

void Filter::DecodeFilter(LinkedList<Matcher> &L, const String &F) {
  String Filter(F), Temp;
  TextMatcher *M;
  int FieldID;

  while (Filter.Length() != 0) {
    if (Filter[0] == '!') {
      Filter.Tokenize("!", Temp);
      FieldID = AccessFormatPlugin::GetFieldID(Temp);
      Filter.Tokenize("!", Temp);

      M = GetCachedRMatchers()[Temp];
      if (M == 0) {
        M = new RegexMatcher(Temp);
        GetCachedRMatchers()[Temp] = M;
      }
    } else if (Filter[0] == '|') {
      Filter.Tokenize("|", Temp);
      FieldID = AccessFormatPlugin::GetFieldID(Temp);
      Filter.Tokenize("|", Temp);

      M = GetCachedWMatchers()[Temp];
      if (M == 0) {
        M = new WildcardMatcher(Temp);
        GetCachedWMatchers()[Temp] = M;
      }
    } else {
      M = GetCachedWMatchers()[F];
      if (M == 0) {
        M = new WildcardMatcher(F);
        GetCachedWMatchers()[F] = M;
      }

      L.AddToTail(Matcher(AccessFormatPlugin::GetFieldID("URL"), M));
      return;
    }

    L.AddToTail(Matcher(FieldID, M));
  }
}

Filter::Filter(const String &i, const String &e) : IncludeFilter(i), ExcludeFilter(e) {
  RefCount = 0;

  if (FilterCount++ == 0) {
    GetCachedRMatchers().SetDefault(0);
    GetCachedWMatchers().SetDefault(0);
  }

  DecodeFilter(I, i);
  DecodeFilter(E, e);
}

Filter::~Filter() {
  if (--FilterCount == 0) {
    Table<String,TextMatcher*>::Iterator It = GetCachedRMatchers().GetStart();

    // Loop through to make sure every Matcher is deleted...
    for (; It; It++) delete It->S;

    // Delete the Hash Table next...
    GetCachedRMatchers(1);


    // Delete the Wildcard Matchers now...
    It = GetCachedWMatchers().GetStart();

    // Loop through to make sure every Matcher is deleted...
    for (; It; It++) delete It->S;
    // Delete the Hash Table next...
    GetCachedWMatchers(1);
  }
}

String Filter::ToParamStr() {
  return IncludeFilter + String(':') + ExcludeFilter;
}

int Filter::Match(const AccessFormatPlugin &A) {
  // Match it
  LinkedList<Matcher>::Iterator It = I.GetIterator();

  for (; It; It++) {
    if (!It->S->Match(A.GetField(It->P)))
      return 0;
  }

  for (It = E.GetIterator(); It; It++) {
    if (It->S->Match(A.GetField(It->P))) 
      return 0; 
  }

  return 1;
}

