//===-- RegisterValue.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_RegisterValue_h
#define lldb_RegisterValue_h

// C Includes
#include <string.h>

// C++ Includes
// Other libraries and framework includes
#include "llvm/ADT/APInt.h"

// Project includes
#include "lldb/lldb-public.h"
#include "lldb/lldb-private.h"
#include "lldb/Host/Endian.h"
#include "lldb/Core/Scalar.h"

namespace lldb_private {

    class RegisterValue
    {
    public:
        enum
        {
            kMaxRegisterByteSize = 32u
        };

        enum Type
        {
            eTypeInvalid,
            eTypeUInt8,
            eTypeUInt16,
            eTypeUInt32,
            eTypeUInt64,
            eTypeUInt128,
            eTypeFloat,
            eTypeDouble,
            eTypeLongDouble,
            eTypeBytes
        };
        
        RegisterValue () : 
            m_type (eTypeInvalid),
            m_scalar ((unsigned long)0)
        {
        }

        explicit 
        RegisterValue (uint8_t inst) : 
            m_type (eTypeUInt8)
        {
            m_scalar = inst;
        }

        explicit 
        RegisterValue (uint16_t inst) : 
            m_type (eTypeUInt16)
        {
            m_scalar = inst;
        }

        explicit 
        RegisterValue (uint32_t inst) : 
            m_type (eTypeUInt32)
        {
            m_scalar = inst;
        }

        explicit 
        RegisterValue (uint64_t inst) : 
            m_type (eTypeUInt64)
        {
            m_scalar = inst;
        }

        explicit 
        RegisterValue (llvm::APInt inst) :
            m_type (eTypeUInt128)
        {
            m_scalar = llvm::APInt(inst);
        }

        explicit 
        RegisterValue (float value) : 
            m_type (eTypeFloat)
        {
            m_scalar = value;
        }

        explicit 
        RegisterValue (double value) : 
            m_type (eTypeDouble)
        {
            m_scalar = value;
        }

        explicit 
        RegisterValue (long double value) : 
            m_type (eTypeLongDouble)
        {
            m_scalar = value;
        }

        explicit 
        RegisterValue (uint8_t *bytes, size_t length, lldb::ByteOrder byte_order)
        {
            SetBytes (bytes, length, byte_order);
        }

        RegisterValue::Type
        GetType () const
        {
            return m_type;
        }

        bool
        CopyValue (const RegisterValue &rhs);

        void
        SetType (RegisterValue::Type type)
        {
            m_type = type;
        }

        RegisterValue::Type
        SetType (const RegisterInfo *reg_info);
        
        bool
        GetData (DataExtractor &data) const;
        
        // Copy the register value from this object into a buffer in "dst"
        // and obey the "dst_byte_order" when copying the data. Also watch out
        // in case "dst_len" is longer or shorter than the register value 
        // described by "reg_info" and only copy the least significant bytes 
        // of the register value, or pad the destination with zeroes if the
        // register byte size is shorter that "dst_len" (all while correctly
        // abiding the "dst_byte_order"). Returns the number of bytes copied
        // into "dst".
        uint32_t
        GetAsMemoryData (const RegisterInfo *reg_info,
                         void *dst, 
                         uint32_t dst_len, 
                         lldb::ByteOrder dst_byte_order,
                         Error &error) const;

        uint32_t
        SetFromMemoryData (const RegisterInfo *reg_info,
                           const void *src,
                           uint32_t src_len,
                           lldb::ByteOrder src_byte_order,
                           Error &error);
                           
        bool
        GetScalarValue (Scalar &scalar) const;

        uint8_t
        GetAsUInt8(uint8_t fail_value = UINT8_MAX, bool *success_ptr = nullptr) const
        {
            if (m_type == eTypeUInt8)
            {
                if (success_ptr)
                    *success_ptr = true;                
                return m_scalar.UChar(fail_value);
            }
            if (success_ptr)
                *success_ptr = true;
            return fail_value;
        }

        uint16_t
        GetAsUInt16(uint16_t fail_value = UINT16_MAX, bool *success_ptr = nullptr) const;

        uint32_t
        GetAsUInt32(uint32_t fail_value = UINT32_MAX, bool *success_ptr = nullptr) const;

        uint64_t
        GetAsUInt64(uint64_t fail_value = UINT64_MAX, bool *success_ptr = nullptr) const;

