//=-- Options.cpp - This class manages the MagicStats.cfg file ---------------=
//
//  By using the ConfigFile class, this class keeps track of the specific data
//  that resides in the MagicStats.cfg file.  This class is also responsible
//  for parsing the command line and for keeping track of all of the 
//  MSOptions that the user selects.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2001 Chris Lattner
//=---------------------------------------------------------------------------=

#include <ctype.h>
#include <stdlib.h>   // Uses toupper(), exit()

#include "Table.h"
#include "Options.h"
#include "ServerSettings.h"
#include "ResourceManager.h"
#include "AccessLogManager.h"
#include "Plugin.h"
#include "PlatformAbstraction.h"
#include "ConfigRep.h"

// Define the static variable...
String MSOptions::CurrentUserName;
String MSOptions::BuiltInURL, MSOptions::BuiltInPath;

//ConfigFile   MSOptions::ConfigF;
LinkedList<CFileSection> *MSOptions::ConfigFile;

int MSOptions::Verbose, MSOptions::Debug, MSOptions::Progress, 
    MSOptions::PrintPlugins, MSOptions::PrintThemes;
int MSOptions::MaxAccesses;
int MSOptions::CatchupAccessLog;

CSString *MSOptions::ThemeToLint = 0;

// StartDate - Date/Time MagicStats started each page generation...  These are
// never explicitly initialized, because the constructors default to the current
// time of their construction for their value.
//
Date MSOptions::StartDate[Plugin::UpdateMaximum];
Date MSOptions::CurDate;

int MSOptions::init(int argc, char *argv[]) {
  CurrentUserName = PlatformAbstraction::GetUserName();

  // Ignore errors loading this file...
  LoadServerConfig(PlatformAbstraction::GetServerConfigFilename());

  ProcessCommandLine(argc, argv);

  if (Verbose)
    cout << "MagicStats v" << Version << "...\t"
            "By: Chris Lattner (sabre@nondot.org)\n" << flush;

  if (Debug) cout << "  Loading configuration file...\n" << flush;

  // Load the User's Configuration file...
  ConfigFileSection *UserConfigFile = LoadConfigFile(MagicStatsCfgFilename);
  if (UserConfigFile == 0) return -1;

  // Convert the config file to it's flat representation...
  VarTable SystemVars;
  SetGlobalVariables(SystemVars);

  ConfigFile = new LinkedList<CFileSection>(
                                 UserConfigFile->EvaluateFile(SystemVars));

  // If debug, print it all out...
  //if (Debug) cout << "Loaded ConfigFile: " << *ConfigFile << endl;

  // Locate the BuiltIn plugins resources path...
  if (ResourceManager::FindPlugin("BuiltIn", BuiltInPath, BuiltInURL)) {
    cout << "Could not find Plugins/BuiltIn/ in any Resources Directories!\n"
            "  Checked the following directories:\n";
    ResourceManager::PrintResourcePaths(2);  // Indent two spaces.
    cout << endl;
  }

  if (PrintThemes || PrintPlugins) {
    cout << "Active Resource Paths:\n";
    ResourceManager::PrintResourcePaths(2);  // Indent two spaces.
  }

  if (PrintThemes) {
    ResourceManager::PrintThemes();
    if (!PrintPlugins)
      return 1;
    cout << "\n\n";    // Print a seperator
  }

  if (ThemeToLint) {
    LintTheme(*ThemeToLint);
    return 1;
  }

  return 0;
}

void MSOptions::LintTheme(const String &ThemeName) {
  String Path, URL;

  if (ResourceManager::FindTheme(ThemeName, Path, URL)) {
    cout << "Could not lint theme '" << ThemeName << "': Theme not found!\n";
    return;
  }
  
  cout << "Linting theme '" << ThemeName << "'.\n  Theme Path: "
       << Path << "\n  Theme URL : " << URL << endl << endl;
  
  MSFilename ConfigPath(Path);

  // Load the User's Configuration file...
  ConfigFileSection *CFile = LoadConfigFile(ConfigPath);
  if (CFile == 0) return;

  // Convert the config file to it's flat representation...
  VarTable SystemVars;
  SetGlobalVariables(SystemVars);

  if (CFile->LintTheme(SystemVars)) {
    cout << ThemeName << " Theme has lint!\n";
  } else {
    cout << ThemeName << " Theme is free of lint!\n";
  }
}


// LoadServerConfig - If the server configuration file is available, load it 
// and incorporate the settings in it into this Options object...
//
int MSOptions::LoadServerConfig(const MSFilename &Filename) {
  ServerSettings Settings;
  if (Filename.Exists())     // Don't print error if global cfg not found...
    Settings.Load(Filename);
  else
    if (Verbose)
      cout << "Server Installation not found, using user installation.\n";
  return 0;
}


