//==================================================================================================
// S t a n d a r d                                                                        Interface
// S t o r a g e
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library 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 Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to store data. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "standard/storage.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guStandardStorage
#define guStandardStorage

// Headers //---------------------------------------------------------------------------------------
#include <bpp/standard/maths.hpp> /*INCLUDE*/

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef STANDARD_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  standardStorage
#define private_area standardStorage_private

namespace public_area  { /*NAMESPACE*/ using namespace standardMaths; }
namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniStandardStorage
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area {}

namespace private_area {
 class clFolderProcessingDosUnix;
 class clFolderProcessingLines;
 class clFolderProcessingRemoving;
}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area {
 function void       convertFileDosUnix(tcString,tyBoolean);
 function tyCardinal countLinesInFile(tcString);
 function void       cutFile(tcString,tyInteger,tyBoolean=true);

 inline void convertFolderDosUnix(tcString,const std_vector(clString) &,tyBoolean,
                                  tyBoolean,tyBoolean=false);

 inline tyCardinal countLinesInFolder(tcString,const std_vector(clString) &,
                                      tyBoolean,tyBoolean=false);

 inline void removeFromFolder(tcString,const std_vector(clString) &,tyBoolean,tyBoolean=false);

 function void renumFiles(tcString,tcString,tcString,tyInteger,tyCardinal,tyBoolean=true,
                          tyBoolean=false);

 function void restoreFile(tcString,tyBoolean=true);
}

namespace private_area {}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
extern_dynamic_constant(private,clString,goTemporaryFileName,?);

// F o l d e r P r o c e s s i n g D o s U n i x  Interface //--------------------------------------
namespace private_area {
 class clFolderProcessingDosUnix : public clFolderProcessing {
  //-----------------------------------------------------------------------------------------Private
  private_property clString                     atBlankSpace;
  private_property const std_vector(clString) & atFormatS;
  private_property tyBoolean                    atDosToUnix;
  private_property tyBoolean                    atDisplayed;

  private_property constructor clFolderProcessingDosUnix(const clFolderProcessingDosUnix &);
  private_property clFolderProcessingDosUnix & operator = (const clFolderProcessingDosUnix &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clFolderProcessingDosUnix(const std_vector(clString) &,tyBoolean,
                                                        tyBoolean=true);

  public_property virtual destructor clFolderProcessingDosUnix(void) {}

  public_property void startFolder(ctString &);
  public_property void endFolder(ctString &);
  public_property void file(ctString &);
  public_property void unknown(ctString &);
 };
}

// F o l d e r P r o c e s s i n g L i n e s  Interface //------------------------------------------
namespace private_area {
 class clFolderProcessingLines : public clFolderProcessing {
  //-----------------------------------------------------------------------------------------Private
  private_property clString                     atBlankSpace;
  private_property const std_vector(clString) & atFormatS;
  private_property tyBoolean                    atDisplayed;

  private_property constructor clFolderProcessingLines(const clFolderProcessingLines &);
  private_property clFolderProcessingLines & operator = (const clFolderProcessingLines &);
  //------------------------------------------------------------------------------------------Public
  read_only_attribute(tyCardinal,atNbLine,nbLine);

  public_property constructor clFolderProcessingLines(const std_vector(clString) &,tyBoolean=true);
  public_property virtual destructor clFolderProcessingLines(void) {}

  public_property void startFolder(ctString &);
  public_property void endFolder(ctString &);
  public_property void file(ctString &);
  public_property void unknown(ctString &);
 };
}

// F o l d e r P r o c e s s i n g R e m o v i n g  Interface //------------------------------------
namespace private_area {
 class clFolderProcessingRemoving : public clFolderProcessing {
  //-----------------------------------------------------------------------------------------Private
  private_property clString                     atBlankSpace;
  private_property const std_vector(clString) & atFormatS;
  private_property tyBoolean                    atDisplayed;

