/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the 
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors: 
 *   Erik Verhoeven - initial API and implementation 
 ********************************************************************************/

#include "entity_base.h"
#include "../parser.h"
#include "../exception.h"
#include "entity_value.h"
#include "declaration_entity.h"
#include "enum_entity.h"
#include "struct_entity.h"
#include "union_entity.h"
#include "typedef_entity.h"
#include "interface_entity.h"
#include "exception_entity.h"
#include <functional>
#include <type_traits>
#include <limits>
#include <functional>

sdv::idl::EDeclType CTypeDeclaration::GetBaseType() const
{
	return m_eBaseType;
}

void CTypeDeclaration::SetBaseType(sdv::idl::EDeclType eBaseType)
{
	m_eBaseType = eBaseType;
}

sdv::u8string CTypeDeclaration::GetTypeString() const
{
	return m_ssOriginalType;
}

void CTypeDeclaration::SetTypeString(const sdv::u8string& rssType)
{
	m_ssOriginalType = rssType;
}

void CTypeDeclaration::AddTypeString(const sdv::u8string& rss)
{
	m_ssOriginalType += rss;
}

sdv::IInterfaceAccess* CTypeDeclaration::GetTypeDefinition() const
{
	return m_ptrOriginalType.get();
}

CEntityPtr CTypeDeclaration::GetTypeDefinitionEntityPtr() const
{
	return m_ptrOriginalType;
}

void CTypeDeclaration::SetTypeDefinitionEntityPtr(const CEntityPtr& rptrDefinition)
{
	m_ptrOriginalType = rptrDefinition;
}

uint32_t CTypeDeclaration::GetFixedLength() const
{
	return m_uiFixedLen;
}

void CTypeDeclaration::SetFixedLength(uint32_t uiFixedLength)
{
	m_uiFixedLen = uiFixedLength;
}

uint32_t CTypeDeclaration::GetDecimals() const
{
	return m_uiDecimals;
}

void CTypeDeclaration::SetDecimals(uint32_t uiDecimals)
{
	m_uiDecimals = uiDecimals;
}

sdv::IInterfaceAccess* CTypeDeclaration::GetValueType() const
{
	return m_ptrValueType.get();
}

void CTypeDeclaration::SetValueTypePtr(const std::shared_ptr<CTypeDeclaration>& rptrValueType)
{
	m_ptrValueType = rptrValueType;
}

sdv::IInterfaceAccess* CTypeDeclaration::GetKeyType() const
{
	return m_ptrKeyType.get();
}

void CTypeDeclaration::SetKeyTypePtr(const std::shared_ptr<CTypeDeclaration>& rptrKeyType)
{
	m_ptrKeyType = rptrKeyType;
}

CEntity::CEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent) :
    m_ptrContext(rptrContext), m_ptrParent(ptrParent)
{
    if (!ptrParent)
        throw CCompileException("Internal error: expected a valid parent entity.");
    m_pParser = ptrParent->m_pParser;
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    m_vecDeclTypes = ptrParent->m_vecDeclTypes;
}

CEntity::CEntity(const CContextPtr& rptrContext, CEntityPtr ptrParent, const CTokenList& rlstTokenList) :
    m_ptrContext(rptrContext), m_ptrParent(ptrParent), m_lstLocalTokenList(rlstTokenList)
{
    if (!ptrParent)
        throw CCompileException("Internal error: expected a valid parent entity.");
    m_pParser = ptrParent->m_pParser;
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    m_vecDeclTypes = ptrParent->m_vecDeclTypes;
}

CEntity::CEntity(CParser& rParser, const CContextPtr& rptrContext) :
    m_ptrContext(rptrContext), m_pParser(&rParser), m_ssName("root"), m_vecDeclTypes(g_vecDeclTypes)
{
    // Add additional types based on enabled extensions
    if (rParser.GetEnvironment().InterfaceTypeExtension())
    {
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"interface_id", sdv::idl::EDeclType::decltype_interface_id});
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"interface_t", sdv::idl::EDeclType::decltype_interface_type});
    }
    if (rParser.GetEnvironment().ExceptionTypeExtension())
    {
        m_vecDeclTypes.push_back(TDeclTypeAssoc{ "exception_id", sdv::idl::EDeclType::decltype_exception_id });
    }
    if (rParser.GetEnvironment().PointerTypeExtension())
    {
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"pointer", sdv::idl::EDeclType::decltype_pointer});
    }
    if (rParser.GetEnvironment().UnicodeExtension())
    {
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"char16", sdv::idl::EDeclType::decltype_char16});
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"char32", sdv::idl::EDeclType::decltype_char32});
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"u8string", sdv::idl::EDeclType::decltype_u8string});
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"u16string", sdv::idl::EDeclType::decltype_u16string});
        m_vecDeclTypes.push_back(TDeclTypeAssoc{"u32string", sdv::idl::EDeclType::decltype_u32string});
    }
}

sdv::interface_t CEntity::GetInterface(sdv::interface_id idInterface)
{
    if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
        return static_cast<sdv::IInterfaceAccess*>(this);
    if (idInterface == sdv::GetInterfaceId<sdv::idl::IEntityInfo>())
        return static_cast<sdv::idl::IEntityInfo*>(this);
    if (idInterface == sdv::GetInterfaceId<sdv::idl::IEntityComments>())
        return static_cast<sdv::idl::IEntityComments*>(this);
    if (idInterface == sdv::GetInterfaceId<sdv::idl::IEntityContext>())
        return static_cast<sdv::idl::IEntityContext*>(this);
    if (idInterface == sdv::GetInterfaceId<sdv::idl::IForwardDeclarationEntity>() && m_bForwardDeclaration)
        return static_cast<sdv::idl::IForwardDeclarationEntity*>(this);
    return nullptr;
}

sdv::idl::EEntityType CEntity::GetType() const
{
    return sdv::idl::EEntityType::type_unknown;
}

sdv::IInterfaceAccess* CEntity::GetEntity()
{
    if (!m_bForwardDeclaration) return nullptr;
    if (!m_ptrParent) return nullptr;
    std::pair<CEntityPtr, bool> prEntity = m_ptrParent->FindLocal(m_ssName, Get<CDeclarationEntity>() && !Get<CTypedefEntity>() ? true : false);
    return static_cast<sdv::IInterfaceAccess*>(prEntity.first.get());
}

std::string CEntity::GetDeclTypeStr(bool /*bResolveTypedef*/) const
{
    throw CCompileException("Internal error: unexpected request for declaration type.");
}

sdv::u8string CEntity::GetName() const
{
    return m_ssName;
}

sdv::u8string CEntity::GetScopedName() const
{
    // Do not return the root entity.
    if (IsRootEntity()) return std::string();

    std::string ssScopeName;
    if (m_ptrParent) ssScopeName = m_ptrParent->GetScopedName();

    // Only add the name of the entity if not anonymous (only part of a declaration).
    const CDeclarationEntity* pDeclaration = Get<CDeclarationEntity>();
    if (!pDeclaration || !pDeclaration->IsAnonymous())
    {
        if (!ssScopeName.empty()) ssScopeName += "::";
        ssScopeName += m_ssName;
    }
    return ssScopeName;
}

std::string CEntity::MakeFullScoped(const std::string& rssExpression) const
{
    // Parse the expression
    CTokenList lstTokens = Tokenize(rssExpression, GetContext());

    // Create a new expression
    std::stringstream sstreamNewExpression;

    // Rescope any identifier if there is any...
    std::string ssIdentifier;
    auto fnWriteIdentifier = [&]()
    {
        if (ssIdentifier.empty())
            return;

        // Search for the entity
        CEntityPtr ptrEntity = Find(ssIdentifier);
        if (ptrEntity)
            sstreamNewExpression << ptrEntity->GetScopedName();
        else // Not found; add the identifier as is.
            sstreamNewExpression << ssIdentifier;
        ssIdentifier.clear();
    };

    // Run through all tokens.
    for (const CToken& rtoken : lstTokens)
    {
        switch (rtoken.GetType())
        {
        case ETokenType::token_identifier:
            ssIdentifier += static_cast<std::string>(rtoken);
            break;
        case ETokenType::token_separator:
            if (rtoken == "::")
            {
                ssIdentifier += static_cast<std::string>(rtoken);
                break;
            }
            fnWriteIdentifier();
            sstreamNewExpression << static_cast<std::string>(rtoken);
            break;
        default:
            fnWriteIdentifier();
            sstreamNewExpression << static_cast<std::string>(rtoken);
            break;
        }
    }

    // Any identifiers left?
    fnWriteIdentifier();

    // Done
    return sstreamNewExpression.str();
}

