/**
 *  @file   disk.h
 *  @brief  ディレクトリ・エントリやファイル属性関係.
 *  @author tenk*
 *  @note
 *  -   win/linuxのディレクトリ・エントリやファイル属性関係の薄めのラッパー.
 *  -   関数の返り値がintなら、特に別記なければ、0がok. 負がエラー.
 *  -   大半がヘッダのみのinlineだが、一部、別の .c に実体を定義.
 *  -   Public Domain Software
 */
#ifndef DISK_H
#define DISK_H

#include <stddef.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// ファイル名のサイズ. 利用側のバッファ準備用.
#if defined _MAX_PATH
#define DISK_MAX_PATH   _MAX_PATH       ///< 通常のパス名の長さ.
#else
#define DISK_MAX_PATH   1024            ///< 通常のパス名の長さ. (仮)
#endif

#if defined _WIN32  // =======================================================
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/utime.h>
#ifndef __cplusplus
 #define inline __inline
#endif

typedef struct __stat64     disk_file_stat_t;
typedef struct __utimbuf64  disk_utimbuf_t;
typedef unsigned __int64    disk_time_t;

inline int      disk_rename(const TCHAR* oldp, const TCHAR* newp) { assert(oldp&&newp); return MoveFile( oldp, newp ) ? 0 : -1; }
inline int      disk_remove(const TCHAR* fname) { assert(fname); return DeleteFile( fname )         ? 0 : -1; }
inline int      disk_mkdir (const TCHAR* fname) { assert(fname); return CreateDirectory(fname,NULL) ? 0 : -1; }
inline int      disk_rmdir (const TCHAR* fname) { assert(fname); return RemoveDirectory( fname )    ? 0 : -1; }
inline int      disk_chdir (const TCHAR* fname) { assert(fname); return SetCurrentDirectory(fname)  ? 0 : -1; }
inline TCHAR*   disk_getcwd(TCHAR d[], size_t l) { assert(d&&l); return GetCurrentDirectory(l, d)   ? d : 0 ; }

inline int      disk_chmod (const TCHAR* fnm, unsigned m) { assert(fnm&&m); return _tchmod(fnm, m); }
inline int      disk_fileStat(const char* fnm, disk_file_stat_t* s) { assert(fnm&&s); return _stat64(fnm, s); }
inline int      disk_utime(const char* fnm, disk_utimbuf_t* u) { assert(fnm&&u); return _utime64(fnm, u); }

/// ファイルがあるか?
inline int      disk_fileExist(const TCHAR* f) { assert(f); return 0xFFFFFFFF != GetFileAttributes(f); }

/// ファイル名のフルパス化. (実際のカレントディレクトリを反映)
inline TCHAR*   disk_fullpath(TCHAR f[], size_t l, const TCHAR* s) { assert(f&&l&&s); return _fullpath(f, s, l); }

/// ファイルサイズ取得.
inline unsigned __int64  disk_fileSize(const TCHAR* f) {
                    WIN32_FIND_DATA  d; HANDLE h;
                    return f && ((h=FindFirstFile(f,&d)) != INVALID_HANDLE_VALUE) && FindClose(h)
                         ? (((unsigned __int64)d.nFileSizeHigh<<32)|d.nFileSizeLow) : 0;
                }

