/* Written by Krzysztof Kowalczyk (http://blog.kowalczyk.info)
   The author disclaims copyright to this source code. */
#include "log_util.h"
#include "str_util.h"

/* Simple logging. 'slog' stands for "simple logging". I figured that 'log'
   is a very common name so used something else, but still short as a prefix
   for API names */

/* TODO: slogfmt(const char *fmt, ...) */

/* TODO: extend to more than one file, by keeping a list of file names */
const TCHAR * g_cur_fileName = NULL;

/* initialize logging system, should be called before any logging calls */
BOOL slog_init(void)
{
    /* do nothing yet */
    return TRUE;
}

/* deinitialize logging system. Should be called before the program quits */
void slog_deinit(void)
{
    slog_file_log_stop(NULL);
}

/* start logging to a file 'fileName'. From now on until slog_file_log_stop()
   all slog* logging will also go to a file. If a file of that name already
   exists, it'll overwrite it. */
BOOL slog_file_log_start(const TCHAR *fileName)
{
    if (!fileName) return FALSE;
    g_cur_fileName = tstr_dup(fileName);
    DeleteFile(fileName);
    return TRUE;
}

/* like 'slog_file_log_start' but will create a unique file based on 'fileName'.
   If a 'fileName' has extension, it'll try the first available
   '$file-$NNN.$ext' file (e.g. "my-app-log-000.txt") if 'fileName' is "my-app-log.txt"
   If there is no extension, it'll be '$file-$NNN' */
int slog_file_log_unique_start(const TCHAR *fileName)
{
    assert(0); /* not implemented */
    return FALSE;
}

void slog_file_log_stop(const TCHAR *fileName)
{
    /* 'fileName' is currently unused. The idea is that it should match the
        name given to slog_file_log_start */
    if (g_cur_fileName) {
        free((void*)g_cur_fileName);
        g_cur_fileName = NULL;
    }
}

/* log 'txt' to all currently enabled loggers */
void slog_str(const char *txt)
{
    DWORD   to_write_cb;
    DWORD   written_cb;
    int     f_ok;
    HANDLE  fh;

    if (!txt) return;

    if (!g_cur_fileName) return;

    /* we're using this inefficient way of re-opening the file for each
       log so that we can also watch this file life using tail-like program */
    fh = CreateFile(g_cur_fileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,  
            OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,  NULL); 
    if (INVALID_HANDLE_VALUE == fh)
        return;
    SetFilePointer(fh, 0, NULL, FILE_END);
    to_write_cb = (DWORD)strlen(txt);
    f_ok = WriteFile(fh, (void*)txt, to_write_cb, &written_cb, NULL);
    assert(f_ok && (written_cb == to_write_cb));
    CloseHandle(fh);
}

void slog_str_printf(const char *format, ...)
{
    char *      tmp;
    va_list     args;

    va_start(args, format);
    tmp = str_printf_args(format, args);
    va_end(args);
    if (!tmp) return;
    slog_str(tmp);
    free(tmp);
}

static WCHAR* last_error_as_wstr(void)
{
    WCHAR *msgBuf = NULL;
    WCHAR *copy;
    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &msgBuf, 0, NULL);
    if (!msgBuf) return NULL;
    copy = wstr_dup(msgBuf);
    LocalFree(msgBuf);
    return copy;
}

void slog_last_error(const char *optional_prefix)
{
    WCHAR *txt = last_error_as_wstr();
    if (!txt) return;
    slog_str(optional_prefix);
    slog_wstr_nl(txt);
    free(txt);
}

/* TODO: converting by casting isn't always correct but here we don't care much */
char *wstr_to_str(const WCHAR *txt)
{
    char *txt_copy, *tmp;

    if (!txt) return NULL;

    txt_copy = (char*)malloc(tstr_len(txt) + 1);
    if (!txt_copy) return NULL;

    tmp = txt_copy;
    while (*txt) {
        *tmp++ = (char)*txt++;
    }
    *tmp = 0;
    return txt_copy;
}

void slog_wstr(const WCHAR *txt)
{
    char *txt_copy;

    txt_copy = wstr_to_str(txt);
    if (!txt_copy) return;
    slog_str(txt_copy);
    free(txt_copy);
}

/* log 'txt' to all currently enabled loggers and add newline */
void slog_str_nl(const char *txt)
{
    /* TODO: given the 'reopen the file each time' implementation of
       slgotxt, this should be optimized */
    slog_str(txt);
    slog_str("\n");
}

void slog_wstr_nl(const WCHAR *txt)
{
    slog_wstr(txt);
    slog_wstr(_T("\n"));
}
