/**
 *  @file   file_hdl.h
 *  @brief  ファイルハンドルを用いる ファイルapi の薄いラッパー.
 *  @author tenk* (Masashi Kitamura)
 *  @note
 *  -   win と linux(unix)系を対象(想定).
 *  -   基本的に unix 系の関数仕様に似せている.
 *      このため成功/失敗は 0/負値 にしているので注意.
 *  -   コピペ利用の元ネタとしても想定しているため、型名等はos固有のまま記述.
 *  -   winのHANDLEで標準入出力は0,1,2ではないので、マクロ名を使う必要あり.
 *  -   Public Domain Software
 */

#ifndef FILE_HDL_H_INCLUDED
#define FILE_HDL_H_INCLUDED

#if defined _WIN32  // --------------------------------------------------------
#include <stddef.h>
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#include <assert.h>
#include <malloc.h>
#include <stdarg.h>

#ifndef __cplusplus
 #define inline __inline
#endif

// 使う側が移植性を考慮する場合に利用する型名等.
typedef HANDLE              FILE_HDL;
typedef unsigned __int64    file_size64_t;
typedef __int64             file_off_t;
typedef unsigned __int64    file_time_t;
#define FILE_ERR_HDL        INVALID_HANDLE_VALUE    // (-1)
#define FILE_HDL_STDIN      GetStdHandle(STD_INPUT_HANDLE)
#define FILE_HDL_STDOUT     GetStdHandle(STD_OUTPUT_HANDLE)
#define FILE_HDL_STDERR     GetStdHandle(STD_ERROR_HANDLE)

// ファイルオープン, r,w,rp,wp はfopenの"rb","wb","rb+","wb+"に相応.
inline HANDLE   file_open_r (const TCHAR* nm) { assert(nm); return CreateFile(nm, GENERIC_READ , FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); }
inline HANDLE   file_open_w (const TCHAR* nm) { assert(nm); return CreateFile(nm, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); }
inline HANDLE   file_open_rp(const TCHAR* nm) { assert(nm); return CreateFile(nm, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); }
inline HANDLE   file_open_wp(const TCHAR* nm) { assert(nm); return CreateFile(nm, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); }

inline void     file_close(HANDLE hdl)                         { if (hdl != INVALID_HANDLE_VALUE) CloseHandle(hdl); }
inline size_t   file_read(HANDLE h, void* b, size_t sz)        { DWORD r=0; assert(h!=INVALID_HANDLE_VALUE&&b&&sz); if (!ReadFile(h,b,sz,&r,0)) r=0; return r; }
inline size_t   file_write(HANDLE h, const void* b, size_t sz) { DWORD r=0; assert(h!=INVALID_HANDLE_VALUE&&b&&sz); if (!WriteFile(h,b,sz,&r,0)) r=0; return r; }
inline int      file_flush(HANDLE h)                           { assert(h!=INVALID_HANDLE_VALUE); return FlushFileBuffers(h) ? 0 : -1; }
//inline int    file_seek32(HANDLE h, int ofs, int mode)       { assert(h!=INVALID_HANDLE_VALUE); return SetFilePointer(h, (LONG)ofs, 0, mode); }
inline __int64  file_seek(HANDLE h, __int64 ofs, int mode)     { assert(h!=INVALID_HANDLE_VALUE); return SetFilePointerEx(h, *(LARGE_INTEGER*)&ofs, (LARGE_INTEGER*)&ofs, mode) ? ofs : 0; }
inline __int64  file_tell(HANDLE h)                            { assert(h!=INVALID_HANDLE_VALUE); return file_seek(h, 0, FILE_CURRENT); }
inline unsigned __int64 file_getSize(HANDLE h)                 { assert(h!=INVALID_HANDLE_VALUE); unsigned __int64 l = 0; return GetFileSizeEx(h, (LARGE_INTEGER*)&l) ? l : 0; }

// 時間の取得. 成功したら 0, 失態したら負を返す.
inline int      file_getTime(HANDLE h, unsigned __int64* pCreat, unsigned __int64* pLastAcs, unsigned __int64* pLastWrt) {
    assert(h != INVALID_HANDLE_VALUE);
    return GetFileTime(h, (FILETIME*)pCreat, (FILETIME*)pLastAcs, (FILETIME*)pLastWrt) ? 0 : -1;
}