  private_property constructor clFolderProcessingRemoving(const clFolderProcessingRemoving &);
  private_property clFolderProcessingRemoving & operator = (const clFolderProcessingRemoving &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clFolderProcessingRemoving(const std_vector(clString) &,
                                                         tyBoolean=true);

  public_property virtual destructor clFolderProcessingRemoving(void) {}

  public_property void startFolder(ctString &);
  public_property void endFolder(ctString &);
  public_property void file(ctString &);
  public_property void unknown(ctString &);
 };
}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area {
 //-------------------------------------------------------------------------------ConvertFileDosUnix
 /*FUNCTION*/
 /* Converts ASCII files in a folder from the Unix format into the MS-DOS format, and vice
    versa. The scanning of the folder can be recursive and information can be displayed. */
 inline void convertFolderDosUnix(tcString agFolderName,const std_vector(clString) & agFileFormatS,
                                  tyBoolean agDosToUnix,tyBoolean agRecursive,
                                  tyBoolean agDisplayed) {
  private_area::clFolderProcessingDosUnix lcProcessing(agFileFormatS,agDosToUnix,agDisplayed);

  scanFolder(agFolderName,lcProcessing,agRecursive);
 }
 //-------------------------------------------------------------------------------CountLinesInFolder
 /*FUNCTION*/
 /* Counts the number of lines in the ASCII files of a folder. The scanning of the folder can be
    recursive and information can be displayed. */
 inline tyCardinal countLinesInFolder(tcString agFolderName,
                                      const std_vector(clString) & agFileFormatS,
                                      tyBoolean agRecursive,tyBoolean agDisplayed) {
  private_area::clFolderProcessingLines lcProcessing(agFileFormatS,agDisplayed);

  scanFolder(agFolderName,lcProcessing,agRecursive);
  return (lcProcessing.nbLine());
 }
 //---------------------------------------------------------------------------------RemoveFromFolder
 /*FUNCTION*/
 /* Removes files from a folder. The scanning of the folder can be recursive and information
    can be displayed. */
 inline void removeFromFolder(tcString agFolderName,const std_vector(clString) & agFileFormatS,
                              tyBoolean agRecursive,tyBoolean agDisplayed) {
  private_area::clFolderProcessingRemoving lcProcessing(agFileFormatS,agDisplayed);

  scanFolder(agFolderName,lcProcessing,agRecursive);
 }
}

namespace private_area {}

// X X X  Inline //---------------------------------------------------------------------------------
namespace {}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// S t a n d a r d                                                                   Implementation
// S t o r a g e
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library 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 Library General Public License for more details (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "standard/storage.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define STANDARD_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/standard/storage.hpp> /*INTERFACE*/
#include <bpp/file_name.hpp> /*NEED*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  standardStorage
#define private_area standardStorage_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Standard/Storage");

// Initialization //--------------------------------------------------------------------------------
#undef iniStandardStorage
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
dynamic_constant(clString,goTemporaryFileName);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //-------------------------------------------------------------------------------ConvertFileDosUnix
 /*FUNCTION*/
 /* Converts an ASCII file from the Unix format into the MS-DOS format, and vice versa. */
 function void convertFileDosUnix(tcString agFileName,tyBoolean agDosToUnix) {
  method_name("convertFileDosUnix");

  tyCharacter lcCharacter;
  clInFile    lcFin;
  clOutFile   lcFout;
  tyInteger   lcLength;

  tyCharacter lcEndLine   = 10;
  tyBoolean   lcFirstLine = true;
  tyCharacter lcForbidden = 13;
  tyCharacter lcNewLine[] = { 13, 10 };

  open(lcFin,agFileName,ios::in|ios::binary);
  open(lcFout,private_area::goTemporaryFileName->data(),ios::out|ios::binary);
  lcLength=size(lcFin);

  while (lcLength>0) {
   lcFin.read(&lcCharacter,1);
   lcLength--;

   if (not agDosToUnix) {
    if (lcCharacter!=13) lcFout.write(&lcCharacter,1);
   }
   else {
    if (lcFirstLine) {
     if (lcCharacter==13) { lcEndLine=13; lcForbidden=10; lcFirstLine=false; }
     if (lcCharacter==10) { lcEndLine=10; lcForbidden=13; lcFirstLine=false; }
    }

    if (lcCharacter!=lcForbidden) {
     if (lcCharacter==lcEndLine) lcFout.write(lcNewLine,2);
     else lcFout.write(&lcCharacter,1);
    }
   }
  }

  close(lcFin);
  close(lcFout);

  if (lcFin.fail()) send_error(erFileReading);
  if (lcFout.fail()) send_error(erFileWriting);

  removeFile(agFileName);
  changeName(private_area::goTemporaryFileName->data(),agFileName);
 }
 //---------------------------------------------------------------------------------CountLinesInFile
 /*FUNCTION*/ /* Counts the number of lines in an ASCII file. */
 function tyCardinal countLinesInFile(tcString agFileName) {
  method_name("countLinesInFile");

  tyCharacter lcCharacter;
  clInFile    lcFin;
  tyInteger   lcLength;
  tyCardinal  lcNbLine;

  open(lcFin,agFileName,ios::in|ios::binary);
  lcLength=size(lcFin);
  lcNbLine=0;

  while (lcLength>0) {
   lcFin.read(&lcCharacter,1);
   lcLength--;
   if (lcCharacter==10) lcNbLine++;
  }

  close(lcFin);
  if (lcFin.fail()) send_error(erFileReading);
  return (lcNbLine);
 }
 //------------------------------------------------------------------------------------------CutFile
 /*FUNCTION*/ /* Cuts a file into several smaller files of a given size. */
 function void cutFile(tcString agFileName,tyInteger agNumberByte,tyBoolean agDisplayed) {
  method_name("cutFile");

  tyInteger lcBufferSize   = integer(streamBufferSize());
  tyInteger lcNumberByte   = 0;
  tyBoolean lcOutputOpen   = false;
  tyInteger lcVolumeNumber = 0;

  tyString  lcBuffer;
  tyInteger lcRemainingByte;
  clInFile  lcFin;
  clOutFile lcFout;
  tyInteger lcLength;
  clString  lcName;

  lcBuffer=new_array(tyCharacter,lcBufferSize);
  open(lcFin,agFileName,ios::in|ios::binary);
  lcRemainingByte=size(lcFin);

  while (not lcFin.fail() and not lcFout.fail() and lcRemainingByte!=0) {
   lcLength=mini(lcBufferSize,agNumberByte*1024-lcNumberByte);
   lcLength=mini(lcLength,lcRemainingByte);
   lcFin.read(lcBuffer,lcLength);

   if (lcNumberByte==0) {
    lcVolumeNumber++;
    lcNumberByte=0;
    lcName=clString(agFileName)+"."+standardMaths::string(lcVolumeNumber);
    if (agDisplayed) environment::out(lcName.data(),true,true);
    if (lcOutputOpen) close(lcFout);
    open(lcFout,lcName.data(),ios::out|ios::binary|ios::trunc);
    lcOutputOpen=true;
   }

   lcNumberByte=(lcNumberByte+lcLength)%(agNumberByte*1024);
   lcRemainingByte-=lcLength;
   lcFout.write(lcBuffer,lcLength);
  }

  if (lcOutputOpen) close(lcFout);
  close(lcFin);
  delete_array(lcBuffer);
  if (lcFin.fail()) send_error(erFileReading);
  if (lcFout.fail()) send_error(erFileWriting);
 }
 //---------------------------------------------------------------------------------------RenumFiles
 /*FUNCTION*/
 /* Renames files by numbering them. The files that match a given file format are renamed according
    to a new file format described by a string (<CODE>%</CODE> locates the position of the number)
    and a number of digits for the numbering. */
 function void renumFiles(tcString agFolderName,tcString agOldFormat,tcString agNewFormat,
                          tyInteger agFirstNumber,tyCardinal agNbDigit,
                          tyBoolean agCaseSensitive,tyBoolean agDisplayed) {
  method_name("renumFiles");

  typedef clFileNameS::const_iterator clIterator;

  clIterator  lcCurrentName;
  clFileNameS lcFileNameS;
  clIterator  lcLastName;

  clString lcNewFormat(agNewFormat);

  clString   lcNewName;
  tyCardinal lcPosition;
  clString   lcOldName;
  clString   lcZeros;

  find(agNewFormat,'%',lcPosition);
  getFileList(agFolderName,agOldFormat,lcFileNameS,agCaseSensitive);

  lcCurrentName=lcFileNameS.begin();
  lcLastName=lcFileNameS.end();

  while (lcCurrentName!=lcLastName) {
   if (agDisplayed) environment::out((*lcCurrentName).data());
   lcNewName=standardMaths::string(agFirstNumber);
   lcZeros.resize(agNbDigit-lcNewName.size());
   lcZeros.fill(0,lcZeros.size(),'0');

   lcNewName = lcNewFormat.part(0,lcPosition) + lcZeros + lcNewName
               + lcNewFormat.part(lcPosition+1,lcNewFormat.size()-lcPosition-1);

   if (agDisplayed) environment::out(" => ");
   if (agDisplayed) environment::out(lcNewName.data(),true);
   lcOldName=clString(agFolderName)+fileNameSeparator()+(*lcCurrentName);
   lcNewName=clString(agFolderName)+fileNameSeparator()+lcNewName;
   if (fileExists(lcNewName.data())) send_error(erFileAlreadyExists);
   changeName(lcOldName.data(),lcNewName.data());
   lcCurrentName++;
   agFirstNumber++;
  }
 }
 //--------------------------------------------------------------------------------------RestoreFile
 /*FUNCTION*/ /* Restores a file from files resulting of a cutting. */
 function void restoreFile(tcString agFileName,tyBoolean agDisplayed) {
  method_name("restoreFile");

  tyCardinal lcVolumeNumber = 1;
  clString   lcName = clString(agFileName)+"."+standardMaths::string(lcVolumeNumber);

  clInFile  lcFin;
  clOutFile lcFout;

  open(lcFout,agFileName,ios::out|ios::binary|ios::trunc);

  while (not lcFout.fail() and not lcFin.fail() and fileExists(lcName.data())) {
   if (agDisplayed) environment::out(lcName.data(),true,true);
   open(lcFin,lcName.data(),ios::in|ios::binary);
   copy(lcFout,lcFin,true);
   close(lcFin);
   lcVolumeNumber++;
   lcName=clString(agFileName)+"."+standardMaths::string(lcVolumeNumber);
  }

  close(lcFout);
  if (lcFin.fail()) send_error(erFileReading);
  if (lcFout.fail()) send_error(erFileWriting);
 }
}

namespace private_area {}

// F o l d e r P r o c e s s i n g D o s U n i x  Implementation //---------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------Constructor
 property clFolderProcessingDosUnix::
 clFolderProcessingDosUnix(const std_vector(clString) & agFormatS,tyBoolean agDosToUnix,
                           tyBoolean agDisplayed)
 : atBlankSpace(),atFormatS(agFormatS),atDosToUnix(agDosToUnix),atDisplayed(agDisplayed) {}
 //--------------------------------------------------------------------------------------StartFolder
 property void clFolderProcessingDosUnix::startFolder(ctString & agFullName) {
  clString lcPath;
  clString lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  if (atDisplayed) {
   environment::out(atBlankSpace.data(),false,true);
   environment::out(lcShortName.data());
   environment::out(":",true);
  }

  atBlankSpace+=" ";
 }
 //----------------------------------------------------------------------------------------EndFolder
 property void clFolderProcessingDosUnix::endFolder(ctString &)
 { atBlankSpace=atBlankSpace.part(0,atBlankSpace.size()-1); }
 //---------------------------------------------------------------------------------------------File
 property void clFolderProcessingDosUnix::file(ctString & agFullName) {
  std_vector(clString)::const_iterator lcCurrentFormat = atFormatS.begin();
  std_vector(clString)::const_iterator lcLastFormat    = atFormatS.end();

  tyBoolean lcMatched = false;

  clString lcPath;
  clString lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  while (not lcMatched and lcCurrentFormat!=lcLastFormat) {
   lcMatched=matched(lcShortName.data(),(*lcCurrentFormat).data());
   lcCurrentFormat++;
  }

  if (lcMatched) {
   if (atDisplayed) {
    environment::out(atBlankSpace.data(),false,true);
    environment::out(lcShortName.data(),true);
   }

   convertFileDosUnix(agFullName.data(),atDosToUnix);
  }
 }
 //------------------------------------------------------------------------------------------Unknown
 property void clFolderProcessingDosUnix::unknown(ctString &) {}
}

// F o l d e r P r o c e s s i n g L i n e s  Implementation //-------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------Constructor
 property clFolderProcessingLines::clFolderProcessingLines(const std_vector(clString) & agFormatS,
                                                           tyBoolean agDisplayed)
 : atBlankSpace(),atFormatS(agFormatS),atDisplayed(agDisplayed),atNbLine(0) {}
 //--------------------------------------------------------------------------------------StartFolder
 property void clFolderProcessingLines::startFolder(ctString & agFullName) {
  clString lcPath;
  clString lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  if (atDisplayed) {
   environment::out(atBlankSpace.data(),false,true);
   environment::out(lcShortName.data());
   environment::out(":",true);
  }

  atBlankSpace+=" ";
 }
 //----------------------------------------------------------------------------------------EndFolder
 property void clFolderProcessingLines::endFolder(ctString &)
 { atBlankSpace=atBlankSpace.part(0,atBlankSpace.size()-1); }
 //---------------------------------------------------------------------------------------------File
 property void clFolderProcessingLines::file(ctString & agFullName) {
  std_vector(clString)::const_iterator lcCurrentFormat = atFormatS.begin();
  std_vector(clString)::const_iterator lcLastFormat    = atFormatS.end();

  tyBoolean lcMatched = false;

  tyCardinal lcNbLine;
  clString   lcPath;
  clString   lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  while (not lcMatched and lcCurrentFormat!=lcLastFormat) {
   lcMatched=matched(lcShortName.data(),(*lcCurrentFormat).data());
   lcCurrentFormat++;
  }

  if (lcMatched) {
   if (atDisplayed) {
    environment::out(atBlankSpace.data(),false,true);
    environment::out(lcShortName.data());
   }

   lcNbLine=countLinesInFile(agFullName.data());

   if (atDisplayed) {
    environment::out(" [");
    environment::out(lcNbLine);
    environment::out("]",true);
   }

   atNbLine+=lcNbLine;
  }
 }
 //------------------------------------------------------------------------------------------Unknown
 property void clFolderProcessingLines::unknown(ctString &) {}
}