CContextPtr CEntity::GetContext() const
{
    return m_ptrContext;
}

CContextPtr CEntity::GetContext()
{
    return m_ptrContext;
}

CEntityPtr CEntity::GetParentEntity() const
{
    return m_ptrParent;
}

CEntityPtr CEntity::GetParentEntity()
{
    return m_ptrParent;
}

sdv::IInterfaceAccess* CEntity::GetParent() const
{
    auto ptrInterface = GetParentEntity();
    return ptrInterface.get();
}

CEntityPtr CEntity::GetRootEntity() const
{
    // The root doesn't have a parent.
    // Iterate through the parents until the root parent is returned.
    if (m_ptrParent) return m_ptrParent->GetRootEntity();

    // Get the root -> change the return type.
    // NOTE: shared_from_this returns a smart pointer to a const object. Since a smart pointer to a non-const object cannot be
    // casted to a smart pointer to a const object, we cannot use the shared_from_this function on the current this object.
    // Instead, get the non-const version of this first before calling shared_from_this.
    return const_cast<CEntity*>(this)->shared_from_this();
}

CEntityPtr CEntity::GetRootEntity()
{
    // The root doesn't have a parent.
    // Iterate through the parents until the root parent is returned.
    return !m_ptrParent ? shared_from_this() : m_ptrParent->GetRootEntity();
}

CEntityPtr CEntity::GetResolvedEntity() const
{
    // Is the entity a typedef, get the assigned type before finding the child
    CEntityPtr ptrResolvedEntity = const_cast<CEntity*>(this)->shared_from_this();
    do
    {
        // Check whether the type is a typedef declaration
        const CDeclarationEntity* pDeclaration = ptrResolvedEntity->Get<CDeclarationEntity>();
        if (!pDeclaration) break;

        // Type is a typedef, get the assigned type
        ptrResolvedEntity = pDeclaration->GetTypeEntity();
    } while (ptrResolvedEntity);

    // In case the typedef is not referring to an entity, but to a basic type, return the typedef itself.
    return ptrResolvedEntity ? ptrResolvedEntity : const_cast<CEntity*>(this)->shared_from_this();
}

CEntityPtr CEntity::GetResolvedEntity()
{
    // Is the entity a typedef, get the assigned type before finding the child
    CEntityPtr ptrResolvedEntity = shared_from_this();
    do
    {
        // Check whether the type is a typedef declaration
        if (ptrResolvedEntity->GetType() != sdv::idl::EEntityType::type_typedef) break;
        const CDeclarationEntity* pDeclaration = ptrResolvedEntity->Get<CDeclarationEntity>();
        if (!pDeclaration) break;

        // Type is a typedef, get the assigned type
        ptrResolvedEntity = pDeclaration->GetTypeEntity();
    } while (ptrResolvedEntity);

    // In case the typedef is not referring to an entity, but to a basic type, return the typedef itself.
    return ptrResolvedEntity ? ptrResolvedEntity : shared_from_this();
}

void CEntity::SetCommentTokens(const CTokenList& rlstComments, bool bPreceeding /*= true*/)
{
    std::stringstream sstreamComments;

    // Detect the type of comments, remove the indentation and create a (multi-line) comment string.
    uint32_t uiFlags = 0;
    for (const CToken& rComment : rlstComments)
    {
        uint32_t uiLocalFlags = 0;
        std::string ssComment = static_cast<std::string>(rComment);
        bool bEmpty = sstreamComments.str().empty();

        if (ssComment.substr(0, 2) == "/*") // C-stlye comment
        {
            uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::c_style);
            size_t nPos = 2;
            if (ssComment.substr(0, 4) == "/**<") // C-style Javadoc comment following a statement
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::c_style_javadoc_post);
                nPos = 4;
            }
            else if (ssComment.substr(0, 4) == "/*!<") // C-style QT comment following statement
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::c_style_qt_post);
                nPos = 4;
            }
            else if (ssComment.substr(0, 3) == "/**") // C-style Javadoc comment
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::c_style_javadoc);
                nPos = 3;
            }
            else if (ssComment.substr(0, 3) == "/*!") // C-style QT comment
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::c_style_qt);
                nPos = 3;
            }

            // Check for a proper finalization of the comment
            if (ssComment.length() < 4 || ssComment.substr(ssComment.length() - 2) != "*/")
                continue;   // ERROR - not a valid comment

            bool bInitial = true;
            while (nPos < ssComment.length() - 3)
            {
                // Skip spaces and tabs
                if (ssComment[nPos] == ' ' || ssComment[nPos] == '\t')
                {
                    while (ssComment[nPos] == ' ' || ssComment[nPos] == '\t')
                        nPos++;
                    continue;
                }

                // Treat initial and following lines differently
                if (bInitial)
                {
                    // If the next character is a newline, skip the newline and continue with the next line
                    if (ssComment[nPos] == '\n')
                    {
                        nPos++;
                        continue;
                    }
                    if (ssComment[nPos] == '\r' && ssComment[nPos + 1] == '\n')
                    {
                        nPos += 2;
                        continue;
                    }
                }

                // If available, skip asterisk and one space
                if (ssComment[nPos] == '*')
                {
                    nPos++;
                    if (ssComment[nPos] == ' ') nPos++;
                }

                // If this is the last line, skip the line.
                if (nPos == ssComment.length() - 2) continue;

                // Read the line
                size_t nStart = nPos;
                size_t nStop = nStart;
                while (true)
                {
                    if (ssComment[nPos] == '\n')
                    {
                        if (!sstreamComments.str().empty()) sstreamComments << std::endl;
                        sstreamComments << ssComment.substr(nStart, nStop - nStart + 1);
                        nPos++;
                        break;
                    }
                    if (ssComment[nPos] == '\r' && ssComment[nPos + 1] == '\n')
                    {
                        if (!sstreamComments.str().empty()) sstreamComments << std::endl;
                        sstreamComments << ssComment.substr(nStart, nStop - nStart + 1);
                        nPos += 2;
                        break;
                    }
                    if (nPos >= ssComment.length() - 3)
                    {
                        if (nPos != nStart)
                        {
                            if (!sstreamComments.str().empty()) sstreamComments << std::endl;
                            sstreamComments << ssComment.substr(nStart, nStop - nStart + 1);
                        }
                        break;
                    }

                    // Increase position
                    nPos++;

                    // Do not include any whitespace
                    if (!std::isspace(ssComment[nPos]))
                        nStop = nPos;
                }
                bInitial = false;
            }
        }
        else if (ssComment.substr(0, 2) == "//") // C++-stlye comment
        {
            uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style);
            size_t nPos = 2;
            if (ssComment.substr(0, 4) == "///<") // C++-style Javadoc comment following statement
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style_javadoc_post);
                nPos = 4;
            }
            else if (ssComment.substr(0, 4) == "//!<") // C++-style QT comment following statement
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style_qt_post);
                nPos = 4;
            }
            else if (ssComment.substr(0, 3) == "///") // C++-style Javadoc comment
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style_javadoc);
                nPos = 3;
            }
            else if (ssComment.substr(0, 3) == "//!") // C++-style QT comment
            {
                uiLocalFlags = static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style_qt);
                nPos = 3;
            }

            // If available, skip one space
            if (ssComment[nPos] == ' ') nPos++;

            // Do not include any whitespace at the end
            size_t nLen = ssComment.length() - nPos;
            while (nLen > 0 && std::isspace(ssComment[nPos + nLen - 1]))
                nLen--;

            // Copy the line
            if (nLen)
            {
                if (!sstreamComments.str().empty()) sstreamComments << std::endl;
                sstreamComments << ssComment.substr(nPos, nLen);
            }
        }
        else
            continue;   // Not a normal comment - ERROR?

        // Determine on how to combine the flags. For the first comment, the flags are all cleared.
        if (!bEmpty)
        {
            // Javadoc favors over QT comment.
            if (((uiLocalFlags | uiFlags) & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_mask)) ==
                (static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_qt) |
                static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_javadoc)))
                uiFlags = (uiFlags & ~static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_mask)) |
                static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_javadoc);

            // Formatted comment favors over unformatted comment.
            if ((uiLocalFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_mask)) &&
                !(uiFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_mask)))
                uiFlags = uiFlags | (uiLocalFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_mask));

            // C-Style favors over C++-style comment.
            if ((uiLocalFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style)) !=
                (uiFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style)))
                uiFlags = (uiFlags & ~static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::cpp_style)) |
                    static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::c_style);

            // Preceding favors over succeeding comment.
            if ((uiLocalFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::loc_succeeding)) !=
                (uiFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::loc_succeeding)))
                uiFlags = uiFlags & ~static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::loc_succeeding);
        }
        else // First comment or only C-style comment available.
            uiFlags = uiLocalFlags;
    }

    // Are there any comments to store?
    if (sstreamComments.str().empty()) return;

    // Replace the comment string if the existing comment string doesn't hold any formatted comments
    if ((m_uiCommentFlags & static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::format_mask)) == 0)
    {
        m_ssComments = sstreamComments.str();
        m_uiCommentFlags = uiFlags;
    }

    // Is this a succeeding comment? If so, set the flag and remove the comments from the parser
    if (!bPreceeding)
    {
        m_uiCommentFlags |= static_cast<uint32_t>(sdv::idl::IEntityComments::ECommentMask::loc_succeeding);
        if (!m_pParser)
            throw CCompileException("Internal error: no parser available.");
        m_pParser->SkipAdjacentComments();
    }
}