/// 時間の取得.     値はシステムに依存.
inline int      disk_getTime(const TCHAR* name, disk_time_t* pCr, disk_time_t* pAcs, disk_time_t* pWrt) {
                    HANDLE  hdl;
                    if (name && (hdl = CreateFile(name, GENERIC_READ , FILE_SHARE_READ, 0
                            , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) != INVALID_HANDLE_VALUE)
                    {
                        int r = (GetFileTime(hdl, (FILETIME*)pCr, (FILETIME*)pAcs, (FILETIME*)pWrt) );
                        CloseHandle(hdl);
                        return r ? 0 : -1;
                    }
                    return 0;
                }

/// 時間の設定.     値はシステム依存.
inline int      disk_setTime(const TCHAR* name, disk_time_t crt, disk_time_t acs, disk_time_t wrt) {
                    int    rc  = 0;
                    HANDLE hdl = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
                    if (hdl != INVALID_HANDLE_VALUE) {
                        rc = SetFileTime(hdl, (FILETIME*)&crt, (FILETIME*)&acs, (FILETIME*)&wrt);
                        CloseHandle(hdl);
                    }
                    return rc ? 0 : -1;
                }

#if 1   // windows のみの専用.
/// exeのパス名取得.      返値0:ok  負:エラー. 詳細はdisk_lastError()から取得.
inline TCHAR*   disk_getExePath(TCHAR nameBuf[], size_t nameBufSize)
                { return GetModuleFileName(0, nameBuf, nameBufSize) ? nameBuf : 0; }

/// システムディレクトリの取得.   返値0:ok  負:エラー. 詳細はdisk_lastError()から取得.
inline TCHAR*   disk_getSystemDir(TCHAR nameBuf[], size_t nameBufSize)
                { return GetSystemDirectory(nameBuf, nameBufSize) ? nameBuf: 0; }

/// windowsディレクトリの取得.    返値0:ok  負:エラー. 詳細はdisk_lastError()から取得.
inline TCHAR*   disk_getWindowsDir(TCHAR nameBuf[], size_t nameBufSize)
                { return GetWindowsDirectory(nameBuf, nameBufSize) ? nameBuf: 0; }
#endif


#else   // linux ==============================================================

//#define _LARGEFILE64_SOURCE   //きっとここで定義するんじゃなくてコンパイラ引数で.
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <utime.h>
#ifndef _T
 #define TCHAR  char
 #define _T(x)  x
#endif

typedef struct stat         disk_file_stat_t;
typedef struct utimbuf      disk_utimbuf_t;
typedef time_t              disk_time_t;

inline int      disk_rename(const TCHAR* oldp, const TCHAR* newp) { assert(oldp&&newp); return rename( oldp, newp ); }
inline int      disk_remove(const TCHAR* fname) { assert(fname); return remove(fname); }
inline int      disk_mkdir (const TCHAR* fname) { assert(fname); return mkdir(fname, 0777); }
inline int      disk_rmdir (const TCHAR* fname) { assert(fname); return rmdir(fname); }
inline int      disk_chdir (const TCHAR* fname) { assert(fname); return chdir(fname); }
inline TCHAR*   disk_getcwd(TCHAR d[], size_t l) { assert(d&&l); return getcwd(d, l); }

inline int      disk_chmod (const TCHAR* fnm, unsigned m) { assert(fnm&&m); return chmod(fnm, m); }
inline int      disk_fileStat(const char* f, disk_file_stat_t* s) { assert(f&&s); return stat(f, s); }
inline int      disk_utime (const char* f, disk_utimbuf_t* u) { assert(f&&u); return utime(f, u); }

/// ファイルがあるか?
inline int      disk_fileExist(const TCHAR* f)
                {int h;assert(f);return ((h=open(f,O_RDONLY/*|O_BINARY*/,0766)) >= 0) ? (close(h),1) : 0;}

/// ファイル名のフルパス化. (実際のカレントディレクトリを反映. gnuライブラリ拡張機能を使用.)
inline TCHAR*   disk_fullpath(TCHAR f[], size_t l, const TCHAR* p)
                { assert(f&&l&&p); return (p=realpath(p,0)) ? (f[--l]=0, strncpy(f,p,l),free((void*)p), (TCHAR*)p) : 0; }

/// ファイルサイズ取得.
inline uint64_t disk_fileSize(const TCHAR* fnm)
                {struct stat s; assert(fnm); return (!stat(fnm, &s)) ? s.st_size : 0; }

/// 時間の取得.     値はシステムに依存.
inline int      disk_getTime(const TCHAR* name, disk_time_t* pCr, disk_time_t* pAcs, disk_time_t* pWrt) {
                    struct stat st;
                    if (name && stat(name, &st) == 0) {
                        if (pWrt) *pWrt = st.st_mtime;
                        if (pAcs) *pAcs = st.st_atime;
                        if (pCr ) *pCr  = st.st_ctime;
                        return  0;
                    } else {
                        return -1;
                    }
                }

/// 時間の設定.     createTimeは無視する.
inline int      disk_setTime(const TCHAR* name, disk_time_t createTime, disk_time_t acs, disk_time_t wrt) {
                    struct utimbuf u = { 0 };
                    createTime;
                    u.actime    = acs;
                    u.modtime   = wrt;
                    return utime(name, &u);
                }
#endif  // ==================================================================


#endif  // DISK_H