/* vim: set ts=8 sts=4 sw=4 tw=80 noet: */
/*======================================================================
Copyright (C) 2004,2005,2009,2013 Walter Doekes <walter+tthsum@wjd.nu>
This file is part of tthsum.

tthsum 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.

tthsum 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.

You should have received a copy of the GNU General Public License
along with tthsum.  If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "texts.h"

#include <assert.h>

#ifdef _WIN32
#    define WINDOWS_LEAN_AND_MEAN
#    include <windows.h>
#else /* !_WIN32 */
#    include <stdio.h>
#    include <string.h>
#    include <errno.h>
#endif /* !_WIN32 */


static int last_error = 0;
static int last_error_os = 0;
static const char* last_error_context = 0;
#define LAST_ERROR_SIZE 512
static char last_error_buf[LAST_ERROR_SIZE];

static const char* texts[] = {
    /* ERROR_FIRST */
    "No error yet",
    /* BASE32_INVALID_CHARACTER */
    "Invalid character encountered in BASE32 stream",
    /* ESCAPE_INVALID_ESCAPE */
    "Invalid escape sequence encountered",
    /* TEXTS_UNKNOWN_ERROR */
    "Unknown error",
    /* THEX_INVALID_BLOCK_SIZE */
    "The read operation isn't supplying multiples of 1024",
    /* TTHSUM_FILENAME_TOO_LARGE */
    "File name is too long",
    /* TTHSUM_LINE_CORRUPT */
    "Improperly formatted line",
    /* TTHSUM_MISMATCHED_TTH */
    "TTH check failed for",
    /* UTF8_INVALID_UNICODE */
    "Invalid character encountered in UNICODE stream",
    /* UTF8_INVALID_UTF8 */
    "Invalid character encountered in UTF8 stream",
    /* UTF8_OVERLONG_UTF8 */
    "Overlong character encountered in UTF8 stream"
    /* ERROR_LAST */
};

void set_error(const char* context, int error) {
    assert(strlen(context) < 32);
    if (error == -1) {
#ifdef _WIN32
	last_error = GetLastError();
#else /* !_WIN32 */
	last_error = errno;
#endif /* !_WIN32 */
	last_error_os = 1;
    } else {
	last_error = error;
	last_error_os = 0;
    }
    last_error_context = context;
}

static int my_strcpy(char* dst, const char* src, int size) {
    char *d = dst;

    if (size <= 0)
        return 0; /* no warning when truncating */

    while (--size != 0 && *src != '\0') {
	*d++ = *src++;
    }
    *d = '\0';

    return d - dst; /* how many bytes we wrote excluding NUL */
}

const char* get_error() {
    int i;
#ifdef _WIN32
    DWORD w;
#endif /* !_WIN32 */

    /* Avoid snprintf because it's not C90 */
    if (!last_error_os) {
	i = my_strcpy(last_error_buf, last_error_context, LAST_ERROR_SIZE);
	i += my_strcpy(last_error_buf + i, ": ", LAST_ERROR_SIZE - i);
	my_strcpy(last_error_buf + i, texts[last_error], LAST_ERROR_SIZE - i);
	return last_error_buf;
    }

    i = my_strcpy(last_error_buf, last_error_context, LAST_ERROR_SIZE);
    i += my_strcpy(last_error_buf + i, ": ", LAST_ERROR_SIZE - i);
#ifdef _WIN32
    if ((w = FormatMessage(
	    FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
	    NULL, (DWORD)last_error, 0, last_error_buf + i,
	    LAST_ERROR_SIZE - 32 - i, NULL)) != 0) {
	if (w >= 2) /* 32 is more than enough for " (0xINT)" */
	    sprintf(last_error_buf + i + w - 2, " (0x%x)", last_error);
    } else {
	i += my_strcpy(last_error_buf + i, get_text(TEXTS_UNKNOWN_ERROR),
		LAST_ERROR_SIZE - i);
	if (LAST_ERROR_SIZE - i >= 32) {
	    sprintf(last_error_buf + i, " (0x%x)", (unsigned)last_error);
	}
    }
#else /* !_WIN32 */
    i += my_strcpy(last_error_buf + i, strerror(last_error),
	    LAST_ERROR_SIZE - i);
    if (LAST_ERROR_SIZE - i >= 32) { /* 32 is more than enough */
	sprintf(last_error_buf + i, " (%i)", last_error);
    }
#endif /* !_WIN32 */
    return last_error_buf;
}

const char* get_text(enum text_id id) {
    return texts[id];
}
