//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published
//  by the Free Software Foundation; either version 3.0 of the License, or
//  any later version.
//
//  The GPSTk is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with GPSTk; if not, write to the Free Software Foundation,
//  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
//  
//  Copyright 2004, The University of Texas at Austin
//
//============================================================================

//============================================================================
//
//This software developed by Applied Research Laboratories at the University of
//Texas at Austin, under contract to an agency or agencies within the U.S. 
//Department of Defense. The U.S. Government retains all rights to use,
//duplicate, distribute, disclose, or release this software. 
//
//Pursuant to DoD Directive 523024 
//
// DISTRIBUTION STATEMENT A: This software has been approved for public 
//                           release, distribution is unlimited.
//
//=============================================================================

/**
 * @file FileSpec.hpp
 * Define the specification of a file.
 */

#ifndef GPSTK_FILESPEC_HPP
#define GPSTK_FILESPEC_HPP

#include <vector>
#include <functional>
#include <map>

#include "CommonTime.hpp"

#ifdef _WIN32
const char slash = '\\';
#else
const char slash = '/';
#endif

namespace gpstk
{
      /// @ingroup FileDirProc
      //@{

      /// This exception is thrown when there is a problem with
      /// handling file specifications.
      /// @ingroup exceptiongroup
   NEW_EXCEPTION_CLASS(FileSpecException, gpstk::Exception);

      /**
       * A FileSpec is how file names are defined.  For instance,
       * ASM%4Y.%3j is the file spec for a SMODF file.  The format is similar
       * to what you would use in sprintf(), and specifically it uses the same
       * fields as CommonTime::printf().  For the most predictable results,
       * fully specify the field length (as in %4Y vs. %Y).  Check the
       * FileSpecType enum values for how characters map to fields. Any new
       * fields added should conform to CommonTime or already existing fields,
       * if there are any.
       */
   class FileSpec
   {
   public:
         /** This enum lists the different possible elements you can
          * have in a FileSpec.
          *
          * @note 
          * Whenever a format is added or removed from the CommonTime
          * class, it more than likely should also be added or removed
          * from the FileSpec class.  Additionally, the format
          * character must not conflict with any of the existing
          * format characters in CommonTime or FileSpec.
          */
      enum FileSpecType
      {
         unknown,       ///< Unknown type
         station,       ///< 'n' A field for station numbers
         receiver,      ///< 'r' A field for the receiver number
         prn,           ///< 'p' A field for PRN number
         selected,      ///< 't' A field for selected/unselected receiver
         sequence,      ///< 'I' A sequence number, as in part 1, part 2, etc..
         version,       ///< 'v' A version number, as in version 2 
                        ///<     is more recent than version 1
         fixed,         ///< A field for fixed characters
         clock,         ///< 'k' A field for the clock number
         text,          ///< 'x' A field for arbitrary text


            // see CommonTime for more information on the following elements
         year,          ///< 'y' or 'Y' A field for a year
         month,         ///< 'm' A field for month (numeric)
         dayofmonth,    ///< 'd' A field for day-of-month  
         hour,          ///< 'H' A field for hours (out of 24)
         minute,        ///< 'M' A field for minutes (out of 60)
         second,        ///< 'S' A field for seconds (out of 60)
         fsecond,       ///< 'f' A field for seconds (float)
         gpsweek,       ///< 'G' A field for 10 bit GPS weeks
         fullgpsweek,   ///< 'F' A field for full GPS weeks
         gpssecond,     ///< 'g' A field for GPS second-of-week
         mjd,           ///< 'Q' A field for Modified Julian Date
         dayofweek,     ///< 'w' A field for day-of-week (numeric)
         day,           ///< 'j' A field for day of year
         doysecond,     ///< 's' a field for second-of-day (float)
         zcount,        ///< 'Z' A field for GPS Z-count
         zcountfloor,   ///< 'z' A field for GPS Z-count rounded down
         unixsec,       ///< 'U' A field for UNIX seconds
         unixusec,      ///< 'u' A field for UNIX microseconds
         fullzcount,    ///< 'C' or 'c' A field for Full GPS Z-count

         end            ///< A place holder for the end of this list
      };

         /// A map from a FileSpecType to a string, used in the
         /// toString function.
      typedef std::map<FileSpecType, std::string> FSTStringMap;


         /// An enum for identifying the sort order of the list returned
         /// from FileHunter.
      enum FileSpecSortType
      {
         ascending,
         descending
      };

         /// Default constructor
      FileSpec() {}

         /// Constructor with a string to parse
      FileSpec(const std::string& fileSpec)
         throw(FileSpecException)
      {init(fileSpec);}

         /// Destructor
      virtual ~FileSpec() {}

         /// Reinitializes this FileSpec with the new string
      virtual FileSpec& newSpec(const std::string& fileSpec)
         throw(FileSpecException)
      {init(fileSpec); return *this;}

         /// Returns the string of the filespec
      virtual std::string getSpecString(void) const
      {return fileSpecString;}

