/*
 * ==========================================================================
 *         _   _      _   ____            __ __  __      _
 *        | \ | | ___| |_|  _ \ ___ _ __ / _|  \/  | ___| |_ ___ _ __
 *        |  \| |/ _ \ __| |_) / _ \ '__| |_| |\/| |/ _ \ __/ _ \ '__|
 *        | |\  |  __/ |_|  __/  __/ |  |  _| |  | |  __/ ||  __/ |
 *        |_| \_|\___|\__|_|   \___|_|  |_| |_|  |_|\___|\__\___|_|
 *
 *                  NetPerfMeter -- Network Performance Meter
 *                 Copyright (C) 2009-2026 by Thomas Dreibholz
 * ==========================================================================
 *
 * 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, either version 3 of the License, or
 * (at your option) any later version.

 * 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, see <http://www.gnu.org/licenses/>.
 *
 * Contact:  dreibh@simula.no
 * Homepage: https://www.nntb.no/~dreibh/netperfmeter/
 */

#include "inputfile.h"

#include <cstring>


// ###### Constructor #######################################################
InputFile::InputFile()
{
   File      = nullptr;
   BZFile    = nullptr;
   ReadError = false;
   Line      = 0;
}


// ###### Destructor ########################################################
InputFile::~InputFile()
{
   finish();
}


// ###### Initialize output file ############################################
bool InputFile::initialize(const char*           name,
                           const InputFileFormat format)
{
   // ====== Initialize object ==============================================
   finish();

   StoragePos = 0;
   Line       = 0;
   Format     = format;
   if(name != nullptr) {
      Name = std::string(name);
   }
   else {
      Name = std::string();
   }

   // ====== Initialize file ================================================
   BZFile = nullptr;
   File = (name != nullptr) ? fopen(name, "r") : tmpfile();
   if(File == nullptr) {
      std::cerr << "ERROR: Unable to open input file <" << Name << ">!\n";
      ReadError = true;
      return false;
   }

   // ====== Initialize BZip2 compressor ====================================
   if(format == IFF_BZip2) {
      int bzerror;
      BZFile = BZ2_bzReadOpen(&bzerror, File, 0, 0, nullptr, 0);
      if(bzerror != BZ_OK) {
         std::cerr << "ERROR: Unable to initialize BZip2 compression on file <" << Name << ">!\n"
                   << "Reason: " << BZ2_bzerror(BZFile, &bzerror) << "\n";
         BZ2_bzWriteClose(&bzerror, BZFile, 0, nullptr, nullptr);
         ReadError = true;
         finish();
         return false;
      }
   }

   ReadError = false;
   return true;
}


// ###### Finish output file ################################################
bool InputFile::finish(const bool closeFile)
{
   // ====== Finish BZip2 compression =======================================
   if(BZFile) {
      int bzerror;
      BZ2_bzReadClose(&bzerror, BZFile);
      BZFile = nullptr;
   }

   // ====== Close or rewind file ===========================================
   if(File) {
      if(closeFile) {
         fclose(File);
         File = nullptr;
      }
      else {
         rewind(File);
      }
   }
   return !ReadError;
}


// ###### Read line from file ###############################################
ssize_t InputFile::readLine(char* buffer, size_t bufferSize, bool& eof)
{
   eof = false;

   if(bufferSize < 1) {
      return -1;
   }
   bufferSize--;  // Leave one byte for terminating 0x00 byte.

   ssize_t bytesRead;
   for(;;) {
      if(StoragePos >= bufferSize) {
         std::cerr << "ERROR: Line " << Line << " of file <"
                   << Name << "> is too long to fit into buffer!\n";
         return -1;
      }
      memcpy(buffer, (const char*)&Storage, StoragePos);
      if(Format == IFF_BZip2) {
         int bzerror;
         bytesRead = BZ2_bzRead(&bzerror, BZFile, (char*)&buffer[StoragePos],
                                std::min(sizeof(Storage), bufferSize) - StoragePos);
      }
      else if(Format == IFF_Plain) {
         bytesRead = (ssize_t)fread((char*)&buffer[StoragePos], 1,
                                    std::min(sizeof(Storage), bufferSize) - StoragePos, File);
      }
      else {
         bytesRead = -1;
      }

      if(bytesRead < 0) {
         return bytesRead;   // Error.
      }
      else {
         bytesRead += (ssize_t)StoragePos;
         buffer[bytesRead] = 0x00;
         if(bytesRead == 0) {
            eof = true;
            return 0;   // End of file.
         }

         StoragePos = 0;
         for(ssize_t i = 0;i < bytesRead;i++) {
            if(buffer[i] == '\n') {
               StoragePos = (size_t)(bytesRead - i - 1);
               memcpy((char*)&Storage, &buffer[i + 1], StoragePos);
               buffer[i] = 0x00;
               Line++;
               return i;   // A line has been read.
            }
         }
      }
   }
}
