// This file is part of the FXT library.
// Copyright (C) 2010 Joerg Arndt
// License: GNU General Public License version 3 or later,
// see the file COPYING.txt in the main directory.

#include "fxttypes.h"
#include "jjassert.h"
#include "aux0/timer.h"
#include "mult/fxtmult.h"  // diskio
#include "mult/diskaux.h"

#include <sys/time.h>  // gettimeofday(), timeval
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>  // EBADF etc.
#include <cstdio>
#include <errno.h>

#include "fxtio.h"


#define  PRINTRW  0
//
#if  ( PRINTRW==1 )
#warning  'FYI: verbose disk read/write'
#endif

static timeval t0, t1;

static void
tell_lseek_err()
// Check errno and print info if set.
{
    if ( errno )  cerr << "\n errno=" << errno << " == ";
    {
        switch ( errno )
        {
        case EBADF:  cerr << "EBADF" << endl;  break;
        case ESPIPE:  cerr << "ESPIPE" << endl;  break;
        case EINVAL:  cerr << "EINVAL" << endl;  break;
        default:  cerr << "UNKNOWN ERROR" << endl;
        }
        cerr << "see 'man 2 lseek' for explanation." << endl;
    }
}
// -------------------------

static void
tell_read_err()
// Check errno and print info if set.
{
    if ( errno )
    {
        cerr << "\n errno=" << errno << " == ";
        switch ( errno )
        {
        case EINTR:  cerr << "EINTR" << endl;  break;
        case EAGAIN:  cerr << "EAGAIN" << endl;  break;
        case EIO:  cerr << "EIO" << endl;  break;
        case EISDIR:  cerr << "EISDIR" << endl;  break;
        case EBADF:  cerr << "EBADF" << endl;  break;
        case EINVAL:  cerr << "EINVAL" << endl;  break;
        case EFAULT:  cerr << "EFAULT" << endl;  break;
        default:  cerr << "UNKNOWN ERROR" << endl;
        }
        cerr << "see 'man 2 read' for explanation." << endl;
    }
}
// -------------------------


void
seek_read(int fd, ulong off, double *f, ulong fn)
// Read doubles f[0..fn-1] from file with filedescriptor fd.
// Start in file after (double) offset off.
{
#if  ( PRINTRW==1 )
    cerr << "seek_read():"
         << "  d_off=" << off
         << "  d_len="<< fn
         << "  d_end="<< off+fn-1
         << "  c_end="<< (off+fn)*sizeof(double)-1
         << endl;
#endif

    v_seek_read(fd, off*sizeof(double), (void *)f, fn*sizeof(double)); // jjcast
}
// -------------------------


void
v_seek_read(int fd, ulong off, void *f, ulong fn)
// From file with filedescriptor fd read fn bytes into f[].
// Start in file after (byte) offset off.
// Called by seek_read().
{
    gettimeofday(&t0, 0);
    ulong w;

    w = lseek(fd, off, SEEK_SET);
    fxtmult::ndiskseeks += 1;
    if ( errno )
    {
        tell_lseek_err();
        jjassert2( 0, "lseek for file failed");
    }

    w = read(fd,f,fn);
    if ( errno )
    {
        tell_read_err();
        jjassert2( 0, "read from file failed" );
    }

    gettimeofday(&t1, 0);
    fxtmult::diskiotime += timediff(t0, t1);
    fxtmult::diskio += (double)fn;
}
// -------------------------


static void
tell_write_err()
// Check errno and print info if set.
{
    if ( errno )
    {
        cerr << "\n errno=" << errno << " == ";
        switch ( errno )
        {
        case EBADF:   cerr << "EBADF" << endl;   break;
        case EINVAL:  cerr << "EINVAL" << endl;  break;
        case EFAULT:  cerr << "EFAULT" << endl;  break;
        case EPIPE:   cerr << "EPIPE" << endl;   break;
        case EAGAIN:  cerr << "EAGAIN" << endl;  break;
        case EINTR:   cerr << "EINTR" << endl;   break;
        case ENOSPC:  cerr << "ENOSPC" << endl;  break;
        case EIO:     cerr << "EIO" << endl;     break;
        default:      cerr << "UNKNOWN ERROR" << endl;
        }
        cerr << "see man 2 write for explanation." << endl;
    }
}
// -------------------------

void
seek_write(int fd, ulong off, double *f, ulong fn)
// Write doubles from f[0..fn-1] to file with filedescriptor fd.
// Start in file after (double) offset off.
{
#if  ( PRINTRW==1 )
    cerr << "seek_write():"
         << "  doff=" << off
         << "  dlen="<< fn
         << "  d_end="<< off+fn-1
         << "  c_end="<< (off+fn)*sizeof(double)-1
         << endl;
#endif

    v_seek_write(fd, off*sizeof(double), (void *)f, fn*sizeof(double)); // jjcast
}
// -------------------------


void
v_seek_write(int fd, ulong off, void *f, ulong fn)
// To file with filedescriptor fd write fn bytes from f[].
// Start in file after (byte) offset off.
// Called by seek_write().
{
    gettimeofday(&t0, 0);
    ulong w;

    w = lseek(fd, off, SEEK_SET);
    fxtmult::ndiskseeks += 1;
    if ( errno )
    {
        tell_lseek_err();
        jjassert2( 0, "lseek for file failed");
    }

    w = write(fd, f, fn);
//    w = TEMP_FAILURE_RETRY (write (fd, f, fn));
    if ( errno )
    {
        tell_write_err();
        jjassert2( 0, "write to file failed" );
    }

    gettimeofday(&t1, 0);
    fxtmult::diskiotime += timediff(t0, t1);
    fxtmult::diskio += (double)fn;
}
// -------------------------



void
open_or_die(const char *fn, int *fd)
// Open file.  Die if that fails.
{
    int flag = O_RDWR | O_CREAT;
//    int flag = O_RDWR | O_CREAT | O_TRUNC;  // debug

    *fd = open(fn, flag, 0600);
    jjassert2( -1!=*fd, "error opening file" );
}
// -------------------------

void
close_or_die(int fd)
// Close file.  Die if that fails.
{
    ulong w;
    w = close(fd);
    jjassert2( 0==w, "close of file failed");
}
// -------------------------

