/*****
 * runfile.in
 *
 * Runtime functions for file operations.
 *
 *****/

file*    => primFile()

#include "fileio.h"
#include "callable.h"
#include "triple.h"
#include "array.h"
#include "seconds.h"

#if defined(_WIN32)
#include <io.h>
#else
#include <unistd.h>
#endif

using namespace camp;
using namespace settings;
using namespace vm;

string commentchar="#";

// Autogenerated routines:


bool ==(file *a, file *b)
{
  return a == b;
}

bool !=(file *a, file *b)
{
  return a != b;
}

file* :nullFile()
{
  return &camp::nullfile;
}

file* input(string name=emptystring, bool check=true,
            string comment=commentchar, string mode=emptystring)
{
  file *f=NULL;
  if(mode == "binary")
    f=new ibfile(name,check);
  else if(mode == "xdr" || mode == "xdrgz") {
#ifdef HAVE_LIBTIRPC
    if(mode == "xdr")
      f=new ixfile(name,check);
    else
      f=new igzxfile(name,check);
#else
    ostringstream buf;
    buf << name << ": XDR read support not enabled";
    error(buf);
#endif
  } else if(mode == "") {
    char c=comment.empty() ? (char) 0 : comment[0];
    f=new camp::ifile(name,c,check);
  } else {
    f=NULL;
    ostringstream buf;
    buf << name << ": invalid file mode '" << mode << "'";
    error(buf);
  }

  f->open();
  return f;
}

file* output(string name=emptystring, bool update=false,
             string comment=commentchar, string mode=emptystring)
{
  file *f=NULL;
  if(mode == "pipe") {
    f=new opipe(name);
  } else if(mode == "binary") {
    if(update) f=new iobfile(name);
    else f=new obfile(name);
  } else if(mode == "xdr" || mode == "xdrgz") {
#ifdef HAVE_LIBTIRPC
    if(mode == "xdr") {
      if(update)
        f=new ioxfile(name);
      else
        f=new oxfile(name);
    } else
      f=new ogzxfile(name);
#else
    ostringstream buf;
    buf << name << ": XDR write support not enabled";
    error(buf);
#endif
  } else if(mode == "") {
    if(update) {
      char c=comment.empty() ? (char) 0 : comment[0];
      f=new iofile(name,c);
    } else f=new ofile(name);
  } else {
    f=NULL;
    ostringstream buf;
    buf << name << ": invalid file mode '" << mode << "'";
    error(buf);
  }

  f->open();
  if(update) f->seek(0,false);

  return f;
}

bool eof(file *f)
{
  return f->eof();
}

bool eol(file *f)
{
  return f->eol();
}

bool error(file *f)
{
  return f->error();
}

void clear(file *f)
{
  f->clear();
}

void close(file *f)
{
  f->close();
}

Int precision(file *f=NULL, Int digits=0)
{
  if(f == 0) f=&camp::Stdout;
  return f->precision(digits);
}

void flush(file *f)
{
  f->flush();
}

string getc(file *f)
{
  char c=0;
  if(f->isOpen()) f->read(c);
  return string(1,c);
}

Int tell(file *f)
{
  return f->tell();
}

void seek(file *f, Int pos)
{
  f->seek(pos,pos >= 0);
}

void seekeof(file *f)
{
  f->seek(0,false);
}

string :namePart(file f)
{
  return f.filename();
}

string :modePart(file f)
{
  return f.FileMode();
}

// Set file dimensions
file* :dimensionSetHelper(Int nx=-1, Int ny=-1, Int nz=-1, file *f)
{
  f->dimension(nx,ny,nz);
  return f;
}

callable* :dimensionSet(file *f)
{
  return new thunk(new bfunc(dimensionSetHelper),f);
}

array * :dimensionPart(file f)
{
  array *a=new array(3);
  (*a)[0]=f.Nx();
  (*a)[1]=f.Ny();
  (*a)[2]=f.Nz();
  return a;
}