// 時間の設定. 成功したら 0, 失態したら負を返す.
inline int      file_setTime(HANDLE h, unsigned __int64 creatTim, unsigned __int64 lastAcs, unsigned __int64 lastWrt) {
    assert(h != INVALID_HANDLE_VALUE);
    return SetFileTime(h, (FILETIME*)&creatTim, (FILETIME*)&lastAcs, (FILETIME*)&lastWrt) ? 0 : -1;
}


#else   // linux(unix) //----------------------------------------------------

#define _LARGEFILE64_SOURCE         // include順番を気をつけないと乗っ取れない...
#define _FILE_OFFSET_BITS   64      // include順番を気をつけないと乗っ取れない...
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdarg.h>

// 使う側が移植性を考慮する場合に利用する型名等.
#define FILE_ERR_HDL    (-1)
typedef int             FILE_HDL;
typedef uint64_t        file_size64_t;
typedef off64_t         file_off_t;
typedef time_t          file_time_t;

#define FILE_HDL_STDIN      0
#define FILE_HDL_STDOUT     1
#define FILE_HDL_STDERR     2

// ファイルオープン, r,w,rp,wp はfopenの"rb","wb","rb+","wb+"に相応.
inline int      file_open_r (const char* nm) { assert(nm); return open(nm, O_RDONLY, 0766); }
inline int      file_open_w (const char* nm) { assert(nm); return open(nm, O_WRONLY|O_CREAT|O_TRUNC, 0766); }
inline int      file_open_rp(const char* nm) { assert(nm); return open(nm, O_RDWR, 0766); }
inline int      file_open_wp(const char* nm) { assert(nm); return open(nm, O_RDWR|O_CREAT, 0766); }

inline void     file_close(int hdl)                           { if (hdl != -1) close(hdl); }
inline size_t   file_read(int h, void* buf, size_t sz)        { assert(h!=-1 && buf && sz); return read(h, buf, sz); }
inline size_t   file_write(int h, const void* buf, size_t sz) { assert(h!=-1 && buf && sz); return write(h, buf, sz); }
inline int      file_flush(int h)                             { h; return 0; }  // ダミー.

// ほんとに 64ビットを有効になっているかは環境の設定しだい.
inline int64_t  file_seek(int h, int64_t ofs, int mode) { assert(h!=-1); return lseek64(h,ofs,mode); }
inline int64_t  file_tell(int h)                        { assert(h!=-1); return file_seek(h, 0, 1/*seek_cur*/); }
inline uint64_t file_getSize(int h)                     { assert(h!=-1); struct stat st; return fstat(h, &st)==0 ? st.st_size : 0; }

/// 時間の取得. 値はシステムに依存.
inline int      file_getTime(int h, time_t* pCreat, time_t* pLastAcs, time_t* pLastWrt) {
    struct stat st;
    int         rc;
    assert(h != 0);
    rc = fstat(h, &st);
    if (rc == 0) {
        if (pLastWrt) *pLastWrt = st.st_mtime;
        if (pLastAcs) *pLastAcs = st.st_atime;
        if (pCreat  ) *pCreat   = st.st_ctime;
    }
    return rc;
}

/// 時間の設定. creatTimは無視するので注意.
inline int      file_setTime(int h, time_t creatTim, time_t lastAcs, time_t lastWrt) {
    struct timeval tv[2];
    assert(h != 0);
    creatTim;
    tv[0].tv_sec  = lastAcs;
    tv[0].tv_usec = 0;
    tv[1].tv_sec  = lastWrt;
    tv[1].tv_usec = 0;
    return futimes(h, tv);
}
#endif  // _WIN32   // -------------------------------------------------------


// 文字列出力. ※ \n はwinでも生の\nなので注意.
inline size_t   file_puts(const char* s, FILE_HDL h) { return file_write(h, s, strlen(s)); }

#if 1
#include <stdio.h>

// printf書式の文字列出力. ※ \n はwinでも生の\nなので注意.
inline size_t   file_printf(FILE_HDL h, const char* fmt, ...) {
    char    buf[ 2048 ], *s = buf, *p = s;
    size_t  bufSz = 2048 , n;
    assert(h!=(FILE_HDL)-1 && fmt);
    do {va_list  a;
        va_start(a,fmt);
        n = vsnprintf((s=p), bufSz, fmt, a);
        va_end(a);
    } while (n >= bufSz-1 && (p = (char*)alloca((bufSz *= 2))) != 0);
    return file_write(h, s, n);
}
#endif


#endif  // FILE_HDL_INCLUDED