CTokenList CEntity::GetPreCommentTokenList()
{
    CToken token = PeekToken();
    uint32_t uiLine = token.GetLine();
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    CTokenList lstComments = m_pParser->GetComments();
    CTokenList lstPreComments;
    for (CTokenList::reverse_iterator itComment = lstComments.rbegin(); itComment != lstComments.rend(); ++itComment)
    {
        // Check for a valid context
        if (!token.GetContext().get()) break;

        // Are the comments within the same context?
        if (*itComment->GetContext().get() != *token.GetContext().get()) continue;

        // Is this comment belonging to the entity?
        if (itComment->GetEndLine() < uiLine - 1) break;

        // Insert the comment before the other comments of this entity.
        lstPreComments.push_front(*itComment);
        uiLine = itComment->GetLine();
    }
    return lstPreComments;
}

void CEntity::ProcessPostCommentTokenList(uint32_t uiLine)
{
    uint32_t uiLineTemp = uiLine;
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    CTokenList lstComments = m_pParser->GetComments();
    CTokenList lstPostComments;
    for (CTokenList::iterator itComment = lstComments.begin(); itComment != lstComments.end(); ++itComment)
    {
        // Are the comments within the same context?
        if (*itComment->GetContext().get() != *GetContext().get()) continue;

        // Is this comment belonging to the entity?
        if (itComment->GetLine() > uiLineTemp) break;

        // Insert the comment after the other comments of this entity.
        lstPostComments.push_back(*itComment);
        uiLineTemp = itComment->GetEndLine() + 1;
    }
    if (!lstPostComments.empty())
        SetCommentTokens(lstPostComments, false);
}

sdv::u8string CEntity::GetComments(uint32_t& ruiFlags) const
{
    ruiFlags = m_uiCommentFlags;
    return m_ssComments;
}

sdv::idl::IEntityContext::ELocation CEntity::GetLocation() const
{
    return m_ptrContext ? m_ptrContext->GetLocation() : sdv::idl::IEntityContext::ELocation::source;
}

sdv::u8string CEntity::GetSourcePath() const
{
    if (!m_ptrContext) return sdv::u8string();
    return m_ptrContext->Source().GetPathRef().generic_u8string();
}

void CEntity::GetPosition(uint32_t& ruiLineBegin, uint32_t& ruiColBegin, uint32_t& ruiLineEnd, uint32_t& ruiColEnd)
{
    ruiLineBegin = m_uiLineBegin;
    ruiColBegin = m_uiColBegin;
    ruiLineEnd = m_uiLineEnd;
    ruiColEnd = m_uiColEnd;
}

void CEntity::CalcHash(CHashObject& rHash) const
{
    // Add the name to the hash
    rHash << GetScopedName();
}

const CParser& CEntity::GetParserRef() const
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    return *m_pParser;
}

CParser& CEntity::GetParserRef()
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    return *m_pParser;
}

CToken CEntity::GetToken()
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    CToken token;
    // When there is a tokenlist, only process the tokens from the tokenlist; do not ask the parser.
    if (!m_lstLocalTokenList.empty())
    {
        token = m_lstLocalTokenList.Current();
        ++m_lstLocalTokenList;
    } else
        token = m_pParser->GetToken();
    return token;
}

CToken CEntity::GetLastValidToken() const
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    return m_pParser->GetLastValidToken();
}

CToken CEntity::PeekToken(size_t nIndex /*= 0*/)
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    CToken token;
	// When there is a tokenlist, only process the tokens from the tokenlist; do not ask the parser.
	if (!m_lstLocalTokenList.empty())
        token = m_lstLocalTokenList.Current(nIndex);
    else
        token = m_pParser->PeekToken(nIndex);
    return token;
}

void CEntity::PrependToken(CToken& rToken)
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    if (!rToken.GetContext())
        rToken.SetContext(m_ptrContext);

    // When there is a tokenlist, only process the tokens from the tokenlist; do not ask the parser.
	if (!m_lstLocalTokenList.empty())
        m_lstLocalTokenList.insert(rToken);
    else
        m_pParser->PrependToken(rToken);
}

CEntityPtr CEntity::Find(const std::string& rssScopedName, bool bCheckParent /*= true*/) const
{
    if (rssScopedName.empty()) return nullptr;

    size_t nScopeSeparator = rssScopedName.find("::");
    size_t nMemberSeparator = rssScopedName.find(".");
    size_t nSeparator = std::min(nScopeSeparator, nMemberSeparator);

    // Special case if the separator at the beginning (root).
    if (nScopeSeparator == 0)
    {
        // Execute the request through the root.
        CModuleEntity* pRootModule = GetRootEntity()->Get<CModuleEntity>();
        if (!pRootModule) throw CCompileException("Internal error: no root module available.");
        return pRootModule->Find(rssScopedName.substr(nSeparator + 2));
    }

    // Check the current map of child entities for the fitting scoped name.
    CEntityPtr ptrChild = FindLocal(rssScopedName.substr(0, nSeparator), true).first;
    if (!ptrChild) ptrChild = FindLocal(rssScopedName.substr(0, nSeparator), false).first;

    // Check for any anonymous children, if they exist, forward the request to them.
    // NOTE: If returning, the complete scoped name has been processed, meaning, if a child is returned, it is the target entity.
    if (!ptrChild)
    {
        for (CEntityPtr ptrChildTemp : m_vecChildren)
        {
            const CDeclarationEntity* pDeclaration = ptrChildTemp->Get<CDeclarationEntity>();
            if (pDeclaration && pDeclaration->IsAnonymous())
            {
                // Get the type entity of the declaration (resolve any typedefs).
                CEntityPtr ptrTypeEntity = pDeclaration->GetTypeEntity()->GetResolvedEntity();
                if (ptrTypeEntity)
                    ptrChild = ptrTypeEntity->Find(rssScopedName, false);
                if (ptrChild) return ptrChild;
            }
        }
    }

    // If a child was found find the next part of the scoped string
    if (ptrChild && nSeparator != std::string::npos)
    {
        // Definitions are separated by the scope operator.
        if (nScopeSeparator != std::string::npos)
        {
            // Resolve the typedef before accessing children.
            ptrChild = ptrChild->GetResolvedEntity();
            const CDefinitionEntity* pDefinition = ptrChild->Get<CDefinitionEntity>();
            ptrChild = pDefinition ? ptrChild->Find(rssScopedName.substr(nSeparator + 2), false) : nullptr;
        } else // Declarations are separated by the member operator.
        {
            // Find the member
            ptrChild = ptrChild->FindMember(rssScopedName.substr(nSeparator + 1));
        }
    }
    if (ptrChild) return ptrChild;

    // If there is no child, is parent checking allowed?
    if (!bCheckParent) return nullptr;

    // Check the parent until there is a child fitting or there is no parent any more.
    if (!ptrChild && m_ptrParent)
        ptrChild = m_ptrParent->Find(rssScopedName, bCheckParent);

    // Return the result
    return ptrChild;
}

