/* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */
/*
 * SSTT - Simplified Spatial Target Tracker
 *
 * (c) Copyrights 2007-2018 Hartmut Seichter
 */
#include "pw/core/debug.hpp"

#include <cstdarg>
#include <cstdio>

#include <sstream>
#include <thread>


namespace pw {

#if defined(ANDROID)

#include <android/log.h>


struct tpConsoleLog : tpLogCallback
{
    void operator()(const char* cstr)
    {
        __android_log_print(ANDROID_LOG_INFO, "libsstt", "%s", cstr);
    }
};

#else
struct tpConsoleLog
{
    void operator()(const char* cstr) const
    {
        ::fputs(cstr,stdout);
        ::fflush(stdout);
    }
};
#endif


struct tpFileLog {
    FILE* m_file;

    tpFileLog() : m_file(fopen("libsstt.txt","a+"))
    {
    }

    ~tpFileLog()
    {
        if (m_file != nullptr) fclose(m_file);
    }

    void operator()(const char* stuff) {
        if (m_file) ::fputs(stuff,m_file);
    }
};



debug::debug() :
    _level(debug::notification)
{
#if defined(_WINCE)
    _callbacks.add(new tpFileLog());
#else
    _callbacks.push_back(tpConsoleLog());
#endif
    // drop some info
    stream stream(this);

//    stream << "SSTT " << versionString(kVersionFull)  << " " << std::thread::hardware_concurrency() << " threads";

}

debug::~debug()
{
}

void debug::write(const std::string& message)
{
    for (CallbackList::iterator i = _callbacks.begin();
         i != _callbacks.end();
         ++i)
    {
        if (message.length()) (*i)(message.c_str());
    }
}


/* static */
debug::stream debug::s(enum debug::level level)
{
    return stream(&debug::get());
}

debug &debug::get() {
    static debug the_log;
    return the_log;
}


//
// tpLogStream
//

debug::stream::stream(debug *log)
    : _log(log)
{
//    _line.append(std::to_string(Timer::now(Timer::UnitSeconds)) + " ");
}

debug::stream::~stream()
{
    _log->write(_line + "\n");
}

debug::stream::stream(const stream &other)
    : _log(other._log)
    , _line(other._line)
{
}

debug::stream &debug::stream::operator <<(const bool &value)
{
    _line.append(value ? "true" : "false");
    return *this;
}

debug::stream &debug::stream::operator <<(const char* value)
{
    _line.append(value);
    return *this;
}

debug::stream &debug::stream::operator <<(const std::string &value)
{
    _line.append(value);
    return *this;
}

debug::stream &debug::stream::operator <<(const float &value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}

debug::stream &debug::stream::operator <<(const double &value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}

debug::stream &debug::stream::operator <<(const int &value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}

debug::stream &debug::stream::operator <<(const unsigned int &value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}

debug::stream &debug::stream::operator <<(const long &value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}

debug::stream &debug::stream::operator <<(const unsigned long &value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}

debug::stream &debug::stream::operator <<(const void *value)
{
    std::stringstream ss;
    ss << value;
    (*this) << ss.str();

    return *this;
}
}