// Set file f to read arrays in line-at-a-time mode
file* :lineSetHelper(bool b=true, file *f)
{
  f->LineMode(b);
  return f;
}

callable* :lineSet(file *f)
{
  return new thunk(new bfunc(lineSetHelper),f);
}

bool :linePart(file f)
{
  return f.LineMode();
}

// Set file to read comma-separated values
file* :csvSetHelper(bool b=true, file *f)
{
  f->CSVMode(b);
  return f;
}

callable* :csvSet(file *f)
{
  return new thunk(new bfunc(csvSetHelper),f);
}

bool :csvPart(file f)
{
  return f.CSVMode();
}

// Set file to read whitespace-separated values
file* :wordSetHelper(bool b=true, file *f)
{
  f->WordMode(b);
  return f;
}

callable* :wordSet(file *f)
{
  return new thunk(new bfunc(wordSetHelper),f);
}

bool :wordPart(file f)
{
  return f.WordMode();
}

// Set file to read/write single precision real XDR values.
file* :singlerealSetHelper(bool b=true, file *f)
{
  f->SingleReal(b);
  return f;
}

callable* :singlerealSet(file *f)
{
  return new thunk(new bfunc(singlerealSetHelper),f);
}

bool :singlerealPart(file f)
{
  return f.SingleReal();
}

// Set file to read/write single precision int XDR values.
file* :singleintSetHelper(bool b=true, file *f)
{
  f->SingleInt(b);
  return f;
}

callable* :singleintSet(file *f)
{
  return new thunk(new bfunc(singleintSetHelper),f);
}

bool :singleintPart(file f)
{
  return f.SingleInt();
}

// Set file to read/write signed int XDR values.
file* :signedintSetHelper(bool b=true, file *f)
{
  f->SignedInt(b);
  return f;
}

callable* :signedintSet(file *f)
{
  return new thunk(new bfunc(signedintSetHelper),f);
}

bool :signedintPart(file f)
{
  return f.SignedInt();
}

// Set file to read an arrayi (i int sizes followed by an i-dimensional array)
file* :readSetHelper(Int i, file *f)
{
  switch(i) {
    case 1:
    f->dimension(-2);
    break;

    case 2:
    f->dimension(-2,-2);
    break;

    case 3:
    f->dimension(-2,-2,-2);
    break;

    default:
    f->dimension();
  }

  return f;
}

callable* :readSet(file *f)
{
  return new thunk(new bfunc(readSetHelper),f);
}

// Delete file named s.
Int delete(string s)
{
  s=outpath(s);
  Int rc=unlink(s.c_str());
  if(rc == 0 && verbose > 0)
    cout << "Deleted " << s << endl;
  return rc;
}

// Rename file "from" to file "to".
Int rename(string from, string to)
{
  from=outpath(from);
  to=outpath(to);
  Int rc=renameOverwrite(from.c_str(),to.c_str());
  if(rc == 0 && verbose > 0)
    cout << "Renamed " << from << " to " << to << endl;
  return rc;
}

// Create a uniquely named temporary file.
string mktemp(string s)
{
  string baseTemplate=s+"XXXXXX";
  char *S=StrdupMalloc(baseTemplate);
  bool success=true;
#if defined(_WIN32)
  if (_mktemp_s(S,baseTemplate.length()+1) != 0)
  {
    success = false;
  }
  FILE* fp;
  if (success && (fopen_s(&fp,S,"w") != 0))
  {
    success = false;
  }
#else
  int fd=mkstemp(S);
  if (fd < 0)
  {
    success = false;
  }
#endif
  if(!success)
  {
    ostringstream buf;
    buf << "Could not create unique temporary filename based on " << s;
    error(buf);
  }
  string T(S);
  free(S);

#if defined(_WIN32)
  bool closeSuccess = fclose(fp) == 0;
#else
  bool closeSuccess = close(fd) == 0;
#endif

  if (!closeSuccess)
  {
    ostringstream buf;
    buf << "Could not finalize temporary file based on " << s;
    error(buf);
  }
  return T;
}
