// fgbdfname.cc

#include "fgbdfname.h"

#ifndef _FGSTRING_H
#include "fgstring.h"
#endif

#ifndef _FGMRANK_H
#include "fgmrank.h"
#endif

#ifndef _FGSCOMP_H
#include "fgscomp.h"
#endif

#ifndef _FGICOMP_H
#include "fgicomp.h"
#endif

#ifndef _FGDYYMMDDCOMP_H
#include "fgdyymmddcomp.h"
#endif

#ifndef _FGCHARCOMP_H
#include "fgcharcomp.h"
#endif

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

#ifndef __SGI_STL_VECTOR_H
#include <vector>
#endif

struct FGBrokenDownFileName::Internal_FGBrokenDownFileName {
  std::vector<FGFileNameComponent*> mComponents;
};

FGBrokenDownFileName::FGBrokenDownFileName(const FGString& fname)
{
  mpInternals = new Internal_FGBrokenDownFileName;

  // Need to catch exceptions to clean up above resources
  try {
    // We must now parse the supplied filename into components
    unsigned int index = 0;
    // Parse state
    bool esc = false;
    bool inTok = false;
    FGString bit;
    while (index <= fname.GetLength()) {
      char c = fname[index++];
      if (c == '\0') {
        // End of string
        if (inTok) {
          // Error!
          throw FGException(FGException::kParseEndOfString);
        }
        if (bit.GetLength() > 0) {
          FGFileNameComponent* pNew;
          pNew = new FGStringComponent(bit);
          mpInternals->mComponents.push_back(pNew);
        }
      } else if (esc || (c != '\\' && c != '<' && c != '>')) {
        bit += c;
        esc = false;
      } else if (c == '\\') {
        esc = true;
      } else if (c == '<') {
        if (inTok) {
          throw FGException(FGException::kParseUnexpectedChar, "<");
        }
        inTok = true;
        if (bit.GetLength() > 0) {
          FGFileNameComponent* pNew;
          pNew = new FGStringComponent(bit);
          mpInternals->mComponents.push_back(pNew);
        }
        bit.MakeEmpty();
      } else {
        // c == '>'
        if (!inTok) {
          throw FGException(FGException::kParseUnexpectedChar, ">");
        }
        FGString strInt("int");
        FGString strYYMMDD("YYMMDD");
        FGString strChar("char");
        FGString strOptChar("optchar");
        if (bit == strInt) {
          FGFileNameComponent* pNew;
          pNew = new FGIntegerComponent;
          mpInternals->mComponents.push_back(pNew);
        } else if (bit == strYYMMDD) {
          FGFileNameComponent* pNew;
          pNew = new FGDateYYMMDDComponent;
          mpInternals->mComponents.push_back(pNew);
        } else if (bit == strChar) {
          FGFileNameComponent* pNew;
          pNew = new FGCharacterComponent(false);
          mpInternals->mComponents.push_back(pNew);
        } else if (bit == strOptChar) {
          FGFileNameComponent* pNew;
          pNew = new FGCharacterComponent(true);
          mpInternals->mComponents.push_back(pNew);
        } else {
          throw FGException(FGException::kUnrecognizedComponent, bit);
        }
        inTok = false;
        bit.MakeEmpty();
      }
    } // end while (more chars to parse)
  } // end try block
  catch (FGException&) {
    delete this;
    throw;
  }
}

FGBrokenDownFileName::~FGBrokenDownFileName()
{
  if (mpInternals) {
    // Free the vector of pointers
    std::vector<FGFileNameComponent*>::iterator iComps;
    for (iComps = mpInternals->mComponents.begin();
         iComps != mpInternals->mComponents.end(); iComps++) {
      delete *iComps;
    }

    delete mpInternals;
  }
}

FGMatchRanking
FGBrokenDownFileName::GetRanking(const FGString& cmp) const
{
  FGMatchRanking ret;
  FGString mangleCompare(cmp);

  // Iterate through components
  std::vector<FGFileNameComponent*>::const_iterator iComps;

  for (iComps = mpInternals->mComponents.begin();
       iComps != mpInternals->mComponents.end(); iComps++) {

    int result = -1;
    bool matched = (*iComps)->MatchAndRankComponent(mangleCompare,
                                                    &result);
    if (matched == false) {
      // Oh dear
      FGMatchRanking duffRet;
      return duffRet;
    }

    if (result != -1) {
      ret.AddValue(result);
    }
  }

  // Not a match if any unmatched string remains
  if (mangleCompare.GetLength() > 0) {
    FGMatchRanking duffRet;
    return duffRet;
  }

  // It's a match.. this return value indicates how good a match
  // i.e. linux-2.2.1.tar.gz is better that linux-2.2.0.tar.gz
  ret.SetMatch();
  return ret;
}
