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

#include "lldb/Core/ValueObjectChild.h"

#include "lldb/Core/Module.h"
#include "lldb/Core/ValueObjectList.h"

#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/Variable.h"

#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

using namespace lldb_private;

ValueObjectChild::ValueObjectChild
(
    ValueObject &parent,
    const CompilerType &compiler_type,
    const ConstString &name,
    uint64_t byte_size,
    int32_t byte_offset,
    uint32_t bitfield_bit_size,
    uint32_t bitfield_bit_offset,
    bool is_base_class,
    bool is_deref_of_parent,
    AddressType child_ptr_or_ref_addr_type,
    uint64_t language_flags
) :
    ValueObject (parent),
    m_compiler_type (compiler_type),
    m_byte_size (byte_size),
    m_byte_offset (byte_offset),
    m_bitfield_bit_size (bitfield_bit_size),
    m_bitfield_bit_offset (bitfield_bit_offset),
    m_is_base_class (is_base_class),
    m_is_deref_of_parent (is_deref_of_parent),
    m_can_update_with_invalid_exe_ctx()
{
    m_name = name;
    SetAddressTypeOfChildren(child_ptr_or_ref_addr_type);
    SetLanguageFlags(language_flags);
}

ValueObjectChild::~ValueObjectChild()
{
}

lldb::ValueType
ValueObjectChild::GetValueType() const
{
    return m_parent->GetValueType();
}

size_t
ValueObjectChild::CalculateNumChildren(uint32_t max)
{
    auto children_count = GetCompilerType().GetNumChildren (true);
    return children_count <= max ? children_count : max;
}

static void
AdjustForBitfieldness(ConstString& name,
                      uint8_t bitfield_bit_size)
{
    if (name && bitfield_bit_size)
    {
        const char *compiler_type_name = name.AsCString();
        if (compiler_type_name)
        {
            std::vector<char> bitfield_type_name (strlen(compiler_type_name) + 32, 0);
            ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", compiler_type_name, bitfield_bit_size);
            name.SetCString(&bitfield_type_name.front());
        }
    }
}

ConstString
ValueObjectChild::GetTypeName()
{
    if (m_type_name.IsEmpty())
    {
        m_type_name = GetCompilerType().GetConstTypeName ();
        AdjustForBitfieldness(m_type_name, m_bitfield_bit_size);
    }
    return m_type_name;
}

ConstString
ValueObjectChild::GetQualifiedTypeName()
{
    ConstString qualified_name = GetCompilerType().GetConstTypeName();
    AdjustForBitfieldness(qualified_name, m_bitfield_bit_size);
    return qualified_name;
}

ConstString
ValueObjectChild::GetDisplayTypeName()
{
    ConstString display_name = GetCompilerType().GetDisplayTypeName();
    AdjustForBitfieldness(display_name, m_bitfield_bit_size);
    return display_name;
}

LazyBool
ValueObjectChild::CanUpdateWithInvalidExecutionContext ()
{
    if (m_can_update_with_invalid_exe_ctx.hasValue())
        return m_can_update_with_invalid_exe_ctx.getValue();
    if (m_parent)
    {
        ValueObject *opinionated_parent = m_parent->FollowParentChain([] (ValueObject* valobj) -> bool {
            return (valobj->CanUpdateWithInvalidExecutionContext() == eLazyBoolCalculate);
        });
        if (opinionated_parent)
            return (m_can_update_with_invalid_exe_ctx = opinionated_parent->CanUpdateWithInvalidExecutionContext()).getValue();
    }
    return (m_can_update_with_invalid_exe_ctx = this->ValueObject::CanUpdateWithInvalidExecutionContext()).getValue();
}

