//=-- OutputManager.h - Manager for producing HTML output --------------------=
//
//  This class converts one shell file into one output HTML file.  It does all
//  of the variable substitution and inserts data from the plugins.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-1998 Chris Lattner
//=---------------------------------------------------------------------------=

#include <fstream.h>          // We need file streams...
#include <string.h>

#include "OutputManager.h"
#include "PluginManager.h"
#include "PagePlugin.h"
#include "Options.h"
#include "VarTable.h"
#include "LinkedList.h"
#include "ConfigFileSection.h"
#include "SkelRep.h"
#include "PlatformAbstraction.h"

void OutputManager::WritePage(const Date &date, int UpdateFreq) {
  LLLink<CFileSection>   *SLink;
  int                     ActualUpdateFreq;

  if (MSOptions::Verbose) {
    cout << "  Creating ";

    switch (UpdateFreq) {
    case Plugin::UpdateDaily :   cout << "daily ";   break;
    case Plugin::UpdateWeekly :  cout << "weekly ";  break;
    case Plugin::UpdateMonthly : cout << "monthly "; break;
    case Plugin::UpdateYearly :  cout << "yearly ";  break;
    case Plugin::UpdateAll : ;
    case Plugin::UpdateNever :   cout << "all "; break;
    }
  
    cout << "html pages for: " << date;
  }
  
  MSOptions::SetDate(date);

  SLink = MSOptions::ConfigFile->GetHead();

  while (SLink != 0) {
    if (SLink->Data.GetSectionName() == String("Global")) {
      SLink = SLink->GetNext();       // Skip the global section
      continue;
    }

    VTExp *UpFreqExp = SLink->Data.GetStatements()["Update"];
    if (UpFreqExp == 0) {
      ActualUpdateFreq = -1;
    } else {
      // Get the update frequency for the current section...
      //
      String Res = UpFreqExp->GetStringValue();
      if (Res.Length() == 0)
        ActualUpdateFreq = -1;
      else
        ActualUpdateFreq = Res.atoi();
    }

    if ((UpdateFreq == Plugin::UpdateAll)  || 
        (ActualUpdateFreq == UpdateFreq)) {

      if (MSOptions::Debug)
        cout << endl << "    OutputManager.cpp:" << __LINE__ 
             << " - Processing section: " << SLink->Data.GetSectionName();
      else if (MSOptions::Verbose)
        cout << "." << flush;
      
      VarTable &List = SLink->Data.GetStatements();

      VTExp *InputVar  = List["Input"],
            *OutputVar = List["Output"];

      if (!InputVar) {
        cout << endl << "No input file specified for page '" 
             << SLink->Data.GetSectionName() << "'!!\n";
        return;
      } else if (!OutputVar) {
        cout << endl << "No output file specified for page '" 
             << SLink->Data.GetSectionName() << "'!!\n";
        return;
      }

      MSFilename InputFilename  = InputVar->GetStringValue();
      MSFilename OutputFilename = OutputVar->GetStringValue();
      
      if (!InputFilename.Valid()) {
        cout << endl << "No input file specified for page '" 
             << SLink->Data.GetSectionName() << "'!!\n";
        return;
      } else if (!OutputFilename.Valid()) {
        cout << endl << "No output file specified for page '" 
             << SLink->Data.GetSectionName() << "'!!\n";
        return;
      }

      MSFilename OET = OutputFilename.EvaluateTokens(MSOptions::CurDate);

      // Change to the theme directory so that files can be found without 
      // absolute paths... Save the current directory so that we can restore it.
      //
      MSFilename OldDir = PlatformAbstraction::GetCWD();
      PlatformAbstraction::ChDir(SLink->Data.GetCWD());

      ConvertShellToOutput(InputFilename, OET, ActualUpdateFreq, 
                           SLink->Data.GetVariables(), 
                           SLink->Data.GetCWD());

      // Restore the old directory.
      PlatformAbstraction::ChDir(OldDir);

      VTExp *LinkVar  = List["AccessLink"];

      if (LinkVar) {
        MSFilename LinkFilename = LinkVar->GetStringValue();
        if (UpdateFreq == Plugin::UpdateAll) {
          if (LinkFilename.Valid()) {
            // Create a link from the link to the filename
            PlatformAbstraction::CreateHTMLSymlink(OET, LinkFilename);
          }
        }

        MSFilename YOET = OutputFilename.EvaluateTokens(MSOptions::CurDate.Yesterday());
        if (!YOET.Exists()) {
          // Create a link from the filename to the link
          PlatformAbstraction::CreateHTMLSymlink(LinkFilename, YOET);
        }

/*
        // Attempt to make the backwards link for the first page...
        MSFilename TOET = OutputFilename.EvaluateTokens(MSOptions::CurDate.Yesterday());
        if (!TOET.Exists()) {
          // Create a link from the filename to the link
          PlatformAbstraction::CreateHTMLSymlink(LinkFilename, TOET);
        }
*/
      }
    }

    SLink = SLink->GetNext();       // Get the next section...
  }  

  if (MSOptions::Verbose) cout << endl;
  // Purge data from plugins...
  PluginManager::FlushPluginState(UpdateFreq);

  // Reset the start time for this level of output frequency...
  if (UpdateFreq >= 0 && UpdateFreq < Plugin::UpdateMaximum)
    MSOptions::StartDate[UpdateFreq] = Date();
}

void OutputManager::ConvertShellToOutput(const MSFilename &InFilename, 
                                         const MSFilename &OutFilename, 
                                         int UpdateFreq, VarTable &Variables,
                                         const String &CWD) {
  ofstream OutFile(OutFilename, ios::out | ios::trunc);

  if (!OutFile.is_open()) {
    cout << "Error opening output file '" << OutFilename 
         << "' for output.  Ignoring!\n";
    return;
  }

  // Get a handle to the skel file...
  const SkelData *Data = GetSkelFile(CWD + "/" + InFilename);

  if (Data == 0) {
    cout << "Error loading skel file '" << InFilename 
         << "' for output conversion!\n";
    return;
  }

  Data->DoOutputFile(OutFile, UpdateFreq, Variables, 0);
}

