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

#include "SymbolFileSymtab.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/Timer.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symtab.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Function.h"

using namespace lldb;
using namespace lldb_private;

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

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


const char *
SymbolFileSymtab::GetPluginNameStatic()
{
    return "symbol-file.symtab";
}

const char *
SymbolFileSymtab::GetPluginDescriptionStatic()
{
    return "Reads debug symbols from an object file's symbol table.";
}


SymbolFile*
SymbolFileSymtab::CreateInstance (ObjectFile* obj_file)
{
    return new SymbolFileSymtab(obj_file);
}

SymbolFileSymtab::SymbolFileSymtab(ObjectFile* obj_file) :
    SymbolFile(obj_file),
    m_source_indexes(),
    m_func_indexes(),
    m_code_indexes(),
    m_data_indexes(),
    m_addr_indexes()
{
}

SymbolFileSymtab::~SymbolFileSymtab()
{
}


uint32_t
SymbolFileSymtab::GetAbilities ()
{
    uint32_t abilities = 0;
    const Symtab *symtab = m_obj_file->GetSymtab();
    if (symtab)
    {

        //----------------------------------------------------------------------
        // The snippet of code below will get the indexes the module symbol
        // table entries that are code, data, or function related (debug info),
        // sort them by value (address) and dump the sorted symbols.
        //----------------------------------------------------------------------
        symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile, m_source_indexes);
        if (!m_source_indexes.empty())
        {
            abilities |= CompileUnits;
        }
        symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, m_func_indexes);
        if (!m_func_indexes.empty())
        {
            symtab->SortSymbolIndexesByValue(m_func_indexes, true);
            abilities |= Functions;
        }

        symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugNo, Symtab::eVisibilityAny, m_code_indexes);
        if (!m_code_indexes.empty())
        {
            symtab->SortSymbolIndexesByValue(m_code_indexes, true);
            abilities |= Labels;
        }

        symtab->AppendSymbolIndexesWithType(eSymbolTypeData, m_data_indexes);

        if (!m_data_indexes.empty())
        {
            symtab->SortSymbolIndexesByValue(m_data_indexes, true);
            abilities |= GlobalVariables;
        }
    }

    return abilities;
}

uint32_t
SymbolFileSymtab::GetNumCompileUnits()
{
    // If we don't have any source file symbols we will just have one compile unit for
    // the entire object file
    if (m_source_indexes.empty())
        return 1;

    // If we have any source file symbols we will logically orgnize the object symbols
    // using these.
    return m_source_indexes.size();
}

CompUnitSP
SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx)
{
    CompUnitSP cu_sp;

    // If we don't have any source file symbols we will just have one compile unit for
    // the entire object file
    if (m_source_indexes.empty())
    {
        const FileSpec &obj_file_spec = m_obj_file->GetFileSpec();
        if (obj_file_spec)
            cu_sp.reset(new CompileUnit(m_obj_file->GetModule(), NULL, obj_file_spec, 0, eLanguageTypeUnknown));

    }
    else if (idx < m_source_indexes.size())
    {
        const Symbol *cu_symbol = m_obj_file->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]);
        if (cu_symbol)
            cu_sp.reset(new CompileUnit(m_obj_file->GetModule(), NULL, cu_symbol->GetMangled().GetName().AsCString(), 0, eLanguageTypeUnknown));
    }
    return cu_sp;
}

size_t
SymbolFileSymtab::ParseCompileUnitFunctions (const SymbolContext &sc)
{
    size_t num_added = 0;
    // We must at least have a valid compile unit
    assert (sc.comp_unit != NULL);
    const Symtab *symtab = m_obj_file->GetSymtab();
    const Symbol *curr_symbol = NULL;
    const Symbol *next_symbol = NULL;
//  const char *prefix = m_obj_file->SymbolPrefix();
//  if (prefix == NULL)
//      prefix == "";
//
//  const uint32_t prefix_len = strlen(prefix);

    // If we don't have any source file symbols we will just have one compile unit for
    // the entire object file
    if (m_source_indexes.empty())
    {
        // The only time we will have a user ID of zero is when we don't have
        // and source file symbols and we declare one compile unit for the
        // entire object file
        if (!m_func_indexes.empty())
        {

        }

        if (!m_code_indexes.empty())
        {
//          StreamFile s(stdout);
//          symtab->Dump(&s, m_code_indexes);

            uint32_t idx = 0;   // Index into the indexes
            const uint32_t num_indexes = m_code_indexes.size();
            for (idx = 0; idx < num_indexes; ++idx)
            {
                uint32_t symbol_idx = m_code_indexes[idx];
                curr_symbol = symtab->SymbolAtIndex(symbol_idx);
                if (curr_symbol)
                {
                    // Union of all ranges in the function DIE (if the function is discontiguous)
                    AddressRange func_range(curr_symbol->GetValue(), 0);
                    if (func_range.GetBaseAddress().IsSectionOffset())
                    {
                        uint32_t symbol_size = curr_symbol->GetByteSize();
                        if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling())
                            func_range.SetByteSize(symbol_size);
                        else if (idx + 1 < num_indexes)
                        {
                            next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]);
                            if (next_symbol)
                            {
                                func_range.SetByteSize(next_symbol->GetValue().GetOffset() - curr_symbol->GetValue().GetOffset());
                            }
                        }

                        FunctionSP func_sp(new Function(sc.comp_unit,
                                                            symbol_idx,                 // UserID is the DIE offset
                                                            LLDB_INVALID_UID,           // We don't have any type info for this function
                                                            curr_symbol->GetMangled(),  // Linker/mangled name
                                                            NULL,                       // no return type for a code symbol...
                                                            func_range));               // first address range

                        if (func_sp.get() != NULL)
                        {
                            sc.comp_unit->AddFunction(func_sp);
                            ++num_added;
                        }
                    }
                }
            }

        }
    }
    else
    {
        // We assume we
    }
    return num_added;
}