         /**
          * Returns a string that can be used to search for files
          * matching this FileSpec.  Essentailly turns every non-fixed
          * field into strings of '?'.
          * @throw FileSpecException when there's an error in the FileSpec
          */
      virtual std::string createSearchString() const
         throw(FileSpecException);

         /**
          * Given a file name and a field, returns that field from the string.
          * Use hasField() first to see if the field exists in the FileSpec.
          * If multiple fields of FileSpecType are defined, only the first
          * is returned.
          * @throw FileSpecException when the FileSpecType doesn't exist
          *  in the FileSpec
          */
      virtual std::string extractField(const std::string& filename, 
                                       const FileSpecType) const
         throw(FileSpecException);

         /**
          * Given a field type, returns true if the FileSpec has that field.
          * @throw FileSpecException when you pass in an invalid FileSpecType
          */
      virtual bool hasField(const FileSpecType) const
         throw(FileSpecException);

         /** 
          * If possible, returns a CommonTime object with the time the file
          * represents.  Since the time resolution only goes to days for
          * most file types, all times are set to midnight of that day.
          * @throw FileSpecException when a time can't be formed
          */
      virtual gpstk::CommonTime extractCommonTime(const std::string& filename)
         const
         throw(FileSpecException);

         /**
          * For the given FileSpec, fills in the fields with the given
          * information and returns a string of that file name.  Use the
          * FSTStringMap to provide all the other non-time data for the
          * string (i.e. FSTSMap[station] = "85408";).  Any unspecified
          * field will be filled with 0's.  The one side affect of this
          * is that you can only specify one of each field in the map,
          * but you're likely not going to need two different station
          * numbers in the file name.  If you want, you can put the CommonTime
          * information into the FSTSMap, but it's not necessary.
          * @return the new filename.
          */
      virtual std::string toString(const gpstk::CommonTime& dt,
                                   const FSTStringMap& fstsMap = FSTStringMap()) 
         const;

         /**
          * Sort the list of files ascending or 
          * descending.  The fields of the files are sorted in the order
          * that they're specified in the FileSpecType enum.
          * The list fileList is modified as a result
          * of this.  If the files in fileList have paths listed, then
          * only the file name (taken to be the word after the last '/') 
          * will be used in the comparison.  This function also filters
          * out older versions of files in the fileList.
          */
      virtual void sortList(std::vector<std::string>& fileList, 
                            const FileSpecSortType fsst = ascending) const
         throw(FileSpecException);

         /// semi-nicely print the FileSpec to the stream.
      virtual void dump(std::ostream& o) const;

   protected:
         /// Parses the string into the FileSpec object
      virtual void init(const std::string& fileSpec)
         throw(FileSpecException);

   public:
         /**
          * Converts the FileSpecType to a string it is identified with.
          * @throw FileSpecException when FileSpecType doesn't match
          *  any known types
          */
      static std::string convertFileSpecType(const FileSpecType)
      throw(FileSpecException);

         /**
          * Converts the string into its corresponding FileSpecType
          * @throw FileSpecException when FileSpecType doesn't match
          *  any known types
          */
      static FileSpecType convertFileSpecType(const std::string&)
         throw(FileSpecException);

   protected:
         /// This is an internal, private class of FileSpec that holds
         /// information for one individual file specification element.
      class FileSpecElement
      {
      public:
            /// This is the default constructor too.
         FileSpecElement(const std::string::size_type numChars = 0, 
                         const std::string::size_type offs = 0, 
                         const FileSpecType fst = unknown,
                         const std::string& fld = std::string())
               : numCh(numChars), offset(offs), type(fst), field(fld)
         {}
         
            /// The number of characters this field is in the file name.
         std::string::size_type numCh;
            /// The offset in the string where this field begins
         std::string::size_type offset;
            /// The type which this field corresponds to.
         FileSpecType type;
            /// For fixed strings, the characters it represents.
            /// For all other types, it's the original string for
            /// that field (i.e. '%03j').
         std::string field;
      };

      struct FileSpecSort :
         public std::binary_function<std::string,std::string,bool>
      {
      public:
         FileSpecSort(const FileSpec& fs, const FileSpecSortType s)
               : fileSpec(fs), sortDir(s) {}
            /// Compares two strings based on the substrings defined by
            /// offset and length accounting for any directory names
            /// in the strings
         bool operator() (const std::string& l, const std::string& r) const;
      private:
            /// ascending or descending
         FileSpecSortType sortDir;
            /// The FileSpec doing the sorting
         const FileSpec &fileSpec;
      };

         /// Holds all of the FileSpecElements for this FileSpec
      std::vector<FileSpecElement> fileSpecList;
         /// Holds the string that the fileSpecList was generated from
      std::string fileSpecString;

      friend struct FileSpecSort;

   }; // class FileSpec

      /// Operator-- for FileSpecType
   FileSpec::FileSpecType& operator-- (FileSpec::FileSpecType& fst, int);
      /// Operator++ for FileSpecType
   FileSpec::FileSpecType& operator++ (FileSpec::FileSpecType& fst, int);

      //@}

} // namespace gpstk

#endif 