CValueNodePtr CEntity::FindValue(const CTokenList& rlstScopedNameInclMembers) const
{
    // Get the value of the scoped name.
    std::pair<std::string, CEntityPtr> prScopedName = ProcessScopedName(rlstScopedNameInclMembers);
    if (!prScopedName.second)
        throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Entity not found.");
    CValueNodePtr ptrValue = prScopedName.second->ValueRef();
    if (!ptrValue) throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Internal error: entity doesn't have a value.");

    // Process any arrays and or members....
    while (true)
    {
        if (!rlstScopedNameInclMembers.Current()) break;

        // Deal with arrays first.
        if (rlstScopedNameInclMembers.Current() == "[")     // Array node
        {
            // The value node pointer should be an array pointer
            CArrayValueNode* pArrayValueNode = ptrValue->Get<CArrayValueNode>();
            if (!pArrayValueNode)
                throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Unexpected token '['.");

            ++rlstScopedNameInclMembers;    // Skip bracket

            // Resolve the value
            std::pair<CConstVariant, bool> prIndex = ProcessNumericExpression(rlstScopedNameInclMembers);
            if (prIndex.second)
                throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Array indices must be a const value.");
            size_t nIndex = prIndex.first.Get<size_t>();

            // Expect closing bracket
            if (rlstScopedNameInclMembers.Current() != "]")
                throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Expecting '['.");
            ++rlstScopedNameInclMembers;    // Skip bracket

            // Get the value node...
            ptrValue = (*pArrayValueNode)[nIndex];
            if (!ptrValue)
                throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Invalid index for array.");
        } else if (rlstScopedNameInclMembers.Current() == ".")  // Compound node
        {
            // The value node pointer should be a compound value pointer
            const CCompoundTypeValueNode* pCompoundValueNode = ptrValue->Get<CCompoundTypeValueNode>();
            if (!pCompoundValueNode)
                throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Unexpected token '.'.");


            ++rlstScopedNameInclMembers;    // Skip dot

            // Expecting an identifier
            if (rlstScopedNameInclMembers.Current().GetType() != ETokenType::token_identifier) return nullptr;

            // Get the member
            ptrValue = pCompoundValueNode->Member(rlstScopedNameInclMembers.Current());
            if (!ptrValue)
                throw CCompileException(rlstScopedNameInclMembers.LastValid(), "Member '",
                    static_cast<std::string>(rlstScopedNameInclMembers.Current()), "' not found.");
            ++rlstScopedNameInclMembers; // Skip identifier
        } else
            break;  // Done....
    }

    return ptrValue;
}

CValueNodePtr CEntity::FindValue(const std::string& rssScopedNameInclMembers) const
{
    try
    {
        return FindValue(Tokenize(rssScopedNameInclMembers, GetContext()));
    } catch (const sdv::idl::XCompileError&)
    {}
    return nullptr;
}

CConstVariant CEntity::FindValueVariant(const std::string& rssScopedNameInclMembers) const
{
    const CSimpleTypeValueNode* pSimpleValueNode = FindValue<CSimpleTypeValueNode>(rssScopedNameInclMembers);
    return pSimpleValueNode ? pSimpleValueNode->Variant() : CConstVariant();
}

uint64_t CEntity::GetId() const
{
    CHashObject hash;
    CalcHash(hash);
    return hash.GetHash();
}

std::pair<std::string, CEntityPtr> CEntity::ProcessScopedName(bool bNoSearchError /*= false*/)
{
    // Get the scoped name
    // NOTE: a scoped name could start with the scope operator '::' identifying a root based type.
    CToken token = GetToken();
    std::string ssName = token;
    while (true)
    {
        if (ssName.back() != ':')
        {
            token = GetToken();
            if (token != "::")
            {
                PrependToken(token);
                break;
            }

            ssName += "::";
        }

        token = GetToken();
        if (token.GetType() != ETokenType::token_identifier)
            throw CCompileException(token, "Expecting an identifier.");
        ssName += static_cast<std::string>(token);
    }

    // Find the corresponding type (resolve typedefs).
    CEntityPtr ptrEntity = Find(ssName, true);
    if (!bNoSearchError && !ptrEntity)
        throw CCompileException(token, "Could not find the definition of '", ssName, "'.");
    return std::make_pair(ssName, ptrEntity);
}

std::pair<std::string, CEntityPtr> CEntity::ProcessScopedName(const CTokenList& rlstExpression,
    bool bNoSearchError /*= false*/) const
{
    if (rlstExpression.End())
        throw CCompileException("Internal error: trying to retrieve scoped name from empty tokenlist.");

    // Get the scoped name
    // NOTE: a scoped name could start with the scope operator '::' identifying a root based type.njjot
    std::string ssName = rlstExpression.Current();
    while (true)
    {
        ++rlstExpression;
        if (ssName.back() != ':')
        {
            if (rlstExpression.Current() != "::")
                break;

            ssName += "::";
            ++rlstExpression;
        }

        if (rlstExpression.Current().GetType() != ETokenType::token_identifier)
            throw CCompileException(rlstExpression.LastValid(), "Expecting an identifier.");
        ssName += static_cast<std::string>(rlstExpression.Current());
    }

    // Find the corresponding type.
    // Find the corresponding type.
    CEntityPtr ptrEntity = Find(ssName);
    if (!bNoSearchError && !ptrEntity)
        throw CCompileException(rlstExpression.LastValid(), "Could not find the definition of '", ssName, "'.");

    // In case the entity is a typedef, const or a variable declaration, trigger the processing of the assignment for this
    // entity, so that it contains a value which can be accessed by the function calling the ProcessScopedName. The call for
    // assignment processing will either do nothing (when the processing was done before already), cause an exception (if the
    // call to ProcessScopedName was triggered by the processing of the assignment - circular reference) or trigger processing
    // (if the processing hadn't been done yet, but is needed for the further processing of the assignment calling this
    // ProcessScopedName function).
    CDeclarationEntity* pBasicType = ptrEntity ? ptrEntity->Get<CDeclarationEntity>() : nullptr;
    if (pBasicType) pBasicType->PostProcess();

    // Return result (resolve any typedef)
    return std::make_pair(ssName, ptrEntity);
}

