// module  prepareAnimatedGif.cpp
// Removes empty file from the current dicrectory
// Generates per no-empty file a gnuplot macro 
// Executes gnuplot with this macro to produce GIF files
// Should be followed in a shell by: convert *.gif out.gif
// This version is only for Unix/Linux
// Written by EH 04-2022, works!

// Versions:
// 30.09.2022 option -skip added to avoid very slow film motion
// 21.11.2022 options -C and -L added, fix for crash in cout expr. (floating point???)
//            two params added in function makeGnuPlotMacro()
// 24.11.2022 the generated macro file is now created/written on /tmp due
//            to access problems
// 25.11.2022 user name added in the title
// 03.12.2022 makeGnuplotMacro() gets now the complete path for the data file
//            getcwd() added to get the path of the actual dir.
// 10.01.2023 some warnings eliminated - will reappear if logging enabled

#include <iostream>
#include <stdio.h>
//#incldue <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>

//#include "hu_utils.h"

using namespace std;

int  logging = 0;
char fileType = 'C'; // can be changed via option -L to 'L'

// ------------------------------------------------
// corresponds to getBasename() under UNIX
// Warning: this is not reentrant!
char *getBasename(const char *path)
{
  int  n, ll, p, start;
  int  locRet = 0;
  static char retbuf[256];
  //
  ll = ::strlen(path);
  retbuf[0] = 0;
  start = 0;
  p     = -1;
  for (n=ll - 1; n >= 0; n--)
  {
    if (path[n] == '.')  p = n;
    if ((path[n] == '\\') || (path[n] == '/')) 
    {
      start = n + 1;
      break;
    }
  }
  ::strcpy(retbuf, &path[start]);
  if (p > 0)
    retbuf[p - start] = 0;
  return retbuf;
}   // end getBasename()

// -------------------------------------------
int makeGnuplotMacro(const char *pMacroFn,   // should be on /tmp!!!!!!
                     const char *pDataFn,    // ".dat" input file, complete path needed
                     int   xRange,
                     int   yRange)
{
  int ret = 0;
  int locRet = 0;
  FILE *ofd = NULL;
  FILE *ifd = NULL;
  char *pbn = NULL;
  char uname[64] = "";
  char *pUn = NULL;
  char title[256] = "";
  char buf[128]   = "";
  char *pBuf      = NULL;
  //
  // get the user name
  pUn = ::getlogin();
  if (pUn != NULL)
    ::strcpy(uname, pUn);
  if (::logging)
       cout << "makeGnuPlotMacro() pMacroFn=" << pMacroFn << 
               " pDataFn=" << pDataFn << " user=" << uname << endl;
  // test if the macro file exists
  ifd = ::fopen(pMacroFn, "r"); 
  if (ifd != NULL)
  {
    // file exists - remove it
    if (::logging)
       cout << "    file exists - will be destroyed" << endl;
    // test accessability
    // Try to read a line
    pBuf = ::fgets(buf, sizeof(buf), ifd);
    if (pBuf == NULL)
    {
      if (::logging)
        cout << "    WARNING: cannot read from file" << endl;
    }
    ::fclose(ifd);
    //
    // remove the file
    locRet = ::remove(pMacroFn);
    if (::logging)
       cout << "remove() returns " << locRet << endl;
    if (locRet == 0)
    {
      if (::logging)
       cout << "    file has been removed - will be recreated" << endl;
    }
    else
    {
       if (::logging)
       cout << "    ERROR: file cannot be removed - will be recreated" << endl;
    }
  }
  // 
  // everything OK here
  ofd = ::fopen(pMacroFn, "w"); // should create a new empty file 
  if (ofd == NULL)
  {
    if (::logging)
       cout << "makeGnuPlotMacro() ERROR: cannot write macro file" << endl;
    ret = -1;
    goto zurueck;
  }
  ::fprintf(ofd, "set terminal gif\n");
  pbn = getBasename(pDataFn);
  ::fprintf(ofd, "set output \"%s.gif\n", pbn);
  ::fprintf(ofd, "set xrange [%d:%d]\n", -xRange, xRange);        
  ::fprintf(ofd, "set yrange [%d:%d]\n", -yRange, yRange);      
  ::sprintf(title, "\"%s %s\"", pDataFn, uname);
  ::fprintf(ofd, "set title %s\n", title); 
  ::fprintf(ofd, "plot \"%s\" with lines\n", pDataFn);
  ::fprintf(ofd, "quit\n");
  //
  ::fclose(ofd);
  //
  zurueck:
  if (::logging)
      cout << "makeGnuPlotMacro() ret=" << ret << endl;
  return ret;
}   // end makeGnuplotMacro()

// ------------------------------
void usage()
{
  cout << "usage: prepareAnimatedGif.cpp [-skip n] [-C] [-L] [-v]" << endl;
  cout << "V1.7 10.01.2023  by EH" << endl;
  cout << "Considers only file of type Cxxx.dat oder Lxxx.dat depending on options -C or -L" << endl;
  cout << endl;
}   // end usage()

