// See the file "COPYING" in the main distribution directory for copyright.

#pragma once

#include <cstring>
#include <string>

#include "zeek/util-types.h"

namespace zeek {

constexpr size_t UID_LEN = 2;

constexpr int UID_POOL_DEFAULT_INTERNAL = 1;
constexpr int UID_POOL_DEFAULT_SCRIPT = 2;
constexpr int UID_POOL_CUSTOM_SCRIPT = 10; // First available custom script level pool.

/**
 * A class for creating/managing UIDs of arbitrary bit-length and converting
 * them to human-readable strings in Base62 format.
 */
class UID {
public:
    /**
     * Default ctor.  The UID is uninitialized.
     */
    UID() : initialized(false) {}

    /**
     * Construct a UID of a given bit-length, optionally from given values.
     * @see UID::Set
     */
    explicit UID(zeek_uint_t bits, const uint64_t* v = nullptr, size_t n = 0, size_t pool = UID_POOL_DEFAULT_INTERNAL) {
        Set(bits, v, n, pool);
    }

    /**
     * Copy constructor.
     */
    UID(const UID& other);

    /**
     * Initialize a UID of a given bit-length, optionally from given values.
     * @param bits The desired length in bits of the UID, up to a max of
     *             UID_LEN * 64.
     * @param v A pointer to an array of values with which to initialize the
     *          UID.  If empty or doesn't contain enough values to satisfy
     *          \a bits, then values are automatically generated using
     *          calculate_unique_id().  If \a bits isn't evenly divisible by
     *          64, then a value is truncated to bit in desired bit-length.
     * @param n number of 64-bit elements in array pointed to by \a v.
     * @param pool the pool of UIDs that is used.
     */
    void Set(zeek_uint_t bits, const uint64_t* v = nullptr, size_t n = 0, size_t pool = UID_POOL_DEFAULT_INTERNAL);

    /**
     * Returns a base62 (characters 0-9, A-Z, a-z) representation of the UID.
     * @param prefix An optional string prefix.
     * @return a base62 string representing the UID.
     */
    std::string Base62(std::string prefix = "") const;

    /**
     * @return false if the UID instance was created via the default ctor
     *         and not yet initialized w/ Set().
     */
    explicit operator bool() const { return initialized; }

    /**
     * Assignment operator.
     */
    UID& operator=(const UID& other);

    /**
     * UID equality operator.
     */
    friend bool operator==(const UID& u1, const UID& u2) { return memcmp(u1.uid, u2.uid, sizeof(u1.uid)) == 0; }

    /**
     * UID inequality operator.
     */
    friend bool operator!=(const UID& u1, const UID& u2) { return ! (u1 == u2); }

private:
    uint64_t uid[UID_LEN];
    bool initialized; // Since technically uid == 0 is a legit UID
};

inline UID::UID(const UID& other) {
    memcpy(uid, other.uid, sizeof(uid));
    initialized = other.initialized;
}

inline UID& UID::operator=(const UID& other) {
    if ( this == &other )
        return *this;

    memmove(uid, other.uid, sizeof(uid));
    initialized = other.initialized;
    return *this;
}

} // namespace zeek