bool
ValueObjectChild::UpdateValue ()
{
    m_error.Clear();
    SetValueIsValid (false);
    ValueObject* parent = m_parent;
    if (parent)
    {
        if (parent->UpdateValueIfNeeded(false))
        {
            m_value.SetCompilerType(GetCompilerType());
            
            CompilerType parent_type(parent->GetCompilerType());
            // Copy the parent scalar value and the scalar value type
            m_value.GetScalar() = parent->GetValue().GetScalar();
            Value::ValueType value_type = parent->GetValue().GetValueType();
            m_value.SetValueType (value_type);
            
            Flags parent_type_flags(parent_type.GetTypeInfo());
            const bool is_instance_ptr_base = ((m_is_base_class == true) && (parent_type_flags.AnySet(lldb::eTypeInstanceIsPointer)));

            if (parent->GetCompilerType().ShouldTreatScalarValueAsAddress())
            {
                lldb::addr_t addr = parent->GetPointerValue ();
                m_value.GetScalar() = addr;
                
                if (addr == LLDB_INVALID_ADDRESS)
                {
                    m_error.SetErrorString ("parent address is invalid.");
                }
                else if (addr == 0)
                {
                    m_error.SetErrorString ("parent is NULL");
                }
                else
                {
                    m_value.GetScalar() += m_byte_offset;
                    AddressType addr_type = parent->GetAddressTypeOfChildren();
                    
                    switch (addr_type)
                    {
                        case eAddressTypeFile:
                        {
                            lldb::ProcessSP process_sp (GetProcessSP());
                            if (process_sp && process_sp->IsAlive() == true)
                                m_value.SetValueType (Value::eValueTypeLoadAddress);
                            else
                                m_value.SetValueType(Value::eValueTypeFileAddress);
                        }
                            break;
                        case eAddressTypeLoad:
                            m_value.SetValueType (is_instance_ptr_base ? Value::eValueTypeScalar: Value::eValueTypeLoadAddress);
                            break;
                        case eAddressTypeHost:
                            m_value.SetValueType(Value::eValueTypeHostAddress);
                            break;
                        case eAddressTypeInvalid:
                            // TODO: does this make sense?
                            m_value.SetValueType(Value::eValueTypeScalar);
                            break;
                    }
                }
            }
            else
            {
                switch (value_type)
                {
                    case Value::eValueTypeLoadAddress:
                    case Value::eValueTypeFileAddress:
                    case Value::eValueTypeHostAddress:
                    {
                        lldb::addr_t addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
                        if (addr == LLDB_INVALID_ADDRESS)
                        {
                            m_error.SetErrorString ("parent address is invalid.");
                        }
                        else if (addr == 0)
                        {
                            m_error.SetErrorString ("parent is NULL");
                        }
                        else
                        {
                            // Set this object's scalar value to the address of its
                            // value by adding its byte offset to the parent address
                            m_value.GetScalar() += GetByteOffset();
                        }
                    }
                        break;
                        
                    case Value::eValueTypeScalar:
                        // try to extract the child value from the parent's scalar value
                    {
                        Scalar scalar(m_value.GetScalar());
                        if (m_bitfield_bit_size)
                            scalar.ExtractBitfield(m_bitfield_bit_size, m_bitfield_bit_offset);
                        else
                            scalar.ExtractBitfield(8*m_byte_size, 8*m_byte_offset);
                        m_value.GetScalar() = scalar;
                    }
                        break;
                    default:
                        m_error.SetErrorString ("parent has invalid value.");
                        break;
                }
            }
            
            if (m_error.Success())
            {
                const bool thread_and_frame_only_if_stopped = true;
                ExecutionContext exe_ctx (GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped));
                if (GetCompilerType().GetTypeInfo() & lldb::eTypeHasValue)
                {
                    if (!is_instance_ptr_base)
                        m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get());
                    else
                        m_error = m_parent->GetValue().GetValueAsData (&exe_ctx, m_data, 0, GetModule().get());
                }
                else
                {
                    m_error.Clear(); // No value so nothing to read...
                }
            }
            
        }
        else
        {
            m_error.SetErrorStringWithFormat("parent failed to evaluate: %s", parent->GetError().AsCString());
        }
    }
    else
    {
        m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject.");
    }
    
    return m_error.Success();
}


bool
ValueObjectChild::IsInScope ()
{
    ValueObject* root(GetRoot());
    if (root)
        return root->IsInScope ();
    return false;
}