// ----------------------------------
int main(int argc, char *argv[])
{
  int     n, ret, ln;
  int     locRet;
  int     noFiles = 0;
  int     noFilesWff = 0;
  int     skip    = 0;
  FILE    *ofd;
  char    currentDir[256] = "./";  // actual directory
  char    fname[256] = "";
  char    fnamePath[384] = "";
  pid_t   pid;
  char    *pbn = NULL;
  DIR     *pdir;
  char    *pDir = NULL;
  struct  dirent *pDirectory;
  char    cmd[256] = "";
  char    cwd[128];
  struct  stat statBuf;
  FILE    *macroOfd = NULL;
  //
  for (int n=1; n < argc; n++)
  {
    if (::strcmp(argv[n], "-v") == 0)
      ::logging = 1;
    if (::strcmp(argv[n], "-skip") == 0)
      skip = ::atoi(argv[n + 1]);
    if (::strcmp(argv[n], "-L") == 0)
      ::fileType = 'L';
    if (::strcmp(argv[n], "-C") == 0)
      ::fileType = 'C';  // this is the default
  }
  if (::logging)
  {
    usage();
    cout << "skip=" << skip << endl;
  }
  //
  // get the absolute path for the current directory
  //pDir = ::getcwd(currentDir, ::strlen(currentDir) - 1); // is not reliable!!!!!!!!
  pDir = ::get_current_dir_name();
  if (pDir != NULL)
  {
    ::strcpy(currentDir, pDir);
    ::free(pDir);
    pDir = NULL;    
  }
  if (::logging)
    cout << "path of curr.dir.=" << currentDir << endl;
  //
  // make sure that we can write to "/tmp/gif.m"
  macroOfd = ::fopen("/tmp/gif.m", "w");
  if (macroOfd == NULL)
  {
    // Should not occur!
    cout << "ERROR: cannot create/write file /tmp/gif.m - we stop" << endl;
    goto zurueck;
  }
  ::fclose(macroOfd); 
  //
  pdir = ::opendir(currentDir);
  if (pdir == NULL)
  {
    cout << "ERROR: cannot open the current directory" << endl;
    return -1;
  }
  //
  while (1)
  {
    pDirectory = ::readdir(pdir);
    if (pDirectory == 0) 
      break;
    ::strcpy(fname, pDirectory->d_name);
    if (::logging)
      cout << "curr.file=" << fname << endl;
    pbn = getBasename(fname);
    if (pbn == NULL)
    {
      if (::logging)
       cout << "ERROR: cannot get basename" << endl;
      continue;
    }
    if (::logging)
       cout << "  basename=" << pbn << endl;
    if (::strlen(pbn) == 0)
    {
      if (::logging)
       cout << "ERROR: basename is empty" << endl;
      continue;
    }
    //
    // file name is here OK
    ln = ::strlen(pbn);
     // consider only files of type "Cxxx.dat"
     if (pbn[0] != ::fileType)   // 21.11.2022
       continue;
     if ((::strcmp(fname + ln, ".DAT") == 0) ||
        (::strcmp(fname + ln, ".dat") == 0))
     {
        if (::logging) cout << "found: current file=" << fname << endl;
        //  Is the file empty?
        locRet = ::stat(fname, &statBuf);
        if (locRet != 0)
        {
          if (::logging)
            cout << "ERROR: cannot stat this file" << endl;
          continue;
        }
        if (logging) 
          cout << "stat() ok 1" << endl;
        if (statBuf.st_size == 0)
        {
            // this is an empty file  - remove/delete it
            if (logging) 
              cout << "    is empty" << endl;
            ::strcpy(cmd, "rm ");
            ::strcat(cmd, fname);
            locRet = ::system(cmd);
            if (logging) 
              cout << "ret for system(rm...)=" << locRet << endl;
        }   // end if statBuf.st_size == 0
        if (logging) 
          cout << "stat() ok 2" << endl;
        //
        // Everything OK here with this .dat file
        // Should we skip this file (to allow faster films)?
        noFilesWff++;
        /*
        // crash with floating point exception here...!
        if (::logging)
          cout << "noFilesWff=" << noFilesWff << " skip=" << skip <<
               " noFiles % skip=" << (noFilesWff % skip) << endl << flush;
        */
        if (skip > 0)  // added 21.11.2022
        {
          if ((noFilesWff % skip) != 0)
          {
            if (logging) 
              cout << "file will be skipped(skip param)" << endl;
            continue;
          }
        }
        // Process it
        // Generate a gnuplot macro
        ::strcpy(fnamePath, currentDir);  // 03.12.2022
        ::strcat(fnamePath, "/");
        ::strcat(fnamePath, fname);
        locRet = makeGnuplotMacro("/tmp/gif.m", fnamePath, 6, 6); 
        // 21.11.2022 x,y ranges should come from command line
        //
        // Execute this macro - call gnuplot with this macro as argument
        ::sprintf(cmd, "gnuplot /tmp/gif.m");
        locRet = ::system(cmd);
        if (logging) 
          cout << "ret for system(gnuplot...)=" << locRet << endl;
        noFiles++;
    }   // end if ::strcmp ...
  }   // end while 1
  //
  closedir(pdir);
  //
  zurueck:
  if (::logging)
    cout << "noFiles converted: " << noFiles << endl;
  return noFiles;
}   // end main()
