///
/// @file iowfile.c
/// @authors k. headley
/// @date 01 mar 2020

/// File IO wrapper API
/// (use for cross-platform functions/apps)

/// @sa doxygen-examples.c for more examples of Doxygen markup


/////////////////////////
// Terms of use
/////////////////////////
/*
 Copyright Information

 Copyright 2002-2020 MBARI
 Monterey Bay Aquarium Research Institute, all rights reserved.

 Terms of Use

 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. You can access the GPLv3 license at
 http://www.gnu.org/licenses/gpl-3.0.html

 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
 (http://www.gnu.org/licenses/gpl-3.0.html)

 MBARI provides the documentation and software code "as is", with no warranty,
 express or implied, as to the software, title, non-infringement of third party
 rights, merchantability, or fitness for any particular purpose, the accuracy of
 the code, or the performance or results which you may obtain from its use. You
 assume the entire risk associated with use of the code, and you agree to be
 responsible for the entire cost of repair or servicing of the program with
 which you are using the code.

 In no event shall MBARI be liable for any damages, whether general, special,
 incidental or consequential damages, arising out of your use of the software,
 including, but not limited to, the loss or corruption of your data or damages
 of any kind resulting from use of the software, any prohibited use, or your
 inability to use the software. You agree to defend, indemnify and hold harmless
 MBARI and its officers, directors, and employees against any claim, loss,
 liability or expense, including attorneys' fees, resulting from loss of or
 damage to property or the injury to or death of any person arising out of the
 use of the software.

 The MBARI software is provided without obligation on the part of the
 Monterey Bay Aquarium Research Institute to assist in its use, correction,
 modification, or enhancement.

 MBARI assumes no responsibility or liability for any third party and/or
 commercial software required for the database or applications. Licensee agrees
 to obtain and maintain valid licenses for any additional third party software
 required.
 */

/////////////////////////
// Headers
/////////////////////////
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "iowfile.h"

#if  defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)
#include "ff.h"
#include "ioserial.h"
#include "timers.h"
#include "ui.h"
#elif defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__)
#include <fcntl.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#endif

/////////////////////////
// Macros
/////////////////////////

// These macros should only be defined for
// application main files rather than general C files
/*
/// @def PRODUCT
/// @brief header software product name
#define PRODUCT "TBD_PRODUCT"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-YYYY MBARI Monterey Bay Aquarium Research Institute, all rights reserved."
/// @def NOWARRANTY
/// @brief header software terms of use
#define NOWARRANTY  \
"This program is distributed in the hope that it will be useful,\n"\
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"\
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"\
"GNU General Public License for more details (http://www.gnu.org/licenses/gpl-3.0.html)\n"
*/

/////////////////////////
// Declarations
/////////////////////////

/////////////////////////
// Imports
/////////////////////////

/////////////////////////
// Module Global Variables
/////////////////////////
iow_error_t iow_errno=IOW_OK;
const char *iow_strerr_mem[] ={
        "OK","DISK_ERR","INT_ERR","NOT_READY","NO_FILE",
        "NO_PATH","INVALID_NAME","DENIED","EXIST",
        "INVALID_OBJECT","WRITE_PROTECTED","INVALID_DRIVE",
        "NOT_ENABLED", "NO_FILE_SYSTEM", "MKFS_ABORTED",
        "TIMEOUT", "LOCKED", "NOT_ENOUGH_CORE", "TOO_MANY_OPEN_FILES",
        "INVALID_ARGUMENT","UNKNOWN"
    };

/////////////////////////
// Function Definitions
/////////////////////////

#if  defined(__PIC24FJ256GA106__) || defined(__PIC24FJ256GA606__) || defined(__PIC24FJ1024GA606__)

