/*
 * ************************************************************************************************************
 * Author: Marcin Załęczny
 * Copyright (C)2010 Marcin Załęczny <mzaleczny@hotmail.com>
 * License: The GNU Lesser General Public License, version 3.0 (LGPLv3)
 * Library Name: mzstd
 * Version: 1.07
 * First release date:   2010-11-08
 * Current release date: 2011-03-04
 *
 *
 * This file is part of mzstd library
 * 
 * mzstd library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * mzstd library 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * ************************************************************************************************************
 */

#include "mzstdCommon.h"
#include "mzFileUtils.h"
#include "mzLogger.h"
#include "mzMemoryView.h"

CLogger* logger = NULL;

CLogger::CLogger(CString filename) {
    this->setRealClassName(L"CLogger");
    this->_fh = INVALID_HANDLE_VALUE;
    this->_filename = filename;
    this->_writeOperations = 0;
    if (FileExists(this->_filename)) {
        DeleteFile(this->_filename.toWCHAR());
    }
}

CLogger::~CLogger() {
    this->close();
}

VOID CLogger::close() {
    if (this->_fh != INVALID_HANDLE_VALUE) {
        CloseHandle(this->_fh);
        this->_fh = INVALID_HANDLE_VALUE;
    }
}

VOID CLogger::reopen() {
    this->close();
    CString filename = this->_filename;
    
    this->_fh = CreateFile(filename.getBuffer(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (this->_fh != INVALID_HANDLE_VALUE) {
        this->_filename = filename;
        this->_writeOperations = 0;
	}
}

VOID CLogger::log(WCHAR* format, va_list paramList) {
    if (this->_fh == INVALID_HANDLE_VALUE) {
        this->reopen();
    }    
    if (this->_fh != INVALID_HANDLE_VALUE) {
        CString fmt(format);
        fmt.prepend(L"%dtm ");
        CString s = FormatMessage(fmt.getBuffer(), paramList) + L"\n";
        AppendToFile(this->_fh, s.getBuffer(), ENC_UTF8);
        this->_writeOperations += 1;
        this->flush();
    }
}

VOID CLogger::log(WCHAR* format, ...) {
    if (this->_fh == INVALID_HANDLE_VALUE) {
        this->reopen();
    }
    if (this->_fh != INVALID_HANDLE_VALUE) {
        va_list paramList;
        va_start(paramList, format);
        this->log(format, paramList);
        va_end(paramList);
    }
}

VOID CLogger::log(WCHAR* message, BOOL appendEOL) {
    if (this->_fh == INVALID_HANDLE_VALUE) {
        this->reopen();
    }
    if (this->_fh != INVALID_HANDLE_VALUE) {
        CString fmt(L"%dtm ");
        CString logmessage;
        if (appendEOL) {
            fmt += CString(message) + L"\r\n";
        } else {
            fmt += message;
        }
        
        logmessage = FormatMessage(fmt.getBuffer());
        AppendToFile(this->_fh, logmessage.getBuffer(), ENC_UTF8);
        this->_writeOperations += 1;
        this->flush();
    }
}

VOID CLogger::flush() {
    if (this->_fh != INVALID_HANDLE_VALUE) {
        FlushFileBuffers(this->_fh);
    }
}

BOOL WINAPI InitLogger(CString filename) {
    ::logger = new CLogger(filename);
    if (::logger) {
        return TRUE;
    }
    
    return FALSE;
}

VOID WINAPI CleanupLogger() {
    if (::logger) {
        CString fn = ::logger->getFilename();
        delete ::logger;
        ::logger = NULL;
        // if log file is empty then delete it
        if (FileExists(fn)) {
            if (GetFileSize(fn) == 0) {
                DeleteFile(fn.getBuffer());
            }
        }
    }
}

VOID WINAPI FlushLogger() {
    if (::logger) {
        ::logger->flush();
    }
}

BOOL __cdecl WriteLogMessage(DWORD lineNumber, CHAR* sourceFilename, LONG encoding, WCHAR *message, ...) {
    if (!::logger) {
        InitLogger(DEFAULT_LOGGER_FILENAME);
    }
    
    if (::logger) {
        va_list paramList;
        va_start(paramList, message);
        CString log_message = FormatMessage(L"SOURCE_FILE: %sc, SOURCE_LINE: %d, MESSAGE:", sourceFilename, lineNumber) + FormatMessage(message, paramList) + L"\n";
        ::logger->log(log_message.getBuffer(), FALSE);
        va_end(paramList);
        return TRUE;
    }
    
    return FALSE;
}

DECLSPEC_MZSTD BOOL __cdecl WriteLogMessage(WCHAR *message, ...) {
    if (!::logger) {
        InitLogger(DEFAULT_LOGGER_FILENAME);
    }
    
    if (::logger) {
        va_list paramList;
        va_start(paramList, message);
        ::logger->log(message, paramList);
        va_end(paramList);
        return TRUE;
    }
    
    return FALSE;
}

BOOL WINAPI WriteMemoryLogMessage(DWORD lineNumber, CHAR* sourceFilename, LONG encoding, LPVOID memory, ULONG size) {
    if (!::logger) {
        InitLogger(DEFAULT_LOGGER_FILENAME);
    }
    
    if (::logger) {
        CMemoryView mv(memory, size, (DWORD)memory);
        CString log_message = FormatMessage(L"SOURCE_FILE: %sc, SOURCE_LINE: %d, MESSAGE:\n    Memory address: %p  memory size: %ddw\n    Memory content: %s\n", sourceFilename, lineNumber,
            memory, size, mv.getDump(L"", TRUE, FALSE, FALSE).toWCHAR());
        ::logger->log(log_message.toWCHAR(), FALSE);
        return TRUE;
    }
    
    return FALSE;
}
