/*
    zfile.c - file utilities
    Copyright (C) 2011-2018 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

    UTF-8 detect: ěščřžýá

*/

#include <libziaint.h>

#ifdef Z_HAVE_UNISTD_H
#include <fcntl.h>
#include <unistd.h>
#endif

#include <zfile.h>
#include <zpath.h>
#include <string.h>

#include <glib.h>
#ifdef Z_HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef Z_HAVE_WINDOWS_H
#include <windows.h>
#endif

#ifdef Z_HAVE_IO_H
#include <io.h>
#endif
#ifdef Z_HAVE_ERRNO_H
#include <errno.h>
#endif

#ifdef Z_ANDROID
#include <sys/file.h>
#endif

#ifdef Z_MSC
#pragma warning(disable : 4996)
#endif

gchar *zfile_fgets(GString *gs, FILE *f, int stripcomment){
    char s[102], *c;
    int len;
    g_string_truncate(gs, 0);

    while (1){
        c = fgets(s, 100, f);
		if (!c) {
			if (gs->len > 0) break;
			return NULL;
		}
        
        len = strlen(s);
        if (len>0 && s[len-1]=='\n'){ /* MAC ?*/
            s[len-1]='\0';
            if (len>1 && s[len-2]=='\r') s[len-2]='\0';
            g_string_append(gs, s);
            break;
        }
        g_string_append(gs, s);
    }
    
    if (stripcomment){
        char *c;

        c = strchr(gs->str, '#');
        if (c) g_string_truncate(gs, c - gs->str);
    }
        
     /*dbg("%3d '%s'\n",gs->len, gs->str);*/
    return gs->str;    
}


gchar *zfile_mgets(GString *gs, const char *file, long int *pos, const long int len, int stripcomment){
	const char *c;

    /*dbg("safe_mgets(%d,%d,%d)\n", *pos, len, stripcomment);*/
	g_string_truncate(gs, 0);
    
    if (*pos>=len) return NULL;

	for (c=file+(*pos); c!=file+len; c++,(*pos)++){
		switch(*c){
			case '\r':
				continue;
			case '\n':
				c++;(*pos)++;
				goto brk;
		}
		g_string_append_c(gs, *c);
	}
brk:;  
/*    dbg("brk at %d\n", *pos);*/
    if (stripcomment){
        char *c;

        c = strchr(gs->str, '#');
        if (c) g_string_truncate(gs, c - gs->str);
    }
//    dbg("safe_mgets: %3d '%s' *pos=%d len=%d\n",gs->len, gs->str, *pos, len);
    if (*pos>len) return NULL;
    return gs->str;    
}


double z_df(const char *filename){
    double df = -1;

#if defined(HAVE_SYS_VFS_H)
    struct statfs sf;

    if (statfs(filename, &sf)) return -1.0;

    df = (double)sf.f_bsize * (double)sf.f_bavail;
/*    dbg("bsize=%ld, bavail=%ld, ret=%f\n", sf.f_bsize, sf.f_bavail, ret);*/
#elif defined (Z_MSC_MINGW)
    ULARGE_INTEGER FreeBytesAvailable;
	char *wfn;
	
	wfn = g_strdup(filename);

	z_wokna(wfn);
	z_dirname(wfn);
	if (strncmp(wfn, "\\", 2) == 0){
		char *c = wfn;
		wfn = g_strconcat(c, "\\", NULL);
		g_free(c);
	}
	if (GetDiskFreeSpaceEx(wfn, &FreeBytesAvailable, NULL, NULL)) {
		df = (double)FreeBytesAvailable.QuadPart;
	}else{
		int x = GetLastError();
		int y = x;
	}
	g_free(wfn);
	// (GetDiskFreeSpaceEx("C:\\", &FreeBytesAvailable, NULL, NULL)) df = (double)FreeBytesAvailable.QuadPart;
#endif    

	return df;
}




