//===-- LogChannelDWARF.cpp ------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "LogChannelDWARF.h"

#include "lldb/Interpreter/Args.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "SymbolFileDWARF.h"

using namespace lldb;
using namespace lldb_private;


// when the one and only logging channel is enabled, then this will be non NULL.
static LogChannelDWARF* g_log_channel = NULL;

LogChannelDWARF::LogChannelDWARF () :
    LogChannel ()
{
}

LogChannelDWARF::~LogChannelDWARF ()
{
}


void
LogChannelDWARF::Initialize()
{
    PluginManager::RegisterPlugin (GetPluginNameStatic(),
                                   GetPluginDescriptionStatic(),
                                   LogChannelDWARF::CreateInstance);
}

void
LogChannelDWARF::Terminate()
{
    PluginManager::UnregisterPlugin (LogChannelDWARF::CreateInstance);
}

LogChannel*
LogChannelDWARF::CreateInstance ()
{
    return new LogChannelDWARF ();
}

lldb_private::ConstString
LogChannelDWARF::GetPluginNameStatic()
{
    return SymbolFileDWARF::GetPluginNameStatic();
}

const char *
LogChannelDWARF::GetPluginDescriptionStatic()
{
    return "DWARF log channel for debugging plug-in issues.";
}

lldb_private::ConstString
LogChannelDWARF::GetPluginName()
{
    return GetPluginNameStatic();
}

uint32_t
LogChannelDWARF::GetPluginVersion()
{
    return 1;
}


void
LogChannelDWARF::Delete ()
{
    g_log_channel = NULL;
}


void
LogChannelDWARF::Disable (const char **categories, Stream *feedback_strm)
{
    if (m_log_ap.get() == NULL)
        return;

    uint32_t flag_bits = m_log_ap->GetMask().Get();
    for (size_t i = 0; categories[i] != NULL; ++i)
    {
         const char *arg = categories[i];

        if      (::strcasecmp (arg, "all")        == 0) flag_bits &= ~DWARF_LOG_ALL;
        else if (::strcasecmp (arg, "info")       == 0) flag_bits &= ~DWARF_LOG_DEBUG_INFO;
        else if (::strcasecmp (arg, "line")       == 0) flag_bits &= ~DWARF_LOG_DEBUG_LINE;
        else if (::strcasecmp (arg, "pubnames")   == 0) flag_bits &= ~DWARF_LOG_DEBUG_PUBNAMES;
        else if (::strcasecmp (arg, "pubtypes")   == 0) flag_bits &= ~DWARF_LOG_DEBUG_PUBTYPES;
        else if (::strcasecmp (arg, "aranges")    == 0) flag_bits &= ~DWARF_LOG_DEBUG_ARANGES;
        else if (::strcasecmp (arg, "lookups")    == 0) flag_bits &= ~DWARF_LOG_LOOKUPS;
        else if (::strcasecmp (arg, "map")        == 0) flag_bits &= ~DWARF_LOG_DEBUG_MAP;
        else if (::strcasecmp (arg, "default")    == 0) flag_bits &= ~DWARF_LOG_DEFAULT;
        else if (::strcasecmp (arg, "verbose")    == 0) flag_bits &= ~DWARF_LOG_VERBOSE;
        else if (::strncasecmp(arg, "comp", 4)    == 0) flag_bits &= ~DWARF_LOG_TYPE_COMPLETION;
        else
        {
            feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
            ListCategories (feedback_strm);
        }
   }
    
    if (flag_bits == 0)
        Delete ();
    else
        m_log_ap->GetMask().Reset (flag_bits);

    return;
}