std::pair<CConstVariant, bool> CEntity::ProcessNumericExpression(const CTokenList& rlstExpression,
    uint32_t uiPrecedence /*= 100*/) const
{
    if (rlstExpression.End()) throw CCompileException("Expecting expression.");

    // Token can be either a left parenthesis, a unary operator (precedence 0), an identifier (defined, macro or unknown), or a
    // number.
    std::pair<CConstVariant, bool> prLValue{0, false};
    if (rlstExpression.Current() == "(")
    {
        ++rlstExpression;

        // Calculate the content between the parenthesis.
        prLValue = ProcessNumericExpression(rlstExpression);

        // Expecting a closing parenthesis
        if (rlstExpression.End() || rlstExpression.Current() != ")")
            throw CCompileException(rlstExpression.LastValid(), "Expecting a right parenthesis.");

        ++rlstExpression;
    } else if (rlstExpression.Current() == "-")  // unary minus
    {
        ++rlstExpression;

        prLValue = ProcessNumericExpression(rlstExpression, 0);
        prLValue.first = -prLValue.first;
    }
    else if (rlstExpression.Current() == "+") // unary plus
    {
        ++rlstExpression;
        prLValue = ProcessNumericExpression(rlstExpression, 0);
    }
    else if (rlstExpression.Current() == "!") // logical not
    {
        ++rlstExpression;
        prLValue = ProcessNumericExpression(rlstExpression, 0);
        prLValue.first = !prLValue.first;
    }
    else if (rlstExpression.Current() == "~") // bitwise not
    {
        ++rlstExpression;
        prLValue = ProcessNumericExpression(rlstExpression, 0);
        prLValue.first = ~prLValue.first;
    }
    else if (rlstExpression.Current() == "::" || rlstExpression.Current().GetType() == ETokenType::token_identifier)
    {
        // Process the identifier
        std::pair<std::string, CEntityPtr> prIdentifier = ProcessScopedName(rlstExpression);

        // Assignments of identifier must be defined first. Type must be of CDeclarationEntity.
        CDeclarationEntity* pEntity = prIdentifier.second->Get<CDeclarationEntity>();
        if (!pEntity) throw CCompileException(rlstExpression.LastValid(), "Identifier is not a definition.");

        // If the identifier is not const, the result is dynamic
        if (!pEntity->IsReadOnly())
            prLValue.second = true;

        // Get the value
        CValueNodePtr ptrValue = pEntity->ValueRef();
        if (!ptrValue) throw CCompileException(rlstExpression.LastValid(), "Identifier doesn't have any value.");

        // If the value is an array, get the indices.
        while (ptrValue->IsArray())
        {
            CArrayValueNode* pArrayValue = ptrValue->Get<CArrayValueNode>();
            if (!pArrayValue)
                throw CCompileException(rlstExpression.LastValid(), "Internal error: the value has array flag set, but is not an array value.");

            if (!rlstExpression.End() && rlstExpression.Current() != "[")
                throw CCompileException(rlstExpression.LastValid(), "Expecting a left square bracket '[', value is an array.");
            ++rlstExpression;

            // Get the value...
            std::pair<CConstVariant, bool> prIndex = ProcessNumericExpression(rlstExpression);

            // The value must be of integral type
            if (!prIndex.first.IsIntegral())
                throw CCompileException(rlstExpression.LastValid(), "Only integral data types are supported for the array size.");

            // If the value is dynamic, set the result to dynamic.
            if (prIndex.second) prLValue.second = true;

            // If the result is dynamic get the initial index
            ptrValue = (*pArrayValue)[prIndex.first.Get<int64_t>()];

            if (!rlstExpression.End() && rlstExpression.Current() != "]")
                throw CCompileException(rlstExpression.LastValid(), "Expecting a right square bracket ']'.");
            ++rlstExpression;
        }

        const CSimpleTypeValueNode* pValue = ptrValue->Get<CSimpleTypeValueNode>();
        if (!pValue)
            throw CCompileException(rlstExpression.LastValid(),
                "Internal error: the entity is defined as declaration type entity, but the value is not a declaration type"
                " value.");
        prLValue.first = pValue->Variant();
    } else if (rlstExpression.Current().GetType() == ETokenType::token_literal)
    {
        prLValue.first = rlstExpression.Current().ValueRef();
        ++rlstExpression;
    }
    else
        throw CCompileException(rlstExpression.LastValid(), "Unexpected token '", static_cast<std::string>(rlstExpression.Current()), "'.");

    // Run until all operations at this or any higher precedence have been executed
    bool bDone = false;
    while (!bDone)
    {
        // Done when the expression is terminated using one of the separators or no more tokens are available.
        if (rlstExpression.End() ||
           rlstExpression.Current() == "}" ||
           rlstExpression.Current() == "]" ||
           rlstExpression.Current() == "," ||
           rlstExpression.Current() == ";")
        {
            bDone = true;
            continue;
        }

        // If right parenthesis, leave the parenthesis and we're done.
        if (rlstExpression.Current() == ")")
        {
            bDone = true;
            continue;
        }

        // Conditionally calculate the result based on the supplied operator function and the precedence level.
        auto fnCalculate = [&](std::function<CConstVariant(CConstVariant&, CConstVariant&)> fnCalculation,
            uint32_t uiOperatorPrecedence)
        {
            // If operator has höhere oder gleiche precedence, replace operator and we're done.
            if (uiOperatorPrecedence >= uiPrecedence)
            {
                bDone = true;
                return;
            }

            // Skip the operator
            ++rlstExpression;

            // If operator has niedriger precedence, get next token and calculate result.
            std::pair<CConstVariant, bool> prRValue = ProcessNumericExpression(rlstExpression, uiOperatorPrecedence);

            // Calculate the result.
            prLValue.first = fnCalculation(prLValue.first, prRValue.first);
        };

        // Check for the operator and calculate the result
        const CToken& rtoken = rlstExpression.Current();
        if (rtoken == "*")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 * rvar2; }, 1);   continue; }
        if (rtoken == "/")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 / rvar2; }, 1);   continue; }
        if (rtoken == "+")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 + rvar2; }, 2);   continue; }
        if (rtoken == "-")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 - rvar2; }, 2);   continue; }
        if (rtoken == "<")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 < rvar2; }, 4);   continue; }
        if (rtoken == "<=") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 <= rvar2; }, 4);  continue; }
        if (rtoken == ">")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 > rvar2; }, 4);   continue; }
        if (rtoken == ">=") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 >= rvar2; }, 4);  continue; }
        if (rtoken == "==") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 == rvar2; }, 5);  continue; }
        if (rtoken == "!=") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 != rvar2; }, 5);  continue; }
        if (rtoken == "%")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 % rvar2; }, 1);   continue; }
        if (rtoken == "<<") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 << rvar2; }, 3);  continue; }
        if (rtoken == ">>") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 >> rvar2; }, 3);  continue; }
        if (rtoken == "&")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 & rvar2; }, 6);   continue; }
        if (rtoken == "^")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 ^ rvar2; }, 7);   continue; }
        if (rtoken == "|")  { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 | rvar2; }, 8);   continue; }
        if (rtoken == "&&") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 && rvar2; }, 9);  continue; }
        if (rtoken == "||") { fnCalculate([](const CConstVariant& rvar1, const CConstVariant& rvar2){ return rvar1 || rvar2; }, 10); continue; }

        // If not an operator, this is an error
        throw CCompileException(rlstExpression.LastValid(), "Expecting operator");
    };

    return prLValue;
}