bool
SymbolFileSymtab::ParseCompileUnitLineTable (const SymbolContext &sc)
{
    return false;
}

bool
SymbolFileSymtab::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files)
{
    return false;
}

size_t
SymbolFileSymtab::ParseFunctionBlocks (const SymbolContext &sc)
{
    return 0;
}


size_t
SymbolFileSymtab::ParseTypes (const SymbolContext &sc)
{
    return 0;
}


size_t
SymbolFileSymtab::ParseVariablesForContext (const SymbolContext& sc)
{
    return 0;
}

Type*
SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid)
{
    return NULL;
}

lldb::clang_type_t
SymbolFileSymtab::ResolveClangOpaqueTypeDefinition (lldb::clang_type_t clang_Type)
{
    return NULL;
}

ClangNamespaceDecl 
SymbolFileSymtab::FindNamespace (const SymbolContext& sc, const ConstString &name)
{
    return ClangNamespaceDecl();
}

uint32_t
SymbolFileSymtab::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc)
{
    if (m_obj_file->GetSymtab() == NULL)
        return 0;

    uint32_t resolved_flags = 0;
    if (resolve_scope & eSymbolContextSymbol)
    {
        sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(so_addr.GetFileAddress());
        if (sc.symbol)
            resolved_flags |= eSymbolContextSymbol;
    }
    return resolved_flags;
}

uint32_t
SymbolFileSymtab::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list)
{
    return 0;
}

uint32_t
SymbolFileSymtab::FindGlobalVariables(const ConstString &name, bool append, uint32_t max_matches, VariableList& variables)
{
    return 0;
}

uint32_t
SymbolFileSymtab::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables)
{
    return 0;
}

uint32_t
SymbolFileSymtab::FindFunctions(const ConstString &name, uint32_t name_type_mask, bool append, SymbolContextList& sc_list)
{
    Timer scoped_timer (__PRETTY_FUNCTION__,
                        "SymbolFileSymtab::FindFunctions (name = '%s')",
                        name.GetCString());

    Symtab *symtab = m_obj_file->GetSymtab();
    if (symtab)
    {
        const uint32_t start_size = sc_list.GetSize();
        std::vector<uint32_t> symbol_indexes;
        symtab->FindAllSymbolsWithNameAndType (name, eSymbolTypeCode, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes);
        const uint32_t num_matches = symbol_indexes.size();
        if (num_matches)
        {
            SymbolContext sc(m_obj_file->GetModule());
            for (uint32_t i=0; i<num_matches; i++)
            {
                sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
                sc_list.Append(sc);
            }
        }
        return sc_list.GetSize() - start_size;
    }
    return 0;
}

uint32_t
SymbolFileSymtab::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list)
{
    Timer scoped_timer (__PRETTY_FUNCTION__,
                        "SymbolFileSymtab::FindFunctions (regex = '%s')",
                        regex.GetText());

    return 0;
}

uint32_t
SymbolFileSymtab::FindTypes (const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::TypeList& types)
{
    if (!append)
        types.Clear();

    return 0;
}
//
//uint32_t
//SymbolFileSymtab::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, TypeList& types)
//{
//  return 0;
//}


//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
const char *
SymbolFileSymtab::GetPluginName()
{
    return "SymbolFileSymtab";
}

const char *
SymbolFileSymtab::GetShortPluginName()
{
    return GetPluginNameStatic();
}

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

void
SymbolFileSymtab::GetPluginCommandHelp (const char *command, Stream *strm)
{
}

Error
SymbolFileSymtab::ExecutePluginCommand (Args &command, Stream *strm)
{
    Error error;
    error.SetErrorString("No plug-in command are currently supported.");
    return error;
}

Log *
SymbolFileSymtab::EnablePluginLogging (Stream *strm, Args &command)
{
    return NULL;
}

