// Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto.  Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once

#include <carb/flatcache/IToken.h>
#include <carb/flatcache/IPath.h>

#include <carb/flatcache/Type.h>

namespace carb
{
namespace flatcache
{
// Basic types
// Types and methods ending in C are intended to be used with C-ABI interfaces.

// PathToAttributesMap allows metadata to be attached to attributes.
// The metadata that exist currently are for (flatcache) internal use only.
// Abstractly it holds an array for each attribute, where element 0
// (NameSuffix::none) is the value itself, and other elements are the metadata.
// It is called NameSuffix because conceptually each metadatum adds a
// new attribute with a name suffix specifying the type of metadata.
// For example, suppose you have an attribute "points" that has an attached
// elemCount. Conceptually you have two attributes "points" and
// "points_elemCount".
enum class NameSuffix
{
    none = 0, // Index NameSuffix::value is the index of the data itself


    // The following metadata is present on USD attributes that connect to others
    connection // The target(prim, attribute) of the connection
};

struct Connection
{
    PathC path;
    TokenC attrName;
};

// AttrNameAndType specifies the name and type of an attribute. When the user
// searches for buckets of prims they use this type to specify which attributes
// the prims must have. Also the user can query the name and type of an
// attribute at a given path, and the output has this type.
//
// This version of the struct contains the type in flatcache format only.
// The original, AttrNameAndType, additionally contains the type in USD format,
// but that version will be deprecated.
struct AttrNameAndType
{
    Type type;
    Token name;
    NameSuffix suffix;

    AttrNameAndType() = default;

    AttrNameAndType(Type type, Token name, NameSuffix suffix = NameSuffix::none)
        : type(type), name(name), suffix(suffix)
    {
    }

    // Note that in the name comparisons below TokenC masks off USD's lifetime bit.
    // For example, tokens created from the same string are considered equal even
    // if one was created with finite lifetime and the other infinite lifetime.

    bool operator<(const AttrNameAndType& rhs) const
    {
        if (TypeC(type) < TypeC(rhs.type))
            return true;
        if (TypeC(rhs.type) < TypeC(type))
            return false;
        if (TokenC(name) < TokenC(rhs.name))
            return true;
        if (TokenC(rhs.name) < TokenC(name))
            return false;

        return suffix < rhs.suffix;
    }

    bool operator==(const AttrNameAndType& other) const
    {
        return type == other.type && name == other.name && suffix == other.suffix;
    }
};
static_assert(std::is_standard_layout<AttrNameAndType>::value,
              "AttrNameAndType must be standard layout as it is used in C-ABI interfaces");

// NOTE: This type alias provides source level compatibility. Usage of the original AttrNameAndType structure has
// been replaced with what was previously called AttrNameAndType_v2 and the _v2 suffix dropped. This alias allows code
// which still refers to AttrNameAndType_v2 to compile.
using AttrNameAndType_v2 = AttrNameAndType;

}
}

namespace std
{
template <>
struct hash<carb::flatcache::AttrNameAndType>
{
    // Use the same hash_combine as boost
    template <class T>
    static inline void hash_combine(std::size_t& seed, const T& v)
    {
        std::hash<T> hasher;
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }

    std::size_t operator()(const carb::flatcache::AttrNameAndType& key) const
    {
        size_t hash = std::hash<carb::flatcache::Type>{}(key.type);
        hash_combine(hash, std::hash<carb::flatcache::Token>{}(key.name));
        hash_combine(hash, uint32_t(key.suffix));
        return hash;
    }
};
}