std::pair<CConstVariant, bool> CEntity::ProcessStringExpression(const CTokenList& rlstExpression) const
{
    std::pair<CConstVariant, bool> prValue{0, false};

    if (rlstExpression.End()) throw CCompileException("Expecting expression.");

    // Run until all the strings are read (they are joined together).
    // If identifiers are used... this must be one single assignment.
    bool bDone = false;
    bool bInitial = true;
    bool bIdentifier = false;
    while (!bDone)
    {
        if (rlstExpression.End() ||
                   rlstExpression.Current() == "}" ||
                   rlstExpression.Current() == "]" ||
                   rlstExpression.Current() == "," ||
                   rlstExpression.Current() == ";")
        {
            if (bInitial) throw CCompileException(rlstExpression.LastValid(), "Missing assignment.");
            bDone = true;
            continue;
        }

        if (rlstExpression.Current() == "::" || rlstExpression.Current().GetType() == ETokenType::token_identifier)
        {
            if (!bInitial)
                throw CCompileException(rlstExpression.LastValid(), "Cannot mix strings and identifiers.");

            // Process the identifier
            std::pair<std::string, CEntityPtr> prIdentifier = ProcessScopedName(rlstExpression);

            // Assignments of identifier must be defined first. Type must be of CDeclarationEntity.
            CDeclarationEntity* pEntity = prIdentifier.second->Get<CDeclarationEntity>();
            if (!pEntity) throw CCompileException(rlstExpression.LastValid(), "Identifier is not a definition.");

            // If the identifier is not const, the result is dynamic
            if (!pEntity->IsReadOnly())
                prValue.second = true;

            // Get the value
            CValueNodePtr ptrValue = pEntity->ValueRef();
            if (!ptrValue) throw CCompileException(rlstExpression.LastValid(), "Identifier doesn't have any value.");

            // If the value is an array, get the indices.
            while (ptrValue->IsArray())
            {
                CArrayValueNode* pArrayValue = ptrValue->Get<CArrayValueNode>();
                if (!pArrayValue)
                    throw CCompileException(rlstExpression.LastValid(), "Internal error: the value has array flag set, but is not an array value.");

                if (!rlstExpression.End() && rlstExpression.Current() != "[")
                    throw CCompileException(rlstExpression.LastValid(), "Expecting a left square bracket '[', value is an array.");
                ++rlstExpression;

                // Get the value...
                std::pair<CConstVariant, bool> prIndex = ProcessNumericExpression(rlstExpression);

                // The value must be of integral type
                if (!prIndex.first.IsIntegral())
                    throw CCompileException(rlstExpression.LastValid(), "Only integral data types are supported for the array size.");

                // If the value is dynamic, set the result to dynamic.
                if (prIndex.second) prValue.second = true;

                ptrValue = (*pArrayValue)[prIndex.first.Get<int64_t>()];

                if (!rlstExpression.End() && rlstExpression.Current() != "]")
                    throw CCompileException(rlstExpression.LastValid(), "Expecting a right square bracket ']'.");
                ++rlstExpression;
            }

            const CSimpleTypeValueNode* pValue = ptrValue->Get<CSimpleTypeValueNode>();
            if (!pValue)
                throw CCompileException(rlstExpression.LastValid(),
                    "Internal error: the entity is defined as declaration type entity, but the value is not a declaration type"
                    " value.");
            prValue.first = pValue->Variant();
            bIdentifier = true;
        } else if (rlstExpression.Current().IsString())
        {
            if (bIdentifier) throw CCompileException(rlstExpression.LastValid(), "Cannot mix strings and identifiers.");

            // Check for UTF-8 and ASCII using the same data type
            const CDeclarationEntity* pDeclaration = Get<CDeclarationEntity>();
            if (pDeclaration &&
                ((pDeclaration->GetBaseType() == sdv::idl::EDeclType::decltype_string && !rlstExpression.Current().IsAscii()) ||
                 (pDeclaration->GetBaseType() == sdv::idl::EDeclType::decltype_u8string && !rlstExpression.Current().IsUtf8())))
                throw CCompileException(rlstExpression.LastValid(), "The string value doesn't fit the target value.");
            prValue.first = rlstExpression.Current().ValueRef();
            ++rlstExpression;
        } else
            throw CCompileException(rlstExpression.LastValid(), "Unexpected token '", static_cast<std::string>(rlstExpression.Current()), "'.");

        bInitial = false;
    };
    return prValue;
}

CTypeDeclaration CEntity::ProcessType(bool bNoSearchError /*= false*/)
{
    CTypeDeclaration typedecl;
    CLog log("Processing the type...");

    // Get the type identifier.
    CToken token = GetToken();
    if (token.GetType() != ETokenType::token_identifier && token.GetType() != ETokenType::token_keyword && token != "::")
        throw CCompileException(token, "Expecting a type identifier.");
    typedecl.SetTypeString(static_cast<std::string>(token));

    // Special cases: unsigned and long
    if (token == "long")
    {
        token = GetToken();
        if (token == "long") typedecl.AddTypeString(" long");
        else if (token == "double") typedecl.AddTypeString(" double");
        else PrependToken(token);
    } else if (token == "unsigned")
    {
        token = GetToken();
        if (token == "short") typedecl.AddTypeString(" short");
        else if (token == "long")
        {
            typedecl.AddTypeString(" long");
            token = GetToken();
            if (token == "long") typedecl.AddTypeString(" long");
            else PrependToken(token);
        }
    }

    // Check for a defined type?
    typedecl.SetBaseType(StringToDeclType(typedecl.GetTypeString()));

    // If the type is enum, struct or union or if the type could not be estimated, set the scoped name flag to potentially get the
    // rest of the name.
    std::pair<std::string, CEntityPtr> prEntity;
    switch (typedecl.GetBaseType())
    {
        case sdv::idl::EDeclType::decltype_enum:
            log << "Declaration type is 'enum'..." << std::endl;

            prEntity = ProcessScopedName(bNoSearchError);
            typedecl.SetTypeString(prEntity.first);
            log << "Declaration uses type " << typedecl.GetTypeString() << "..." << std::endl;
            if (!prEntity.second->Get<CEnumEntity>())
                throw CCompileException(token, "Provided identifier is not an enum.");
            typedecl.SetTypeDefinitionEntityPtr(prEntity.second);
            break;
        case sdv::idl::EDeclType::decltype_struct:
            log << "Declaration type is struct..." << std::endl;

            prEntity = ProcessScopedName(bNoSearchError);
            typedecl.SetTypeString(prEntity.first);
            log << "Declaration uses type " << typedecl.GetTypeString() << "..." << std::endl;
            if (!prEntity.second->Get<CStructEntity>())
                throw CCompileException(token, "Provided identifier is not a struct.");
            typedecl.SetTypeDefinitionEntityPtr(prEntity.second);
            break;
        case sdv::idl::EDeclType::decltype_interface:
            {
            log << "Declaration type is interface..." << std::endl;

            prEntity = ProcessScopedName(bNoSearchError);
            typedecl.SetTypeString(prEntity.first);
            log << "Declaration uses type " << typedecl.GetTypeString() << "..." << std::endl;
            if (!prEntity.second->Get<CInterfaceEntity>())
                throw CCompileException(token, "Provided identifier is not an interface.");
            typedecl.SetTypeDefinitionEntityPtr(prEntity.second);

            break;
        }
        case sdv::idl::EDeclType::decltype_union:
            log << "Declaration type is 'union'..." << std::endl;

            prEntity = ProcessScopedName(true); // Could be without name union followed by switch directly
            typedecl.SetTypeString(prEntity.first);
            log << "Declaration uses type " << typedecl.GetTypeString() << "..." << std::endl;
            if (!prEntity.second->Get<CUnionEntity>())
                throw CCompileException(token, "Provided identifier is not an interface.");
            typedecl.SetTypeDefinitionEntityPtr(prEntity.second);
            break;
		case sdv::idl::EDeclType::decltype_fixed:
            // Check for optional fixed length parameter
            token = PeekToken();
            if (token != "<") break;
            GetToken(); // Skip the bracket...
            // Get the size literal
            token = GetToken();
            if (token.GetType() != ETokenType::token_literal || !token.IsInteger() || token.ValueRef().Get<uint32_t>() < 1)
                throw CCompileException(token, "Expecting a positive number for the fixed size of the fixed point type.");
            typedecl.SetFixedLength(token.ValueRef().Get<uint32_t>());
            token = GetToken();
            if (token != ",")
                throw CCompileException(token, "Expecting a comma ','.");
            // Get the decimals literal
            token = GetToken();
            if (token.GetType() != ETokenType::token_literal || !token.IsInteger() ||
                token.ValueRef().Get<uint32_t>() > typedecl.GetFixedLength())
                throw CCompileException(token, "Expecting a positive number or zero for the decimals of the fixed point type.");
            typedecl.SetDecimals(token.ValueRef().Get<uint32_t>());
            token = GetToken();
            if (token == ">>") //!< Mistakingly detected as shift operator, but closing first nested template expression.
            {
                token = CToken(">", ETokenType::token_operator);
                PrependToken(token);
            }
            if (token != ">")
                throw CCompileException(token, "Expecting '>' closing the template section of the string.");
            break;
        case sdv::idl::EDeclType::decltype_string:
        case sdv::idl::EDeclType::decltype_wstring:
        case sdv::idl::EDeclType::decltype_u16string:
        case sdv::idl::EDeclType::decltype_u32string:
        case sdv::idl::EDeclType::decltype_u8string:
            // Check for optional fixed length parameter
            token = PeekToken();
            if (token != "<") break;
            GetToken(); // Skip the bracket...
            // Get the size literal
            token = GetToken();
            if (token.GetType() != ETokenType::token_literal || !token.IsInteger() || token.ValueRef().Get<uint32_t>() < 1)
                throw CCompileException(token, "Expecting a positive number for the fixed size of the string.");
            typedecl.SetFixedLength(token.ValueRef().Get<uint32_t>());
            token = GetToken();
            if (token == ">>") //!< Mistakingly detected as shift operator, but closing first nested template expression.
            {
                token = CToken(">", ETokenType::token_operator);
                PrependToken(token);
            }
            if (token != ">")
                throw CCompileException(token, "Expecting '>' closing the template section of the string.");
            break;
		case sdv::idl::EDeclType::decltype_pointer:
		case sdv::idl::EDeclType::decltype_sequence:
            // Check for data type and fixed length parameter (latter is optional).
            if (GetToken() != "<")
                throw CCompileException(token, "Expecting '<' defining the template section.");
            // Create a data type
            typedecl.SetValueTypePtr(std::make_shared<CTypeDeclaration>(ProcessType(bNoSearchError)));
            // Check for a comma or a closing bracket
            token = GetToken();
            if (token == ",")
            {
                // Get the size literal
                token = GetToken();
                if (token.GetType() != ETokenType::token_literal || !token.IsInteger() || token.ValueRef().Get<uint32_t>() < 1)
                    throw CCompileException(token, "Expecting a positive number for the fixed size.");
                typedecl.SetFixedLength(token.ValueRef().Get<uint32_t>());
                token = GetToken();
            }
			if (token == ">>") //!< Mistakingly detected as shift operator, but closing first nested template expression.
			{
				token = CToken(">", ETokenType::token_operator);
				PrependToken(token);
			}
            if (token != ">")
                throw CCompileException(token, "Expecting '>' closing the template section.");
            break;
		case sdv::idl::EDeclType::decltype_map:
            // Check for data types of key and value and fixed length parameter (latter is optional).
			if (GetToken() != "<")
				throw CCompileException(token, "Expecting '<' defining the template section.");
			// Create a data type for the key
			typedecl.SetKeyTypePtr(std::make_shared<CTypeDeclaration>(ProcessType(bNoSearchError)));
			// Check for a comma
			token = GetToken();
			if (token != ",") throw CCompileException(token, "Expecting a comma ','.");
			// Create a data type for the value
			typedecl.SetValueTypePtr(std::make_shared<CTypeDeclaration>(ProcessType(bNoSearchError)));
			// Check for a comma or a closing bracket
			token = GetToken();
			if (token == ",")
			{
				// Get the size literal
				token = GetToken();
				if (token.GetType() != ETokenType::token_literal || !token.IsInteger() || token.ValueRef().Get<uint32_t>() < 1)
					throw CCompileException(token, "Expecting a positive number for the fixed size.");
				typedecl.SetFixedLength(token.ValueRef().Get<uint32_t>());
				token = GetToken();
			}
			if (token == ">>") //!< Mistakingly detected as shift operator, but closing first nested template expression.
			{
				token = CToken(">", ETokenType::token_operator);
				PrependToken(token);
			}
			if (token != ">")
				throw CCompileException(token, "Expecting '>' closing the template section.");
			break;
		case sdv::idl::EDeclType::decltype_unknown:
            {
                // The type string still contains (part) of the scoped type name. Parse again
                CToken tokenType(typedecl.GetTypeString());
                PrependToken(tokenType);
                prEntity = ProcessScopedName(bNoSearchError);

                typedecl.SetTypeString(prEntity.first);
                if (!prEntity.second)
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (system type)..." << std::endl;
                else if (prEntity.second->Get<CUnionEntity>())
                {
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (union)..." << std::endl;
                    typedecl.SetBaseType(sdv::idl::EDeclType::decltype_union);
                }
                else if (prEntity.second->Get<CExceptionEntity>())
                {
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (union)..." << std::endl;
                    typedecl.SetBaseType(sdv::idl::EDeclType::decltype_union);
                }
                else if (prEntity.second->Get<CStructEntity>())
                {
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (struct)..." << std::endl;
                    typedecl.SetBaseType(sdv::idl::EDeclType::decltype_struct);
                }
                else if (prEntity.second->Get<CEnumEntity>())
                {
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (enum)..." << std::endl;
                    typedecl.SetBaseType(sdv::idl::EDeclType::decltype_enum);
                }
                else if (prEntity.second->Get<CTypedefEntity>())
                {
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (typedef)..." << std::endl;
                    typedecl.SetBaseType(prEntity.second->Get<CTypedefEntity>()->GetBaseType());
                }
                else if (prEntity.second->Get<CInterfaceEntity>())
                {
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (interface)..." << std::endl;
                    typedecl.SetBaseType(sdv::idl::EDeclType::decltype_interface);
                }
                else
                    log << "Declaration is of type " << typedecl.GetTypeString() << " (system type)..." << std::endl;

                typedecl.SetTypeDefinitionEntityPtr(prEntity.second);
            }
            break;
        default:
            log << "Declaration type is '" << typedecl.GetTypeString() << "'..." << std::endl;

            break;
    }


    // The following should not occur!
    if (!bNoSearchError && typedecl.GetBaseType() == sdv::idl::EDeclType::decltype_unknown)
        throw CCompileException(token, "Declaration type is invalid.");


    return typedecl;
}

