//
// Copyright (c) 2010-2012 Linaro Limited
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License which accompanies
// this distribution, and is available at
// http://www.opensource.org/licenses/mit-license.php
//
// Contributors:
//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
//     Jesse Barker <jesse.barker@linaro.org>
//
#include <unistd.h>
#include <cstdio>
#include <cstdarg>
#include <string>
#include <sstream>
#include <iostream>
#include "log.h"

#ifdef ANDROID
#include <android/log.h>
#endif

using std::string;

const string Log::continuation_prefix("\x10");
string Log::appname_;
bool Log::do_debug_(false);
std::ostream* Log::extra_out_(0);

static const string terminal_color_normal("\033[0m");
static const string terminal_color_red("\033[1;31m");
static const string terminal_color_cyan("\033[36m");
static const string terminal_color_yellow("\033[33m");
static const string empty;

static void
print_prefixed_message(std::ostream& stream, const string& color, const string& prefix,
                       const string& fmt, va_list ap)
{
    va_list aq;

    /* Estimate message size */
    va_copy(aq, ap);
    int msg_size = vsnprintf(NULL, 0, fmt.c_str(), aq);
    va_end(aq);

    /* Create the buffer to hold the message */
    char *buf = new char[msg_size + 1];

    /* Store the message in the buffer */
    va_copy(aq, ap);
    vsnprintf(buf, msg_size + 1, fmt.c_str(), aq);
    va_end(aq);

    /*
     * Print the message lines prefixed with the supplied prefix.
     * If the target stream is a terminal make the prefix colored.
     */
    string linePrefix;
    if (!prefix.empty())
    {
        static const string colon(": ");
        string start_color;
        string end_color;
        if (!color.empty())
        {
            start_color = color;
            end_color = terminal_color_normal;
        }
        linePrefix = start_color + prefix + end_color + colon;
    }

    std::string line;
    std::stringstream ss(buf);

    while(std::getline(ss, line)) {
        /*
         * If this line is a continuation of a previous log message
         * just print the line plainly.
         */
        if (line[0] == Log::continuation_prefix[0]) {
            stream << line.c_str() + 1;
        }
        else {
            /* Normal line, emit the prefix. */
            stream << linePrefix << line;
        }

        /* Only emit a newline if the original message has it. */
        if (!(ss.rdstate() & std::stringstream::eofbit))
            stream << std::endl;
    }

    delete[] buf;
}


void
Log::info(const char *fmt, ...)
{
    static const string infoprefix("Info");
    const string& prefix(do_debug_ ? infoprefix : empty);
    va_list ap;
    va_start(ap, fmt);

#ifndef ANDROID
    static const string& infocolor(isatty(fileno(stdout)) ? terminal_color_cyan : empty);
    const string& color(do_debug_ ? infocolor : empty);
    print_prefixed_message(std::cout, color, prefix, fmt, ap);
#else
    __android_log_vprint(ANDROID_LOG_INFO, appname_.c_str(), fmt, ap);
#endif

    if (extra_out_)
        print_prefixed_message(*extra_out_, empty, prefix, fmt, ap);

    va_end(ap);
}

void
Log::debug(const char *fmt, ...)
{
    static const string dbgprefix("Debug");
    if (!do_debug_)
        return;
    va_list ap;
    va_start(ap, fmt);

#ifndef ANDROID
    static const string& dbgcolor(isatty(fileno(stdout)) ? terminal_color_yellow : empty);
    print_prefixed_message(std::cout, dbgcolor, dbgprefix, fmt, ap);
#else
    __android_log_vprint(ANDROID_LOG_DEBUG, appname_.c_str(), fmt, ap);
#endif

    if (extra_out_)
        print_prefixed_message(*extra_out_, empty, dbgprefix, fmt, ap);

    va_end(ap);
}

void
Log::error(const char *fmt, ...)
{
    static const string errprefix("Error");
    va_list ap;
    va_start(ap, fmt);

#ifndef ANDROID
    static const string& errcolor(isatty(fileno(stderr)) ? terminal_color_red : empty);
    print_prefixed_message(std::cerr, errcolor, errprefix, fmt, ap);
#else
    __android_log_vprint(ANDROID_LOG_ERROR, appname_.c_str(), fmt, ap);
#endif

    if (extra_out_)
        print_prefixed_message(*extra_out_, empty, errprefix, fmt, ap);

    va_end(ap);
}

void
Log::flush()
{
#ifndef ANDROID
    std::cout.flush();
    std::cerr.flush();
#endif
    if (extra_out_)
        extra_out_->flush();
}
