// fgfilegrab.cc

#include "fgfilegrab.h"

// Possible protocol interface derived classes
#ifndef _FGFTPCON_H
#include "fgftpcon.h"
#endif

// Possible picker interface derived classes
#ifndef _FGPICKALL_H
#include "fgpickall.h"
#endif

#ifndef _FGPICKALLSYNC_H
#include "fgpickallsync.h"
#endif

#ifndef _FGPICKBEST_H
#include "fgpickbest.h"
#endif

#ifndef _FGPICKREGEXP_H
#include "fgpickregexp.h"
#endif

#ifndef _FGPICKREGEXPSYNC_H
#include "fgpickregexpsync.h"
#endif

// Directory listings
#ifndef _FGDLIST_H
#include "fgdlist.h"
#endif

// Local filesystem helper
#ifndef _FGFSHELP_H
#include "fgfshelp.h"
#endif

// Action lists
#ifndef _FGALIST_H
#include "fgalist.h"
#endif

#ifndef _FGLOGGER_H
#include "fglogger.h"
#endif

#ifndef _FGEXC_H
#include "fgexc.h"
#endif

#include <assert.h>
#include <stdlib.h>

FGFileGrab::FGFileGrab(const FGString& ruleName,
                       const FGString& host, const FGString& remDir,
                       const FGString& localDir, const FGString& file)
: mpConInterface(0), mpPickInterface(0)
{
  mRemoteHost = host;
  mRemoteDir = remDir;
  mLocalDir = localDir;
  mRuleName = ruleName;

  // XXX :-)
  mpConInterface = new FGFTPCon;
  FGString cmp("*");
  FGString cmp2("*!");
  int nameLen = file.GetLength();
  if (file == cmp) {
    mpPickInterface = new FGPickAll(mpConInterface);
  } else if (file == cmp2) {
    mpPickInterface = new FGPickAllSync(mpConInterface);
  } else if (nameLen > 1 && file[0] == '/' && file[1] == '!') {
    // /! sequence means match a regular expression, and delete local
    // files matching if not found remotely, too.
    FGString regExp(file.Right(nameLen - 2));

    // Alert alert leak
    mpPickInterface = new FGPickRegexpSync(mpConInterface, regExp);

  } else if (nameLen > 0 && file[0] == '/') {
    // / character means regular expression follows
    FGString regExp(file.Right(nameLen - 1));
    // Alert throws exception - leaks mpConInterface
    mpPickInterface = new FGPickRegexp(mpConInterface, regExp);
  } else {
    // Extract number of revisions if neccessary
    int nrevs = 1;
    FGString newFile(file);
    if (file.GetLength() > 1 && file[0] == '[') {
      unsigned int index = 1;
      FGString strNum;
      while (index < file.GetLength() && file[index] != ']') {
        strNum += file[index];
        index++;
      }
      // If we ended by hitting "]" then extract revisions count
      // and lop [] enclosed bit off string
      if (index < file.GetLength()) {
        int newRevs = atoi(strNum);
        if (newRevs > 1) {
          nrevs = newRevs;
        }
        int charsLeft = file.GetLength() - (index + 1);
        newFile = file.Right(charsLeft);
      }
    }
    // Alert alert this might throw an exception
    // If this class (FGFileGrab) allocates any resources in the
    // constructor they will need to be taken care of
    mpPickInterface = new FGPickBest(mpConInterface, newFile, nrevs);
  }
}

const FGString&
FGFileGrab::GetRuleName(void) const
{
  return mRuleName;
}

void
FGFileGrab::FreeResources(void)
{
  if (mpConInterface) {
    delete mpConInterface;
  }
  if (mpPickInterface) {
    delete mpPickInterface;
  }
}

void
FGFileGrab::GrabIt(void) const
{
  // Pre-conditions
  assert(mpConInterface != 0);
  assert(mpPickInterface != 0);

  FGLogger& theLog = FGLogger::GetLogger();

  // Connect to remote
  FGString logMsg;
  logMsg = "Trying to connect to host: ";
  logMsg += mRemoteHost;
  theLog.LogMsg(logMsg, FGLogger::kFGLLVerbose);
  mpConInterface->Connect(mRemoteHost);
  theLog.LogMsg("Connect successful", FGLogger::kFGLLVerbose);

  // Once we are connected we must guarantee to call Disconnect() at
  // some time
  try {

    // Change to required directory
    logMsg = "Changing to remote directory: ";
    logMsg += mRemoteDir;
    theLog.LogMsg(logMsg, FGLogger::kFGLLVerbose);
    mpConInterface->ChangeDir(mRemoteDir);

    // Get list of files in said dir
    theLog.LogMsg("Getting local and remote file lists",
                  FGLogger::kFGLLVerbose);
    FGDirListing remoteList = mpConInterface->GetDirListing();

    // Get list of files in local dir of interest
    FGDirListing localList = FGFileSystemHelper::GetDirListing(mLocalDir);

    // Ask the picker interface what we propose to do given the two file lists
    FGActionList actions = mpPickInterface->DecideActions(localList, remoteList);

    // Let it rip...
    try {
      actions.DoActions();
    }
    catch (FGException&) {
      actions.FreeResources();
      throw;
    }

    // Free some resources
    actions.FreeResources();
  }
  catch (FGException&) {
    theLog.LogMsg("Disconnecting (error condition)",
                  FGLogger::kFGLLVerbose);
    mpConInterface->Disconnect();
    throw;
  }

  // Clean up
  theLog.LogMsg("Disconnecting", FGLogger::kFGLLVerbose);
  mpConInterface->Disconnect();
}