int z_lockf(int fd, int cmd, off_t ofs, off_t len){
#ifdef Z_MSC_MINGW
	HANDLE h;
	OVERLAPPED ov;
	
    //zinternal("lockf neotestována");
	h = (HANDLE)_get_osfhandle(fd);
	if (h == INVALID_HANDLE_VALUE) {
		errno = EBADF;
		return -1;
	}

	memset(&ov, 0, sizeof(OVERLAPPED));
	ov.Offset = ofs;
	if (!LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, len, 0, &ov)) {
		return -1;													
	}

	if (cmd == F_TEST){
		UnlockFile(h, ofs, 0, len, 0);
	}
	return 0;
#elif defined(Z_ANDROID)
    int ret;
    switch(cmd){
        case F_LOCK: //exclusive
            return flock(fd, LOCK_EX);
        case F_TLOCK: // non block, returns error if is locked
            return flock(fd, LOCK_EX | LOCK_NB);
        case F_ULOCK: // unlock
            return flock(fd, LOCK_UN);
        case F_TEST:  // 0=unlocked or locked by me, -1=another process hold lock
            ret = flock(fd, LOCK_EX | LOCK_NB); 
            if (ret != 0) return ret;
            return flock(fd, LOCK_UN);
        default:
            return -1;
    }
#else
	return lockf(fd, cmd, len);
#endif
}

int z_ftruncate(int fd, off_t length){
#ifdef Z_MSC_MINGW
	HANDLE h;
	LARGE_INTEGER large;
	
    //zinternal("lockf neotestována");
	h = (HANDLE)_get_osfhandle(fd);
	if (h == INVALID_HANDLE_VALUE) {
		errno = EBADF;
		return -1;
	}
	large.QuadPart = length;
	if (!SetFilePointerEx(h, large, NULL, FILE_BEGIN)){
		errno = EIO;
		return -1;
	}
	if (!SetEndOfFile(h)){
		errno = EIO;
		return -1;
	}
	return 0;
#else
	return ftruncate(fd, length);
#endif
}

off_t zfile_flen(FILE *f){
	off_t pos, len;
	int ret;

	pos = ftell(f);
	if (pos < 0L) return pos;

	ret = fseek(f, 0L, SEEK_END);
	if (ret < 0) return ret;

	len = ftell(f);
	if (len < 0L) return len;

	ret = fseek(f, pos, SEEK_SET);
	return len;
}

char *z_format_bytes(char *s, int size, long b){

    if (b < 10240){
        g_snprintf(s, size, "%ldB", b);
    }else if (b < 10240*1024){
        g_snprintf(s, size, "%.1fKB", b/1024.0);
    }else{
        g_snprintf(s, size, "%.2fMB", b/(1024.0*1024.0));
    }
    return s;
}

char *zfile_read_textfile(char *filename){
	GString *gs;
	char *ret;
	char buf[65536+1];
	FILE *f;

	z_wokna(filename);

	f = fopen(filename, "rt");
	if (f == NULL) return NULL;

	gs = g_string_sized_new(zfile_flen(f));
	while (1){
		int rd = fread(buf, 1, sizeof(buf) - 1, f);
		if (rd < 0){
			fclose(f);
			g_string_free(gs, TRUE);
			return NULL;
		}
		if (rd == 0) break;
		buf[rd] = '\0';
		g_string_append(gs, buf);
	}
	fclose(f);
	ret = g_strdup(gs->str);
	g_string_free(gs, TRUE);
	return ret;
}

int zfile_printfile(const char *filename, const char *fmt, ...){
    va_list va;
    char *c;
	int len, wt;

	FILE *f = fopen(filename, "wb");
	if (f == NULL) return -1;
    
	va_start(va, fmt);
    c = g_strdup_vprintf(fmt, va);
    va_end(va);

	len = strlen(c);
	wt = fwrite(c, 1, len, f);
	g_free(c);
	if (wt != len) {
        fclose(f);
        return -1;
    }

	fclose(f);
	return wt;
}
