//------------------------------------------------------------------------------
//    module fileutil.h   version 2.0b(english)                               //
//                                                                            //
//    utility functions to read values/strings from a stream. you can         //
//    realize format-sensitive reading without the "always read a whoole      //
//    line" concept.                                                          //
//                                                                            //
//    copyright (c) 1998-2003 by Lars Haendel                                 //
//    home: www.newty.de                                                      //
//                                                                            //
//    This program is free software; you can redistribute it and/or modify    //
//    it under the terms of the GNU General Public License as published by    //
//    the Free Software Foundation as version 2 of the License.               //
//                                                                            //
//    This program 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 General Public License for more details.                            //
//                                                                            //
//    You should have received a copy of the GNU General Public License       //
//    along with this program; if not, write to the Free Software             //
//    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               //
//                                                                            //
//------------------------------------------------------------------------------


#ifndef FILEUTIL_H
#define FILEUTIL_H


//----------------------------------------------------------------------------------------------------------------------
#include <iostream>                    // due to   ifstream ...
#include <fstream>
#include <ctype>                       //          isspace()
#include <limits>                      //          INT_MAX

#include "util.h"                      // define of  MaxBits

// exception codes
#define UnexpectedEndl     (int) 42    // encountered unexpected end of line (endl)
#define UnexpectedEOF      (int) 43    //      "           "     end of file (EOF)
#define NoEndlFound        (int) 44    // an endl was expected but not found
#define IllegalCharacter   (int) 45    // encountered illegal character
#define NoWhitespace       (int) 46    // no whitespace after value
#define KeyNotFound        (int) 47
#define BoolReadFailed     (int) 48
#define ValueMissing       (int) 49    // string/value missing in ReadKey...() functions
#define ErrReadingBits     (int) 50
#define UnterminatedBits   (int) 51



#define PATH_DELIMITER '\\'            // path delimiter in filenames (windows '\\' (backslash), Linux '/' (slash))
#define COMPL_PATH_DELIMITER '/'       //


#ifdef BUILDER
using namespace std;
#endif


//----------------------------------------------------------------------------------------------------------------------
// error handling
const char* GetLastError(const int& errNo);     // get error description
char&       GetIllegalCharacter();
const char* GetLastKey();


//----------------------------------------------------------------------------------------------------------------------
// filename and string manipulations
void ExchangeExt(char*const& szFilename, const char*const& szExtension); // exchange a filename's extension
void EnsurePathDelimiter(char*const& szPath);                            // ensure path delimiter at end
void CorrectPathDelimiter(char*const& szFilename);                       // replace wrong path delimiter by correct one
void ExtractPath(char*const& szDir, const char*const& szFilename);       // extract path
void PrefixPath(char*const& szFilename, const char*const& szDir);        // prepend path if necessary
bool CheckName(const char*const& szName);                      // check for valid filename without(!) path and extension

int         SizeOfString      (const char*const& szString);
const char* FlagToString      (const int& flag, const bool& f_LeftAlign=false);
const char* GetPrefixedPath   (const char*const& szFilename, const char*const& szDir);




//int  CountLinesEx (ifstream& file);
int  SearchKey    (ifstream& file, const char*const& szKey, const int& maxLines=INT_MAX, const char& delimiter=EOF);
int  ReadKeyBool  (ifstream& file, const char*const& szKey, const int defaultValue=-1, const int& maxLines=INT_MAX);
bool ReadKeyString(ifstream& file, const char*const& szKey, char* szString, const int& size, const int& maxLines=INT_MAX);
void ReadBits     (ifstream& file, int& value, const int& nBits=MaxBits);


/**********************************************************************************************************************/
// functions skipws()/skipwsEx() and wrappers

// functions to remove whitespaces out of an ifstream returning the number of encountered endl's
// the extended version also removes all comments
int skipws  (ifstream& file);
int skipwsEx(ifstream& file);


// wrapper for the two functions above which are throwing an exception depending on the number of
// encountered endl's
int  skipwsEndl    (ifstream& file);      // use skipws() throwing exception if no endl occurs
void skipwsNoEndl  (ifstream& file);      //  "     "        "         "      " an  "     "

int  skipwsExEndl  (ifstream& file);      // like above but use skipwsEx()
void skipwsExNoEndl(ifstream& file);      //  "     "    "   "      "



/**********************************************************************************************************************/
// "read"-functions

   // reading a string from an ifstream. All whitespaces, comments and the delimiter
   // terminate the string. The delimiter is left in the stream and is returned.
   char ReadString(ifstream& file, char* szString, const int& size, const char& delimiter=' ');


   /*******************************************************************************************************************/
   // functions reads an object of type T from an ifstream throwing an exception if the
   // reading causes a failstate of the stream. note: the '>>' operator skips whitespaces like
   // endl's. thus if you track the actual line-number in the file don't forget to call
   // skipws()/skipwsEx() before.

      void ReadFailed(ifstream& file);             // utility function - private :-))

   template <class T> T Read(ifstream& file, const T)
   {
      file.clear();                                   // reset stream state

      T value;
      file >> value;                                  // try to read from stream

      if(file.fail()!=0)                              // examine in case of a failstate
         ReadFailed(file);


      // check if the next character after the value is a ws or a comment ...
      if(!isspace(file.peek()) && !IsComment((char) file.peek()))
      {
         GetIllegalCharacter() = (char) file.peek();  // set as "illegal character"
         throw NoWhitespace;                          // ... otherwise: throw exception
      }

      return value;
   };


   /*******************************************************************************************************************/
   // wrapper: read a value and throw an exception if an endl occurs
   template < class T > T ReadExpNoEndl(ifstream& file, T value)
   {
      skipwsNoEndl(file);
      return Read(file, value);
   };


   /*******************************************************************************************************************/
   // search stream for string <key> followed by a '=' and read following value. If read fails
   // throw an exception. Return default value if key is not found.
   template < class T > T ReadKeyValue(ifstream& file, const char* szKey, const T defaultValue,
                                          const int& maxLines=INT_MAX)
   {
      file.clear();
      streampos curPos = file.tellg();                   // preserve actual stream position
      try{
         SearchKey(file, szKey, maxLines, '[');          // search key
      } catch(int errNo) { goto __ReturnDefault__;  }

      if(file.get()!='=')                                // if next character is not '=' -> return default value
         goto __ReturnDefault__;

      if(skipws(file)!=0)
         throw ValueMissing;

      T value;
      try{
      value=Read(file, defaultValue);                              // read
      } catch(int errNo) {  file.seekg(curPos); throw errNo; }

      file.seekg(curPos);                                          // restore stream position
      return value;

      __ReturnDefault__:
      file.seekg(curPos);                                          // restore stream position
      return defaultValue;
   };
#endif