/*
    zerror - error handling
    Copyright (C) 2011-2020 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.

*/

#include <libziaint.h>

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <string.h>

#include <zerror.h>
#include <zstr.h>

#include <stdio.h>
#include <string.h>
#include <glib.h>

#include <errno.h>

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

#ifdef Z_HAVE_NETDB_H
#include <netdb.h>
#endif

#ifdef Z_MINGW
static GStaticMutex strerror_mutex = G_STATIC_MUTEX_INIT; 
#endif

#ifdef Z_MSC
char *strerror_r(int err_no, char *buf, size_t size){
	strerror_s(buf, size, err_no);
	return buf;
}
#endif

#ifdef Z_MINGW
char *strerror_r(int err_no, char *buf, size_t size){
    char *c;

    g_static_mutex_lock(&strerror_mutex);
    c = strerror(err_no);
    g_strlcpy(buf, c, size);
    g_static_mutex_unlock(&strerror_mutex);
	return buf;
}
#endif

char *z_strdup_strerror(int err){
	char errbuf[1024];
    char *c;
		
#ifdef Z_MSC
	strerror_s(errbuf, sizeof(errbuf), err);
    c = g_strdup(errbuf);
#elif defined(Z_MINGW)
    g_static_mutex_lock(&strerror_mutex);
    c = g_strdup(strerror(err));
    g_static_mutex_unlock(&strerror_mutex);
#elif defined (Z_STRERROR_R_RETURNS_INT)
    strerror_r(err, errbuf, sizeof(errbuf));
    return g_strdup(errbuf);
#else
    c = g_strdup(strerror_r(err, errbuf, sizeof(errbuf)));
#endif

	return c;
}

void z_strerror(GString *gs, int err){
	char errbuf[1024];

#ifdef Z_MSC
	strerror_s(errbuf, sizeof(errbuf), err);
    g_string_append(gs, errbuf);
#elif defined(Z_MINGW)
    g_static_mutex_lock(&strerror_mutex);
    g_string_append(gs, strerror(err));
    g_static_mutex_unlock(&strerror_mutex);
#elif defined(Z_STRERROR_R_RETURNS_INT)
    strerror_r(err, errbuf, sizeof(errbuf));
    g_string_append(gs, errbuf);
#else
    g_string_append(gs, strerror_r(err, errbuf, sizeof(errbuf)));
#endif
}

#ifdef Z_MSC_MINGW_CYGWIN
void z_lasterror(GString *gs){
	z_lasterror_e(gs, GetLastError());
}

void z_lasterror_e(GString *gs, int err){
	char *str;

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		err,
		0, // Default language
		(LPTSTR) &str,
		0,
		NULL);

	if (!str){
		g_string_append_printf(gs, "Windows error %d", err);
	}else{
		z_1250_to_8859_2(str);
		z_strip_crlf(str);
	}
	g_string_append(gs, str);
	LocalFree(str);
}
#endif

const char *z_host_error(void){
#ifdef Z_MSC_MINGW
    switch (WSAGetLastError()){
        case WSANOTINITIALISED:
            return "WSA not initialised";
        case WSAENETDOWN:
            return "The network subsystem has failed.";
        case WSAHOST_NOT_FOUND:              
            return "Host not found";
        case WSATRY_AGAIN:
            return "Temporary name server error";
        case WSANO_RECOVERY:
            return "Name server error";
        case WSANO_DATA:
            return "Host have no IP address";
        case WSAEINPROGRESS:
            return "Operation is in progress";
        case WSAEFAULT:
            return "Invalid hostname";
        case WSAEINTR:
            return "Operation canceled";
        default:
            return "Unknown error";
    }
#else
    switch (h_errno){
        case HOST_NOT_FOUND:
            return "Host not found";
        //case NO_ADDRESS:
        case NO_DATA:
            return "Host have no IP address";
        case NO_RECOVERY:
            return "Name server error";
        case TRY_AGAIN:
            return "Temporary name server error";
        default:
            return "Unknown error";
    }
#endif
}

const char *z_sock_strerror_func(int err, char *errbuf, int size){
#ifdef Z_MSC_MINGW
	char *str;
	int wsaerr = WSAGetLastError();

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		err,
		0, // Default language
		(LPTSTR) &str,
		0,
		NULL);
	z_strip_crlf(str);
	z_1250_to_8859_2(str);
    g_strlcpy(errbuf, str, size);
	LocalFree(str);
    return errbuf;
#elif defined(Z_STRERROR_R_RETURNS_INT)
    strerror_r(err, errbuf, size);
    return g_strdup(errbuf);
#else
    char *ret = strerror_r(errno, errbuf, size);
//    dbg("strerror_r: ret=%p '%s'  errbuf=%p '%s'\n", ret, ret, errbuf, errbuf);
    return ret;
#endif
}