int iow_opendir(DIR_T **dp, const char *path)
{
    int retval=-1;

    if(NULL!=dp && NULL!=path){
        FRESULT fr = FR_OK;
        if( (fr=f_opendir(*dp,path))==FR_OK){
            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_readdir(DIR_T *dp, DIRENT_T **dest)
{
    int retval=-1;
    if(NULL!=dp && NULL!=dest){
        FRESULT fr=FR_OK;
        if((fr=f_readdir(dp,*dest))==FR_OK){
            iow_errno=IOW_OK;
            retval=0;
       }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }
    return retval;
}
int iow_closedir(DIR_T *dp)
{
    int retval=-1;

    if(NULL!=dp){
        FRESULT fr = FR_OK;
        if( (fr=f_closedir(dp))==FR_OK){
            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_fopen(FILE_T **fp, const char *path, iow_mode_t flags)
{
    int retval=-1;

    BYTE mode = 0;

    if( (flags&IOW_READ)!=0 ){
        mode|=FA_READ;
    }
    if( (flags&IOW_WRITE)!=0 ){
        mode|=FA_WRITE;
    }
    if( (flags&IOW_CREATE_NEW)!=0 ){
        mode|=FA_CREATE_NEW;
    }
    if( (flags&IOW_CREATE_ALWAYS)!=0 ){
        mode|=FA_CREATE_ALWAYS;
    }
    if( (flags&IOW_OPEN_ALWAYS)!=0 ){
        mode|=FA_OPEN_ALWAYS;
    }
    if( (flags&IOW_OPEN_EXISTING)!=0 ){
        mode|=FA_OPEN_EXISTING;
    }
    if( (flags&IOW_OPEN_APPEND)!=0 ){
        mode|=(FA_CREATE_NEW|FA_WRITE|FA_READ);
    }

    FRESULT fr = FR_OK;

    if( (fr=f_open(*fp,path,mode))==FR_OK){
        iow_errno=IOW_OK;
        retval=0;
    }else{
        // iow_error maps 1:1 to FatFS errors
        // no translation required
        iow_errno=(iow_error_t)fr;
    }

    return retval;
}

int iow_fclose(FILE_T *fp)
{
    int retval=-1;

    if(NULL!=fp){
        FRESULT fr = FR_OK;
        if( (fr=f_close(fp))==FR_OK){
            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_ftruncate(FILE_T *fp)
{
    int retval=-1;

    if(NULL!=fp){
        FRESULT fr = FR_OK;
        if( (fr=f_truncate(fp))==FR_OK){
            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_frename(const char *old, const char *new)
{
    int retval=-1;

    if(NULL!=old && NULL!=new){
        FRESULT fr=FR_OK;
        if((fr=f_rename(old,new))==FR_OK){
           iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_unlink(const char *path)
{
    int retval=-1;

    if(NULL!=path){
        FRESULT fr=FR_OK;
        if((fr=f_unlink(path))==FR_OK){
           iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

size_t iow_fread(FILE_T *fp, void *dest, size_t len)
{
    size_t retval=0;

    if(NULL!=fp && NULL!=dest){
        FRESULT fr = FR_OK;
        UINT br=0;
        UINT btr=len;
        if( (fr=f_read(fp,dest,btr,&br))==FR_OK){
            iow_errno=IOW_OK;
            retval=br;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

size_t iow_fwrite(FILE_T *fp, void *src, size_t len)
{
    size_t retval=0;

    if(NULL!=fp && NULL!=src){
        FRESULT fr = FR_OK;
        UINT br=0;
        uint32_t read_len=len;
        if( (fr=f_write(fp,src,read_len, &br))==FR_OK){
            iow_errno=IOW_OK;
            retval=read_len;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

char *iow_fgets(FILE_T *fp, void *dest, size_t len)
{
    char *retval=0;
    if(NULL!=fp && NULL!=dest){
        retval = f_gets (dest,  len, fp);
    }
    return retval;
}

int iow_fputs(FILE_T *fp, const char *src)
{
    int retval=EOF;
    if(NULL!=fp && NULL!=src){
        retval = f_puts (src, fp);
    }
    return retval;
}

uint32_t iow_ftell(FILE_T *fp)
{
    uint32_t retval=0;

    if(NULL!=fp ){
        DWORD pos=f_tell(fp);
        retval=pos;
        iow_errno=IOW_OK;
     }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_fseek(FILE_T *fp, uint32_t offset)
{
    int retval=-1;

    if(NULL!=fp ){
        FRESULT fr=FR_OK;

        if( (fr=f_lseek(fp,offset))==FR_OK){
	        retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

uint32_t iow_fsize(FILE_T *fp)
{
    uint32_t retval=-1;

    if(NULL!=fp ){
        DWORD size=f_size(fp);
        retval=(uint32_t)size;
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_feof(FILE_T *fp)
{
    int retval=-1;

    // does not set iow_errno unless bad file pointer

    if(NULL!=fp){
        if( !f_eof(fp) ){
            retval=0;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_ferror(FILE_T *fp)
{
    int retval=-1;

    if(NULL!=fp ){
        BYTE err=f_error(fp);
        retval=( (err==0) ? 0 : -1);

        // iow_error maps 1:1 to FatFS errors
        iow_errno=err;

    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

void iow_clearerr(FILE_T *fp)
{
	// not implemented for FatFS
}

const char *iow_fstrerror(int err)
{

    return (err>=0 && err<=IOW_UNKNOWN ? iow_strerr_mem[err] : iow_strerr_mem[IOW_UNKNOWN]);
}

void iow_delay_msec(uint32_t msec)
{
    unsigned int uitime=msec;
    tkDelayMs(uitime);
}

void iow_get_line(void *buf, size_t len)
{
    if(NULL!=buf && len>0){
        char *bp=(char *)buf;
        char *cur=bp;
        while(!uiGetLine(bp,len, &cur, SER_CTTY,(GL_CASE|GL_ECHO))){
            tkDelayMs(50);
            if((cur-bp)>=(len-1)){
                // buffer full
                memset(bp,'\0',len);
                cur=bp;
            }
        }
    }
}

int iow_get_char(bool block)
{
    int retval=0;
    byte ch=0;

    if(block){
        while(!serGetByte(SER_CTTY,&ch))iow_delay_msec(50);
    }else{
        serGetByte(SER_CTTY,&ch);
    }
    retval=ch;
    return retval;
}

int iow_fsstat(const char *path, uint32_t *r_avail, FSYS_T **fsys)
{
    int retval=-1;

    if(NULL!=path && NULL!=fsys ){
        FRESULT fr=FR_OK;

        uint32_t avail=0, *pavail=(NULL!=r_avail ? r_avail : &avail);

        if( (fr=f_getfree(path,pavail,fsys))==FR_OK){
	        retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // no translation required
            iow_errno=(iow_error_t)fr;
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

#elif defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__)

int iow_opendir(DIR_T **dp, const char *path)
{
    int retval=-1;

    if(NULL!=dp && NULL!=path){

        if( (*dp=opendir(path))!=NULL){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_readdir(DIR_T *dp, DIRENT_T **dest)
{
    int retval=-1;
    if(NULL!=dp && NULL!=dest){
        if( (*dest=readdir(dp))!=NULL){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
       }
    }
    return retval;
}

int iow_closedir(DIR_T *dp)
{
    int retval=-1;

    if(NULL!=dp){

        if( (retval=closedir(dp))!=0){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_fopen(FILE_T **fp, const char *path, iow_mode_t flags)
{
    int retval=-1;

    char mode_str[8] = {0};
    iow_errno=IOW_OK;

    // translate mode flags to POSIX mode
    // note only specific combinations of flags are valid
    if( flags==(IOW_READ) ){
        sprintf(mode_str,"r");
    }else if( flags==(IOW_READ | IOW_OPEN_EXISTING) ){
        sprintf(mode_str,"r");
    }else if( flags==( IOW_READ | IOW_WRITE  ) ){
        sprintf(mode_str,"r+");
    }else if( flags==(IOW_CREATE_ALWAYS | IOW_WRITE) ){
        sprintf(mode_str,"w");
    }else if( flags==(IOW_CREATE_ALWAYS | IOW_WRITE | IOW_READ ) ){
        sprintf(mode_str,"w+");
    }else if( flags==(IOW_OPEN_ALWAYS | IOW_WRITE | IOW_READ ) ){
        sprintf(mode_str,"rw+");
    }else if( flags==(IOW_OPEN_APPEND | IOW_WRITE) ){
        sprintf(mode_str,"a");
    }else if( flags==(IOW_OPEN_APPEND | IOW_WRITE | IOW_READ )){
        sprintf(mode_str,"a+");
    }else if( flags==(IOW_CREATE_NEW | IOW_WRITE )){
        sprintf(mode_str,"wx");
    }else if( flags==(IOW_CREATE_NEW | IOW_WRITE | IOW_READ )){
        sprintf(mode_str,"w+x");
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    if(iow_errno==IOW_OK){
        if( (*fp=fopen(path,mode_str))!=NULL){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }// else invalid mode

    return retval;
}

int iow_fclose(FILE_T *fp)
{
    int retval=-1;

    if(NULL!=fp){

        if( (retval=fclose(fp))!=0){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_ftruncate(FILE_T *fp)
{
    int retval=-1;

    if(NULL!=fp){
        int fd = fileno(fp);
        if( (retval=ftruncate(fd,0))!=0){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_frename(const char *old, const char *new)
{
    int retval=-1;
    if(NULL!=old && NULL!=new){
        if( rename(old,new)==0 ){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
       }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_unlink(const char *path)
{
    int retval=-1;
    if(NULL!=path){
        if( unlink(path)==0 ){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
       }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

size_t iow_fread(FILE *fp, void *dest, size_t len)
{
    size_t retval=0;

    if(NULL!=fp && NULL!=dest){
        // fread return *number of items*
        // so use len for number of items, size=1
        uint32_t fpos = iow_ftell(fp);
        uint32_t flen = iow_fsize(fp);
        size_t rlen = ((len+fpos)>flen ? (flen-fpos) : len);
//        fprintf(stderr,"dest %p fp %p len %"FMT_SZ" fpos %"FMT_U32" flen %"FMT_U32" rlen %"FMT_SZ"\r\n",dest,fp,len,fpos,flen,rlen);
        size_t read_len=fread(dest,1,rlen,fp);
        retval=read_len;

        // iow_error maps 1:1 to FatFS errors
        // translate posix errors
        iow_errno=IOW_POSIX2IOW_ERR(errno);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

size_t iow_fwrite(FILE *fp, void *dest, size_t len)
{
    size_t retval=0;

    if(NULL!=fp && NULL!=dest){
        size_t write_len=fwrite(dest,1,len,fp);
        retval=write_len;
        // iow_error maps 1:1 to FatFS errors
        // translate posix errors
        iow_errno=IOW_POSIX2IOW_ERR(errno);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

char *iow_fgets(FILE_T *fp, void *dest, size_t len)
{
    char *retval=0;
    if(NULL!=fp && NULL!=dest){
        retval=fgets(dest,len,fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_fputs(FILE_T *fp, const char *src)
{
    int retval=EOF;
    if(NULL!=fp && NULL!=src){
        retval = fputs (src, fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

uint32_t iow_ftell(FILE *fp)
{
    uint32_t retval=0;

    if(NULL!=fp ){
        long pos=0;
        if( (pos=ftell(fp))>=0){
            retval=(uint32_t)0+pos;
            iow_errno=IOW_OK;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_fseek(FILE *fp, uint32_t offset)
{
    int retval=-1;

    if(NULL!=fp ){
        long lofs=offset;

        if( fseek(fp,lofs,SEEK_SET)==0){
            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

uint32_t iow_fsize(FILE *fp)
{
    uint32_t retval=0;

    if(NULL!=fp ){
        struct stat stat_s;
        int test=0;
        int fd=fileno(fp);
        if( fd>0 && (test=fstat(fd, &stat_s))==0 ){
            size_t st_size = stat_s.st_size;
            retval = (uint32_t)0+st_size;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_feof(FILE *fp)
{
    int retval=-1;

    if(NULL!=fp){
        retval=feof(fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_ferror(FILE *fp)
{
    int retval=-1;

    if(NULL!=fp){
        retval=ferror(fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

void iow_clearerr(FILE *fp)
{
    if(NULL!=fp){
        // resets eof and error
        clearerr(fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
}
const char *iow_fstrerror(int err)
{

    return (err>=0 && err<=IOW_UNKNOWN ? iow_strerr_mem[err] : strerror(IOW_IOW2POSIX_ERR(err)));
}

void s_delay_ns(uint32_t nsec)
{
    long lnsec = nsec;
    struct timespec delay;
    struct timespec rem;
    delay.tv_sec= (lnsec/1000000000);
    delay.tv_nsec=(lnsec%1000000000);

    memset(&rem,0,sizeof(struct timespec));
//    OUT_ERR("%s:%d - s[%ld] ns[%ld]\r\n",__FUNCTION__,__LINE__,delay.tv_sec,delay.tv_nsec);
    while ( nanosleep(&delay,&rem)!=0) {
        memcpy(&delay,&rem,sizeof(struct timespec));
        memset(&rem,0,sizeof(struct timespec));
    }
}

void iow_delay_msec(uint32_t msec)
{
    uint32_t sec = msec/1000;
    uint32_t nsec = (msec%1000)*1000000;
//    OUT_ERR("%s:%d - s[%"PRIu32"] ns[%"PRIu32"]\r\n",__FUNCTION__,__LINE__,sec,nsec);
    uint32_t i=0;
    for(i=0;i<sec;i++)s_delay_ns(1e9);
    if(nsec>0)
    s_delay_ns(nsec);
 }

void iow_get_line(void *buf, size_t len)
{
    fgets(buf,len,stdin);
}

int iow_get_char(bool block)
{
    int retval=0;
    byte ch=0;

    if(block){
        retval=fgetc(stdin);
   }else{
        int tty = fileno(stdin);
        struct termios old;
        struct termios tmp;

        if (tcgetattr(tty, &old)){
             perror("failed to obtain tty attributes");
        }

        memcpy(&tmp, &old, sizeof(old));

        tmp.c_lflag &= ~(ICANON);
        tmp.c_cc[VMIN]=0;
        tmp.c_cc[VTIME]=0;

        if(tcsetattr(tty, TCSANOW, (const struct termios*) &tmp)){
            perror("failed to configure tty descriptor");
        }

        if(read(tty,&ch,1)==1){
            retval=ch;
        }

        // restore tty settings (optional?)
        tcsetattr(tty, TCSANOW, (const struct termios*) &old);
   }
    return retval;
}

int iow_fsstat(const char *path, uint32_t *r_avail, FSYS_T **fsys)
{
    int retval=-1;

    if(NULL!=path && NULL!=fsys && NULL!=*fsys ){

        if( statvfs(path,*fsys)==0){
            if(NULL!=r_avail){
                *r_avail= (*fsys)->f_bavail * (*fsys)->f_frsize;
            }

            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}
#elif defined(__WIN32) || defined(__WIN64)

int iow_opendir(DIR_T **dp, const char *path)
{
    int retval=-1;

    if(NULL!=dp && NULL!=path){
//
//        if( (*dp=opendir(path))!=NULL){
//            retval=0;
//        }else{
//            // iow_error maps 1:1 to FatFS errors
//            // translate posix errors
//            iow_errno=IOW_POSIX2IOW_ERR(errno);
//        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_readdir(DIR_T *dp, DIRENT_T **dest)
{
    int retval=-1;
    if(NULL!=dp && NULL!=dest){
//        if( (*dest=readdir(dp))!=NULL){
//            retval=0;
//        }else{
//            // iow_error maps 1:1 to FatFS errors
//            // translate posix errors
//            iow_errno=IOW_POSIX2IOW_ERR(errno);
//       }
    }
    return retval;
}

int iow_closedir(DIR_T *dp)
{
    int retval=-1;

    if(NULL!=dp){

//        if( (retval=closedir(dp))!=0){
//            retval=0;
//        }else{
//            // iow_error maps 1:1 to FatFS errors
//            // translate posix errors
//            iow_errno=IOW_POSIX2IOW_ERR(errno);
//        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_fopen(FILE_T **fp, const char *path, iow_mode_t flags)
{
    int retval=-1;

    char mode_str[8] = {0};
    iow_errno=IOW_OK;

    // translate mode flags to POSIX mode
    // note only specific combinations of flags are valid
    if( flags==(IOW_READ) ){
        sprintf(mode_str,"r");
    }else if( flags==(IOW_READ | IOW_OPEN_EXISTING) ){
        sprintf(mode_str,"r");
    }else if( flags==( IOW_READ | IOW_WRITE  ) ){
        sprintf(mode_str,"r+");
    }else if( flags==(IOW_CREATE_ALWAYS | IOW_WRITE) ){
        sprintf(mode_str,"w");
    }else if( flags==(IOW_CREATE_ALWAYS | IOW_WRITE | IOW_READ ) ){
        sprintf(mode_str,"w+");
    }else if( flags==(IOW_OPEN_ALWAYS | IOW_WRITE | IOW_READ ) ){
        sprintf(mode_str,"rw+");
    }else if( flags==(IOW_OPEN_APPEND | IOW_WRITE) ){
        sprintf(mode_str,"a");
    }else if( flags==(IOW_OPEN_APPEND | IOW_WRITE | IOW_READ )){
        sprintf(mode_str,"a+");
    }else if( flags==(IOW_CREATE_NEW | IOW_WRITE )){
        sprintf(mode_str,"wx");
    }else if( flags==(IOW_CREATE_NEW | IOW_WRITE | IOW_READ )){
        sprintf(mode_str,"w+x");
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    if(iow_errno==IOW_OK){
        if( (*fp=fopen(path,mode_str))!=NULL){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }// else invalid mode

    return retval;
}

int iow_fclose(FILE_T *fp)
{
    int retval=-1;

    if(NULL!=fp){

        if( (retval=fclose(fp))!=0){
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_frename(const char *old, const char *new)
{
    int retval=-1;
    if(NULL!=old && NULL!=new){
//        if( rename(old,new)==0 ){
//            retval=0;
//        }else{
//            // iow_error maps 1:1 to FatFS errors
//            // translate posix errors
//            iow_errno=IOW_POSIX2IOW_ERR(errno);
//       }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_unlink(const char *path)
{
    int retval=-1;
    if(NULL!=path){
//        if( unlink(path)==0 ){
//            retval=0;
//        }else{
//            // iow_error maps 1:1 to FatFS errors
//            // translate posix errors
//            iow_errno=IOW_POSIX2IOW_ERR(errno);
//       }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

size_t iow_fread(FILE *fp, void *dest, size_t len)
{
    size_t retval=0;

    if(NULL!=fp && NULL!=dest){
        // fread return *number of items*
        // so use len for number of items, size=1
        uint32_t fpos = iow_ftell(fp);
        uint32_t flen = iow_fsize(fp);
        size_t rlen = ((len+fpos)>flen ? (flen-fpos) : len);
//        fprintf(stderr,"dest %p fp %p len %"FMT_SZ" fpos %"FMT_U32" flen %"FMT_U32" rlen %"FMT_SZ"\r\n",dest,fp,len,fpos,flen,rlen);
        size_t read_len=fread(dest,1,rlen,fp);
        retval=read_len;

        // iow_error maps 1:1 to FatFS errors
        // translate posix errors
        iow_errno=IOW_POSIX2IOW_ERR(errno);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

size_t iow_fwrite(FILE *fp, void *dest, size_t len)
{
    size_t retval=0;

    if(NULL!=fp && NULL!=dest){
        size_t write_len=fwrite(dest,1,len,fp);
        retval=write_len;
        // iow_error maps 1:1 to FatFS errors
        // translate posix errors
        iow_errno=IOW_POSIX2IOW_ERR(errno);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

char *iow_fgets(FILE_T *fp, void *dest, size_t len)
{
    char *retval=0;
    if(NULL!=fp && NULL!=dest){
        retval=fgets(dest,len,fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_fputs(FILE_T *fp, const char *src)
{
    int retval=EOF;
    if(NULL!=fp && NULL!=src){
        retval = fputs (src, fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

uint32_t iow_ftell(FILE *fp)
{
    uint32_t retval=0;

    if(NULL!=fp ){
        long pos=0;
        if( (pos=ftell(fp))>=0){
            retval=(uint32_t)0+pos;
            iow_errno=IOW_OK;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_fseek(FILE *fp, uint32_t offset)
{
    int retval=-1;

    if(NULL!=fp ){
        long lofs=offset;

        if( fseek(fp,lofs,SEEK_SET)==0){
            iow_errno=IOW_OK;
            retval=0;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

uint32_t iow_fsize(FILE *fp)
{
    uint32_t retval=0;

    if(NULL!=fp ){
        struct stat stat_s;
        int test=0;
        int fd=fileno(fp);
        if( fd>0 && (test=fstat(fd, &stat_s))==0 ){
            size_t st_size = stat_s.st_size;
            retval = (uint32_t)0+st_size;
        }else{
            // iow_error maps 1:1 to FatFS errors
            // translate posix errors
            iow_errno=IOW_POSIX2IOW_ERR(errno);
        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

int iow_feof(FILE *fp)
{
    int retval=-1;

    if(NULL!=fp){
        retval=feof(fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
    return retval;
}

int iow_ferror(FILE *fp)
{
    int retval=-1;

    if(NULL!=fp){
        retval=ferror(fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

void iow_clearerr(FILE *fp)
{
    if(NULL!=fp){
        // resets eof and error
        clearerr(fp);
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }
}
const char *iow_fstrerror(int err)
{

    return (err>=0 && err<=IOW_UNKNOWN ? iow_strerr_mem[err] : strerror(IOW_IOW2POSIX_ERR(err)));
}

void s_delay_ns(uint32_t nsec)
{
//    long lnsec = nsec;
//    struct timespec delay;
//    struct timespec rem;
//    delay.tv_sec= (lnsec/1000000000);
//    delay.tv_nsec=(lnsec%1000000000);
//
//    memset(&rem,0,sizeof(struct timespec));
////    OUT_ERR("%s:%d - s[%ld] ns[%ld]\r\n",__FUNCTION__,__LINE__,delay.tv_sec,delay.tv_nsec);
//    while ( nanosleep(&delay,&rem)!=0) {
//        memcpy(&delay,&rem,sizeof(struct timespec));
//        memset(&rem,0,sizeof(struct timespec));
//    }
}

void iow_delay_msec(uint32_t msec)
{
//    uint32_t sec = msec/1000;
//    uint32_t nsec = (msec%1000)*1000000;
////    OUT_ERR("%s:%d - s[%"PRIu32"] ns[%"PRIu32"]\r\n",__FUNCTION__,__LINE__,sec,nsec);
//    uint32_t i=0;
//    for(i=0;i<sec;i++)s_delay_ns(1e9);
//    if(nsec>0)
//    s_delay_ns(nsec);
 }

void iow_get_line(void *buf, size_t len)
{
    fgets(buf,len,stdin);
}

int iow_get_char(bool block)
{
    int retval=0;
    byte ch=0;

//    if(block){
//        retval=fgetc(stdin);
//   }else{
//        int tty = fileno(stdin);
//        struct termios old;
//        struct termios tmp;
//
//        if (tcgetattr(tty, &old)){
//          perror("failed to obtain tty attributes");
//        }
//
//        memcpy(&tmp, &old, sizeof(old));
//
//        tmp.c_lflag &= ~ICANON;
//        tmp.c_cc[VMIN]=0;
//        tmp.c_cc[VTIME]=0;
//
//        if (tcsetattr(tty, TCSANOW, (const struct termios*) &tmp)){
//           perror("failed to configure tty descriptor");
//        }
//
//        if(read(tty,&ch,1)==1){
//            retval=ch;
//        }
//
//        // restore tty settings (optional?)
//        tcsetattr(tty, TCSANOW, (const struct termios*) &old);
//   }
    return retval;
}

int iow_fsstat(const char *path, uint32_t *r_avail, FSYS_T **fsys)
{
    int retval=-1;

    if(NULL!=path && NULL!=fsys && NULL!=*fsys ){

//        if( statvfs(path,*fsys)==0){
//            if(NULL!=r_avail){
//                *r_avail= (*fsys)->f_bavail * (*fsys)->f_frsize;
//            }
//
//            iow_errno=IOW_OK;
//            retval=0;
//        }else{
//            // iow_error maps 1:1 to FatFS errors
//            // translate posix errors
//            iow_errno=IOW_POSIX2IOW_ERR(errno);
//        }
    }else{
        iow_errno=IOW_INVALID_PARAMETER;
    }

    return retval;
}

#endif