        llvm::APInt
        GetAsUInt128(const llvm::APInt& fail_value, bool *success_ptr = nullptr) const;

        float
        GetAsFloat(float fail_value = 0.0f, bool *success_ptr = nullptr) const;

        double
        GetAsDouble(double fail_value = 0.0, bool *success_ptr = nullptr) const;

        long double
        GetAsLongDouble(long double fail_value = 0.0, bool *success_ptr = nullptr) const;

        void
        SetValueToInvalid ()
        {
            m_type = eTypeInvalid;
        }

        bool
        ClearBit (uint32_t bit);

        bool
        SetBit (uint32_t bit);

        bool
        operator == (const RegisterValue &rhs) const;

        bool
        operator != (const RegisterValue &rhs) const;

        void
        operator = (uint8_t uint)
        {
            m_type = eTypeUInt8;
            m_scalar = uint;
        }

        void
        operator = (uint16_t uint)
        {
            m_type = eTypeUInt16;
            m_scalar = uint;
        }

        void
        operator = (uint32_t uint)
        {
            m_type = eTypeUInt32;
            m_scalar = uint;
        }

        void
        operator = (uint64_t uint)
        {
            m_type = eTypeUInt64;
            m_scalar = uint;
        }

        void
        operator = (llvm::APInt uint)
        {
            m_type = eTypeUInt128;
            m_scalar = llvm::APInt(uint);
        }

        void
        operator = (float f)
        {
            m_type = eTypeFloat;
            m_scalar = f;
        }

        void
        operator = (double f)
        {
            m_type = eTypeDouble;
            m_scalar = f;
        }

        void
        operator = (long double f)
        {
            m_type = eTypeLongDouble;
            m_scalar = f;
        }

        void
        SetUInt8 (uint8_t uint)
        {
            m_type = eTypeUInt8;
            m_scalar = uint;
        }

        void
        SetUInt16 (uint16_t uint)
        {
            m_type = eTypeUInt16;
            m_scalar = uint;
        }

        void
        SetUInt32 (uint32_t uint, Type t = eTypeUInt32)
        {
            m_type = t;
            m_scalar = uint;
        }

        void
        SetUInt64 (uint64_t uint, Type t = eTypeUInt64)
        {
            m_type = t;
            m_scalar = uint;
        }

        void
        SetUInt128 (llvm::APInt uint)
        {
            m_type = eTypeUInt128;
            m_scalar = uint;
        }

        bool
        SetUInt (uint64_t uint, uint32_t byte_size);
    
        void
        SetFloat (float f)
        {
            m_type = eTypeFloat;
            m_scalar = f;
        }

        void
        SetDouble (double f)
        {
            m_type = eTypeDouble;
            m_scalar = f;
        }

        void
        SetLongDouble (long double f)
        {
            m_type = eTypeLongDouble;
            m_scalar = f;
        }

        void
        SetBytes (const void *bytes, size_t length, lldb::ByteOrder byte_order);

        bool
        SignExtend (uint32_t sign_bitpos);

        Error
        SetValueFromCString (const RegisterInfo *reg_info, 
                             const char *value_str);

        Error
        SetValueFromData (const RegisterInfo *reg_info, 
                          DataExtractor &data, 
                          lldb::offset_t offset,
                          bool partial_data_ok);

        // The default value of 0 for reg_name_right_align_at means no alignment at all.
        bool
        Dump (Stream *s, 
              const RegisterInfo *reg_info, 
              bool prefix_with_name,
              bool prefix_with_alt_name,
              lldb::Format format,
              uint32_t reg_name_right_align_at = 0) const;

        const void *
        GetBytes () const;

        lldb::ByteOrder
        GetByteOrder () const
        {
            if (m_type == eTypeBytes)
                return buffer.byte_order;
            return endian::InlHostByteOrder();
        }
        
        uint32_t
        GetByteSize () const;

        static uint32_t
        GetMaxByteSize ()
        {
            return kMaxRegisterByteSize;
        }

        void
        Clear();

    protected:
        RegisterValue::Type m_type;
        Scalar m_scalar;

        struct
        {
            uint8_t bytes[kMaxRegisterByteSize]; // This must be big enough to hold any register for any supported target.
            uint8_t length;
            lldb::ByteOrder byte_order;
        } buffer;
    };

} // namespace lldb_private

#endif // lldb_RegisterValue_h