// F o l d e r P r o c e s s i n g R e m o v i n g  Implementation //-------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------Constructor
 property clFolderProcessingRemoving::
 clFolderProcessingRemoving(const std_vector(clString) & agFormatS,tyBoolean agDisplayed)
 : atBlankSpace(),atFormatS(agFormatS),atDisplayed(agDisplayed) {}
 //--------------------------------------------------------------------------------------StartFolder
 property void clFolderProcessingRemoving::startFolder(ctString & agFullName) {
  clString lcPath;
  clString lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  if (atDisplayed) {
   environment::out(atBlankSpace.data(),false,true);
   environment::out(lcShortName.data());
   environment::out(":",true);
  }

  atBlankSpace+=" ";
 }
 //----------------------------------------------------------------------------------------EndFolder
 property void clFolderProcessingRemoving::endFolder(ctString &) {
  atBlankSpace=atBlankSpace.part(0,atBlankSpace.size()-1);
 }
 //---------------------------------------------------------------------------------------------File
 property void clFolderProcessingRemoving::file(ctString & agFullName) {
  std_vector(clString)::const_iterator lcCurrentFormat = atFormatS.begin();
  std_vector(clString)::const_iterator lcLastFormat    = atFormatS.end();

  tyBoolean lcMatched = false;

  clString lcPath;
  clString lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  while (not lcMatched and lcCurrentFormat!=lcLastFormat) {
   lcMatched=matched(lcShortName.data(),(*lcCurrentFormat).data());
   lcCurrentFormat++;
  }

  if (lcMatched) {
   if (atDisplayed) {
    environment::out(atBlankSpace.data(),false,true);
    environment::out(lcShortName.data(),true);
   }

   removeFile(agFullName.data());
  }
 }
 //------------------------------------------------------------------------------------------Unknown
 property void clFolderProcessingRemoving::unknown(ctString &) {}
}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    goTemporaryFileName = new_object(clString(environment::temporaryLocation()+
                          fileNameSeparator()+STANDARD_STORAGE_TEMPORARY_FILE));
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) {
  try {
   environment::informTermination(goModuleName);

   delete_object(goTemporaryFileName);
  }

  initializer_catch;
 }
}

// End //-------------------------------------------------------------------------------------------
}