bool
LogChannelDWARF::Enable
(
    StreamSP &log_stream_sp,
    uint32_t log_options,
    Stream *feedback_strm,  // Feedback stream for argument errors etc
    const char **categories  // The categories to enable within this logging stream, if empty, enable default set
)
{
    Delete ();

    if (m_log_ap)
        m_log_ap->SetStream(log_stream_sp);
    else
        m_log_ap.reset(new Log (log_stream_sp));
    
    g_log_channel = this;
    uint32_t flag_bits = 0;
    bool got_unknown_category = false;
    for (size_t i = 0; categories[i] != NULL; ++i)
    {
        const char *arg = categories[i];

        if      (::strcasecmp (arg, "all")        == 0) flag_bits |= DWARF_LOG_ALL;
        else if (::strcasecmp (arg, "info")       == 0) flag_bits |= DWARF_LOG_DEBUG_INFO;
        else if (::strcasecmp (arg, "line")       == 0) flag_bits |= DWARF_LOG_DEBUG_LINE;
        else if (::strcasecmp (arg, "pubnames")   == 0) flag_bits |= DWARF_LOG_DEBUG_PUBNAMES;
        else if (::strcasecmp (arg, "pubtypes")   == 0) flag_bits |= DWARF_LOG_DEBUG_PUBTYPES;
        else if (::strcasecmp (arg, "aranges")    == 0) flag_bits |= DWARF_LOG_DEBUG_ARANGES;
        else if (::strcasecmp (arg, "lookups")    == 0) flag_bits |= DWARF_LOG_LOOKUPS;
        else if (::strcasecmp (arg, "map")        == 0) flag_bits |= DWARF_LOG_DEBUG_MAP;
        else if (::strcasecmp (arg, "default")    == 0) flag_bits |= DWARF_LOG_DEFAULT;
        else if (::strcasecmp (arg, "verbose")    == 0) flag_bits |= DWARF_LOG_VERBOSE;
        else if (::strncasecmp(arg, "comp", 4)    == 0) flag_bits |= DWARF_LOG_TYPE_COMPLETION;
        else
        {
            feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
            if (got_unknown_category == false)
            {
                got_unknown_category = true;
                ListCategories (feedback_strm);
            }
        }
    }
    if (flag_bits == 0)
        flag_bits = DWARF_LOG_DEFAULT;
    m_log_ap->GetMask().Reset(flag_bits);
    m_log_ap->GetOptions().Reset(log_options);
    return m_log_ap.get() != NULL;
}

void
LogChannelDWARF::ListCategories (Stream *strm)
{
    strm->Printf ("Logging categories for '%s':\n"
                  "  all - turn on all available logging categories\n"
                  "  info - log the parsing of .debug_info\n"
                  "  line - log the parsing of .debug_line\n"
                  "  pubnames - log the parsing of .debug_pubnames\n"
                  "  pubtypes - log the parsing of .debug_pubtypes\n"
                  "  aranges - log the parsing of .debug_aranges\n"
                  "  lookups - log any lookups that happen by name, regex, or address\n"
                  "  completion - log struct/unions/class type completions\n"
                  "  map - log insertions of object files into DWARF debug maps\n",
                  SymbolFileDWARF::GetPluginNameStatic().GetCString());
}

Log *
LogChannelDWARF::GetLog ()
{
    if (g_log_channel)
        return g_log_channel->m_log_ap.get();

    return NULL;
}

Log *
LogChannelDWARF::GetLogIfAll (uint32_t mask)
{
    if (g_log_channel && g_log_channel->m_log_ap.get())
    {
        if (g_log_channel->m_log_ap->GetMask().AllSet(mask))
            return g_log_channel->m_log_ap.get();
    }
    return NULL;
}

Log *
LogChannelDWARF::GetLogIfAny (uint32_t mask)
{
    if (g_log_channel && g_log_channel->m_log_ap.get())
    {
        if (g_log_channel->m_log_ap->GetMask().AnySet(mask))
            return g_log_channel->m_log_ap.get();
    }
    return NULL;
}

void
LogChannelDWARF::LogIf (uint32_t mask, const char *format, ...)
{
    if (g_log_channel)
    {
        Log *log = g_log_channel->m_log_ap.get();
        if (log && log->GetMask().AnySet(mask))
        {
            va_list args;
            va_start (args, format);
            log->VAPrintf (format, args);
            va_end (args);
        }
    }
}