void CEntity::SetName(const std::string& rssName, bool bForwardDeclaration /*= false*/, bool bNoInsert /*= false*/)
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    if (!m_ssName.empty())
        throw CCompileException("Internal error: entity name was set twice.");

    // Assign the name
    if (rssName.empty())
        throw CCompileException("Internal error: name was not provided while assigning it to the entity.");
    else
        m_ssName = rssName;

    // Check at the parent for an existing entity with the same name.
    bool bIsDeclaration = Get<CDeclarationEntity>() && !Get<CTypedefEntity>() ? true : false;
    if (!m_ptrParent) throw CCompileException("Internal error: parent was not set for this entity.");
    std::pair<CEntityPtr, bool> prEntity = m_ptrParent->FindLocal(m_ssName, bIsDeclaration);

    // Create lower case name to use as key in the entity map.
    std::string ssLCName = m_ssName;
    if (!m_pParser->GetEnvironment().CaseSensitiveTypeExtension())
    {
        std::transform(ssLCName.begin(), ssLCName.end(), ssLCName.begin(),
            [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
    }

    // Is this entity a declaration only, then set the flag.
    m_bForwardDeclaration = bForwardDeclaration;

    if (prEntity.first)  // Entity with same name exists already
    {
        if (prEntity.second) throw CCompileException("An identifier with the same name exists already through inheritance.");

        // Is this entity a declararion only, then ignore this declaration.
        if (bForwardDeclaration) return;

        // Check whether extendability is not allowed for this entity.
        if (!prEntity.first->m_bForwardDeclaration && !prEntity.first->IsExtendable())
            throw CCompileException("An identifier with the same name exists already (case insensitive name checking).");

        // Check whether the entity is of the same type.
        if (GetType() != prEntity.first->GetType())
            throw CCompileException("An identifier with the same name exists already (case insensitive name checking).");

        // Is a shared children map existing? If not, create one.
        if (!prEntity.first->m_ptrChildDefMap) prEntity.first->m_ptrChildDefMap = std::make_shared<CEntityMap>();
        if (!prEntity.first->m_ptrChildDeclMap) prEntity.first->m_ptrChildDeclMap = std::make_shared<CEntityMap>();

        // Use the existing children map
        m_ptrChildDefMap = prEntity.first->m_ptrChildDefMap;
        m_ptrChildDeclMap = prEntity.first->m_ptrChildDeclMap;

        // Update the child map entry if the stored map entry is only a forward declaration
        if (prEntity.first->m_bForwardDeclaration)
        {
            if (bIsDeclaration)
                m_ptrParent->m_ptrChildDeclMap->at(ssLCName) = shared_from_this();
            else
                m_ptrParent->m_ptrChildDefMap->at(ssLCName) = shared_from_this();
        }
    } else
    {
        if (!bNoInsert)
        {
            // Does the parent have a children map? If not, create one.
            if (!m_ptrParent->m_ptrChildDefMap) m_ptrParent->m_ptrChildDefMap = std::make_shared<CEntityMap>();
            if (!m_ptrParent->m_ptrChildDeclMap) m_ptrParent->m_ptrChildDeclMap = std::make_shared<CEntityMap>();

            // Assign the entity to the children map of the parent.
            if (bIsDeclaration)
                m_ptrParent->m_ptrChildDeclMap->insert(CEntityMap::value_type(ssLCName, shared_from_this()));
            else
                m_ptrParent->m_ptrChildDefMap->insert(CEntityMap::value_type(ssLCName, shared_from_this()));
        }
    }
}

void CEntity::ProcessSystemTypeAssignments()
{
    if (m_lstUnassigned.empty()) CLog() << "No unprocessed entity assignments found..." << std::endl;

    // Go through the list of unassigned entities and process all entity assignments.
    while (!m_lstUnassigned.empty())
    {
        CDeclarationEntity* pEntity = m_lstUnassigned.front()->Get<CDeclarationEntity>();
        if (pEntity) pEntity->PostProcess();
        m_lstUnassigned.pop_front();
    }
}

void CEntity::AddChild(CEntityPtr ptrChild)
{
    // Add the entity to the list of children.
    m_vecChildren.push_back(ptrChild);

    // If this is a declaration entity, add it to the unassigned list to allow assignment processing later
    if (ptrChild->Get<CDeclarationEntity>())
        m_lstUnassigned.push_back(ptrChild);
}

std::pair<CEntityPtr, bool> CEntity::FindLocal(const std::string& rssName, bool bDeclaration) const
{
    if (!m_pParser)
        throw CCompileException("Internal error: no parser available.");
    if (rssName.empty())
        throw CCompileException("Internal error: name was not provided while requesting to search for it.");

    // The definition could have children of its own. Declarations could be of a type that has child declarations.
    bool bIsDeclaration = Get<CDeclarationEntity>() && !Get<CTypedefEntity>() ? true : false;

    // When reuse of names is allowed, check the names within a declaration entity only when the name belongs to a declarations.
    // Otherwise the entity is a definition; check the names of declatations and definitions accordingly to the provided type and
    // the reuse.
    if (m_pParser->GetEnvironment().ContextDependentNamesExtension())
    {
        if (bIsDeclaration && !bDeclaration) return std::make_pair(CEntityPtr(), false);
    }

    // Differentiate between definition and declaration.
    if (bIsDeclaration) // Declaration
    {
        // Get the declarations from the definition
        CEntityPtr ptrTypeEntity = Get<CDeclarationEntity>()->GetTypeEntity();
        const CDefinitionEntity* pDefinition = ptrTypeEntity ? ptrTypeEntity->Get<CDefinitionEntity>() : nullptr;
        if (!pDefinition) return std::make_pair(CEntityPtr(), false);

        // Iterate through the declarations and check for the name
        for (CEntityPtr ptrDecl : pDefinition->GetDeclMembers())
            if (ptrDecl->GetName() == rssName) return std::make_pair(ptrDecl, false);
    }
    else // Definitions
    {
        // Is there a shared children map? If not, we're done...
        if (bDeclaration && !m_ptrChildDeclMap) return std::make_pair(CEntityPtr(), false);
        if (!bDeclaration && !m_ptrChildDefMap) return std::make_pair(CEntityPtr(), false);

        // Create lower case name to be able to do case-insensitive comparison.
        std::string ssLCName = rssName;
        if (!m_pParser->GetEnvironment().CaseSensitiveTypeExtension())
        {
            std::transform(ssLCName.begin(), ssLCName.end(), ssLCName.begin(),
                [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
        }

        // Search for an entity with the same name.
        if (bDeclaration || !m_pParser->GetEnvironment().ContextDependentNamesExtension())
        {
            CEntityMap::const_iterator itEntity = m_ptrChildDeclMap->find(ssLCName);
            if (itEntity != m_ptrChildDeclMap->end()) return std::make_pair(itEntity->second, false);

            // For each anonymous child entity, do the check.
            for (CEntityMap::value_type vtEntity : *m_ptrChildDeclMap)
            {
                if (vtEntity.second->Get<CDeclarationEntity>() && vtEntity.second->Get<CDeclarationEntity>()->IsAnonymous())
                {
                    std::pair<CEntityPtr, bool> prChild = vtEntity.second->FindLocal(rssName, bDeclaration);
                    if (prChild.first) return prChild;
                }
            }
        }
        if (!bDeclaration || !m_pParser->GetEnvironment().ContextDependentNamesExtension())
        {
            CEntityMap::const_iterator itEntity = m_ptrChildDefMap->find(ssLCName);
            if (itEntity != m_ptrChildDefMap->end()) return std::make_pair(itEntity->second, false);
        }
    }

    // No entity found.
    return std::make_pair(CEntityPtr(), false);
}

CEntityPtr CEntity::FindMember(const std::string& rssScopedName) const
{
    if (rssScopedName.empty()) return nullptr;
    size_t nMemberSeparator = rssScopedName.find(".");

    // Is this a variable? Then get the type.
    CEntityPtr ptrEntity;
    if (Get<CVariableEntity>())
        ptrEntity = Get<CVariableEntity>()->GetTypeEntity();

    // Not a definition, nor a variable - no members defined
    if (!ptrEntity) return nullptr;

    // Resolve any typedefs.
    ptrEntity = ptrEntity->GetResolvedEntity();

    // Is this a compound entity (any entity derived from the struct entity)?
    const CStructEntity* pStructEntity = ptrEntity->Get<CStructEntity>();
    if (!pStructEntity) return nullptr;

    // Go through the declarations from the struct
    CEntityList lstEntities = pStructEntity->GetDeclMembers();
    for (CEntityPtr ptrDecl : lstEntities)
    {
        CEntityPtr ptrChild;
        const CDeclarationEntity* pDeclaration = ptrDecl->Get<CDeclarationEntity>();
        if (!pDeclaration) continue;

        // If the declaration is anonympous, forward the call directly to the declaration (without name checking)
        if (pDeclaration->IsAnonymous())
            ptrChild = ptrDecl->FindMember(rssScopedName);
        else
        {
            // Check for the fitting name
            if (ptrDecl->GetName() != rssScopedName.substr(0, nMemberSeparator)) continue;

            // Any more members?
            if (nMemberSeparator == std::string::npos)
                ptrChild = ptrDecl;
            else
                ptrChild = ptrDecl->FindMember(rssScopedName.substr(nMemberSeparator + 1));
        }

        // Child found?
        if (ptrChild) return ptrChild;
    }

    // When here, no child found
    return nullptr;
}

CEntity::CEntityIterator::CEntityIterator(CEntityVector& rvecEntities) : m_rvecEntities(rvecEntities)
{}

sdv::interface_t CEntity::CEntityIterator::GetInterface(sdv::interface_id idInterface)
{
    if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
        return static_cast<sdv::IInterfaceAccess*>(this);
    if (idInterface == sdv::GetInterfaceId<sdv::idl::IEntityIterator>())
        return static_cast<sdv::idl::IEntityIterator*>(this);
    return nullptr;
}

uint32_t CEntity::CEntityIterator::GetCount() const
{
    return static_cast<uint32_t>(m_rvecEntities.size());
}

sdv::IInterfaceAccess* CEntity::CEntityIterator::GetEntityByIndex(uint32_t uiIndex)
{
    if (static_cast<size_t>(uiIndex) > m_rvecEntities.size()) return nullptr;
    return static_cast<sdv::IInterfaceAccess*>(m_rvecEntities[static_cast<size_t>(uiIndex)].get());
}

std::string CEntity::DeclTypeToString(sdv::idl::EDeclType eType) const
{
    for (const auto& rsEntry : m_vecDeclTypes)
        if (rsEntry.second == eType) return rsEntry.first;
    return std::string();
}

sdv::idl::EDeclType CEntity::StringToDeclType(const std::string& rssType) const
{
    for (const auto& rsEntry : m_vecDeclTypes)
        if (rsEntry.first == rssType) return rsEntry.second;
    return sdv::idl::EDeclType::decltype_unknown;
}