// SetGlobalVariables - Add all of the standard global variables in the MS
// namespace into the specified variable table.
//
void MSOptions::SetGlobalVariables(Table<CSString,VTExp*> &Vars) {
  Vars["$MS.UserName"      ] = new VTStrExp(-1, CurrentUserName);
  Vars["$MS.Update.Always" ] = new VTStrExp(-1, "0");
  Vars["$MS.Update.Daily"  ] = new VTStrExp(-1, "1");
  Vars["$MS.Update.Weekly" ] = new VTStrExp(-1, "2");
  Vars["$MS.Update.Monthly"] = new VTStrExp(-1, "3");
  Vars["$MS.Update.Yearly" ] = new VTStrExp(-1, "4");
  Vars["$MS.Update.Never"  ] = new VTStrExp(-1, "5");
  Vars["$MS.BuiltInURL" ] = new VTStrExp(-1,
MSOptions::GetBuiltInPluginResURL());
}

// PrintHelpMessage - Print the useful help message if the user specified 
// '-h' on the command line...
//
void MSOptions::PrintHelpMessage() {
  cout 
   << "MagicStats v" << Version << "\n\n"
      " -#\t\t\tThe # parameter causes MagicStats to print out a #\n"
         "\t\t\tcharacter for every 100 hits processed.\n\n"
      " -C <CFG File>\t\tThe C parameter specifies the filename to use as a\n"
                  "\t\t\tconfiguration file.\n\n"
    /*
      " -I\t\t\tThe I parameter starts the MagicStats installer.\n\n"
      " -D\t\t\tThe D parameter starts MagicStats as a daemon.\n\n"
    */
      " -lint <Theme>\t\tRun 'Lint' on a theme, to pick off the fluff.\n\n"
      " -P\t\t\tThe P parameter prints out the available plugins.\n\n"
      " -T\t\t\tThe T parameter prints out the available themes.\n\n"
      " -N <num>\t\tThe N parameter limits the number of accesses processed\n"
             "\t\t\tper run.\n\n"
      " -V\t\t\tThe V parameter enables verbosity.\n\n";

  //cout << " -S<percent>\t\tThe S parameter is used to skip to a position in the\n"
  //                  "\t\t\taccess_log file.\n\n"
}


// ProcessCommandLine - Incorporate the information in the command line into
// the local variables in this Options object.
//
int MSOptions::ProcessCommandLine(int argc, char *argv[]) {
  char Command;

  for (int i = 1; i < argc; i++) {
    if (argv[i][0] != '-') {
      cout << "Error!  Unrecognized argument '" << argv[i] 
           << "'!  Ignoring." << endl;
    } else {
      Command = toupper(argv[i][1]);
      switch (Command) {
      case 'C':
        i++;
        if (i >= argc) {
          cout << "Error!  Missing filename parameter for -C command "
                  "line parameter!\n";
        } else {
          MagicStatsCfgFilename = argv[i];
        }

        break;

#if 0
      case 'D':                   // Start MagicStats as a Daemon
        // Start as daemon
        break;
      case 'I':                   // Start the MagicStats installer
        // Install it.
        break;
#endif

      case 'L':
        {
          CSString Arg = argv[i]+1;
          // Case = -lint <themename>
          if (argv[i][2] == 0 || Arg == CSString("Lint")) {
            i++;
            if (i >= argc)
              cout << "Error!  Missing theme name for -lint command line "
                      "parameter!\n";
            else
              ThemeToLint = new CSString(argv[i]);
          } else {                 // Take care of the case of -N2000
            Arg.Left(4);
            if (Arg == CSString("Lint")) {
              ThemeToLint = new CSString(argv[i]+4);   // -lint<themename>
            } else {
              ThemeToLint = new CSString(argv[i]+1);   // -l<themename>
            }
          }
          break;
        }

      case '#':
        Progress = 1;
        break;

      case 'G':                   // Enable Debugging mode
        Debug = Verbose = 1;
        break;

      case '?':
      case 'H':                   // Print out help text
        PrintHelpMessage();
        exit(0);
        break;

      case 'P':                   // Print installed plugins
        PrintPlugins = 1;
        break;

      case 'T':                   // Print installed themes
        PrintThemes = 1;
        break;

      case 'N':                   // Specify the max number of hits to process
        if (argv[i][2] == 0) {    // Take care of the case of -N 2000
          i++;
          if (i >= argc)
            cout << "Error!  Missing number for -N command line parameter!\n";
          else
            MaxAccesses = atoi(argv[i]);
        } else {                 // Take care of the case of -N2000
          MaxAccesses = atoi(&argv[i][2]);
        }
        break;

      case 'S':                   // Skip to the end of the access_log file.
        if (argv[i][2] == 0)
          CatchupAccessLog = 100;
        else
          CatchupAccessLog = atoi(&argv[i][2]);
        if (CatchupAccessLog < 0) CatchupAccessLog = 0;
        if (CatchupAccessLog > 100) CatchupAccessLog = 100;
        break;

      case 'V':                   // Enable Verbose mode
        Verbose = 1;   
        break;
      }
    }
  }
  return 0;
}
