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

#ifndef lldb_Materializer_h
#define lldb_Materializer_h

#include "lldb/lldb-private-types.h"
#include "lldb/Core/Error.h"
#include "lldb/Expression/IRMemoryMap.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/StackFrame.h"

#include <vector>

namespace lldb_private
{
    
class Materializer
{
public:
    Materializer ();
    ~Materializer ();
    
    class Dematerializer
    {
    public:
        Dematerializer () :
            m_materializer(NULL),
            m_map(NULL),
            m_process_address(LLDB_INVALID_ADDRESS)
        {
        }
        
        ~Dematerializer ()
        {
            Wipe ();
        }
        
        void Dematerialize (Error &err,
                            lldb::ClangExpressionVariableSP &result_sp,
                            lldb::addr_t frame_top,
                            lldb::addr_t frame_bottom);
        
        void Wipe ();
        
        bool IsValid ()
        {
            return m_materializer && m_map && (m_process_address != LLDB_INVALID_ADDRESS);
        }
    private:
        friend class Materializer;

        Dematerializer (Materializer &materializer,
                        lldb::StackFrameSP &frame_sp,
                        IRMemoryMap &map,
                        lldb::addr_t process_address) :
            m_materializer(&materializer),
            m_map(&map),
            m_process_address(process_address)
        {
            if (frame_sp)
            {
                m_thread_wp = frame_sp->GetThread();
                m_stack_id = frame_sp->GetStackID();
            }
        }
        
        Materializer       *m_materializer;
        lldb::ThreadWP      m_thread_wp;
        StackID             m_stack_id;
        IRMemoryMap        *m_map;
        lldb::addr_t        m_process_address;
    };
    
    typedef std::shared_ptr<Dematerializer> DematerializerSP;
    typedef std::weak_ptr<Dematerializer> DematerializerWP;
    
    DematerializerSP Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err);
    
    uint32_t AddPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp, Error &err);
    uint32_t AddVariable (lldb::VariableSP &variable_sp, Error &err);
    uint32_t AddResultVariable (const TypeFromUser &type, bool is_lvalue, bool keep_in_memory, Error &err);
    uint32_t AddSymbol (const Symbol &symbol_sp, Error &err);
    uint32_t AddRegister (const RegisterInfo &register_info, Error &err);
    
    uint32_t GetStructAlignment ()
    {
        return m_struct_alignment;
    }
    
    uint32_t GetStructByteSize ()
    {
        return m_current_offset;
    }
    
    uint32_t GetResultOffset ()
    {
        if (m_result_entity)
            return m_result_entity->GetOffset();
        else
            return UINT32_MAX;
    }
    
    class Entity
    {
    public:
        Entity () :
            m_alignment(1),
            m_size(0),
            m_offset(0)
        {
        }
        
        virtual ~Entity ()
        {
        }
        
        virtual void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) = 0;
        virtual void Dematerialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address,
                                    lldb::addr_t frame_top, lldb::addr_t frame_bottom, Error &err) = 0;
        virtual void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) = 0;
        virtual void Wipe (IRMemoryMap &map, lldb::addr_t process_address) = 0;
        
        uint32_t GetAlignment ()
        {
            return m_alignment;
        }
        
        uint32_t GetSize ()
        {
            return m_size;
        }
        
        uint32_t GetOffset ()
        {
            return m_offset;
        }
        
        void SetOffset (uint32_t offset)
        {
            m_offset = offset;
        }
    protected:
        void SetSizeAndAlignmentFromType (ClangASTType &type);
        
        uint32_t    m_alignment;
        uint32_t    m_size;
        uint32_t    m_offset;
    };

private:
    uint32_t AddStructMember (Entity &entity);
    
    typedef std::unique_ptr<Entity>  EntityUP;
    typedef std::vector<EntityUP>   EntityVector;
    
    DematerializerWP                m_dematerializer_wp;
    EntityVector                    m_entities;
    Entity                         *m_result_entity;
    uint32_t                        m_current_offset;
    uint32_t                        m_struct_alignment;
};
    
}

#endif
