/******************************************************************************
 *
 * Project:  OpenCPN
 *
 ***************************************************************************
 *   Copyright (C) 2013 by David S. Register                               *
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
 ***************************************************************************
 */

#include <algorithm>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>

#include <wx/datetime.h>
#include <wx/filename.h>

#include "logger.h"

const wxLogLevel OcpnLog::LOG_BADLEVEL = wxLOG_Max + 1;

const static std::map<wxLogLevel, const char*> name_by_level = {
    { wxLOG_FatalError, "FATALERR"},
    { wxLOG_Error,      "ERROR"},
    { wxLOG_Warning,    "WARNING"},
    { wxLOG_Message,    "MESSAGE"},
    { wxLOG_Status,     "STATUS"},
    { wxLOG_Info,       "INFO"},
    { wxLOG_Debug,      "DEBUG"},
    { wxLOG_Trace,      "TRACE"},
    { wxLOG_Progress,   "PROGRESS"}
};


static std::map<std::string, wxLogLevel> level_by_name;


static std::string basename(const std::string path)
{
    size_t pos = path.rfind(wxFileName::GetPathSeparator(), path.length());
    return pos == std::string::npos ? path : path.substr(pos + 1);
}


static void init_level_by_name()
{
    for (auto it = name_by_level.begin(); it != name_by_level.end(); it++) {
        level_by_name[std::string(it->second)] = it->first;
    }
}

 
static std::string timeStamp()
{
    wxDateTime now = wxDateTime::UNow();
    std::stringstream stamp;
    stamp << std::setfill('0')
          << std::setw(2) << now.GetHour() << ":"
          << std::setw(2) << now.GetMinute() << ":"
          << std::setw(2) << now.GetSecond() << "."
          << std::setw(3) << now.GetMillisecond();
    return stamp.str();
}


std::string OcpnLog::level2str(wxLogLevel level)
{
    auto search = name_by_level.find(level);
    return search == name_by_level.end() ? "Unknown level" : search->second;
}


wxLogLevel OcpnLog::str2level(const char* string)
{
    if (level_by_name.size() == 0) {
        init_level_by_name();
    }
    std::string key(string);
    std::transform(key.begin(), key.end(), key.begin(), ::toupper);
    auto search = level_by_name.find(key);
    return search == level_by_name.end() ? LOG_BADLEVEL : search->second;
}


OcpnLog::OcpnLog(const char* path)
{
    log.open(path, std::fstream::out | std::fstream::app);
}


OcpnLog::~OcpnLog()
{
    log.close();
}

void OcpnLog::Flush()
{
    wxLog::Flush();
    log.flush();
}

void OcpnLog::DoLogRecord(wxLogLevel level,
		          const wxString& msg,
		          const wxLogRecordInfo& info)
{
    std::ostringstream oss;
    oss << timeStamp() << " "
        << std::setw(7) << level2str(level) << " "
        << basename(info.filename) << ":" << info.line << " "
        << msg << std::endl;
    log << oss.str();
}


Logger::Logger()
    :info("", 0, "", ""), level(wxLOG_Info) {};

Logger::~Logger()
{
    wxString msg(os.str());
    wxLog* log = wxLog::GetActiveTarget();
    auto ocpnLog = dynamic_cast<OcpnLog*>(log);
    if (ocpnLog)  {
        ocpnLog->LogRecord(level, msg, info);
    }
}


std::ostream& Logger::get(wxLogLevel l, const char* path, int line)
{
    info.filename = path;
    info.line = line;
    level = l;
    return os;
}


void Logger::logRecord(wxLogLevel level,
                       const char* msg,
                       const wxLogRecordInfo info)
{
    wxLog* log = wxLog::GetActiveTarget();
    auto ocpnLog = dynamic_cast<OcpnLog*>(log);
    if (ocpnLog)  {
        ocpnLog->LogRecord(level, wxString(msg), info);
    }
}


void Logger::logMessage(wxLogLevel level, const char* path, int line,
                        const char* fmt, ...)
{
    wxLogRecordInfo info(__FILE__, __LINE__, "", "");
    char buf[1024];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    auto log = dynamic_cast<OcpnLog*>(wxLog::GetActiveTarget());
    if (log) {
        log->LogRecord(level, buf, info);
    }
}
