///////////////////////////////////////////////////////////////////
//
// THE ID ALLOCATOR
//
// This ID allocator's goal is to allocate IDs in such a way that the
// synchronous model gets the same IDs as the master model.
//
// DESIGN PRINCIPLES
//
// There are two classes defined here: IdGlobalPool, and IdPlayerPool.
//
// Every logged-in player maintains an IdPlayerPool.  That's basically a fifo
// queue of ID batches.  An ID batch is a contiguous range of IDs, containing
// between 128 and 256 contiguous IDs.  The IdPlayerPool is difference
// transmitted, to ensure that the synchronous model has the same batches as the
// master model.
//
// When a player creates a thread, that thread gets an ID batch from the
// IdPlayerPool.  To make this possible, the Lua runtime has been modified so
// that a thread can contain an ID batch.  When that thread allocates IDs, it
// uses the batch it was allocated.  In the unlikely event that a thread's batch
// is used up, the thread falls back to using the IdGlobalPool. Such fallback
// IDs are not likely to be predicted correctly.
//
// When a player creates a thread, he uses up one batch from his IdPlayerPool.
// In the master model, the player pool is 'refilled' by asking the IdGlobalPool
// to create a new batch.  In the synchronous model, the IdPlayerPool is not
// directly refilled, but the difference transmitter effectively refills it.  It
// is imperative that this difference transmission happen before the player's
// asynchronous model runs out of batches, otherwise we'll get prediction
// failures.
//
// It is common that a thread will only use 1 or 2 IDs.  If a thread exits
// without using up most of its IDs, then the batch it contains is still a
// pretty usable batch.  The batch gets returned to the IdGlobalPool, which will
// eventually use that salvaged batch to satisfy an IdPlayerPool refill request.
//
// THE NUMERIC RANGES
//
// Any 53-bit number can be losslessly stored in a lua_Number.  In other
// words, the largest integer that can be stored losslessly is:
//
//  * 0x001FFFFFFFFFFFFF
//
// As it turns out, that's just barely larger than 9 quadrillion.  We are
// going to use IDs that are between 0 and 9 quadrillion.  We divide the
// range of possible IDs into several subsections:
//
//  0 quadrillion + : manually created IDs.
//  1 quadrillion + : used by master model's IdGlobalPool to create batches.
//  5 quadrillion + : used by master model's IdGlobalPool to create individual IDs.
//  8 quadrillion + : used by sync model's IdGlobalPool to create individual IDs.
//
// If you exhaust the Master Model's invididual pool at a rate of 10,000,000 IDs
// per second, the individual pool will last for 12 years.
//
// BATCH REPRESENTATION
//
// A batch is represented as a 64-bit integer.  The batch contains all IDs
// starting with that integer, and incrementing, until the the two lowest digits
// are 0x00. 
//
// For example, consider the batch ID 0x11111111111111FC.  That batch includes
// these IDs:
//
//  * 0x11111111111111FC
//  * 0x11111111111111FD
//  * 0x11111111111111FE
//  * 0x11111111111111FF
//
// But it does not include 0x1111111111111200.
//
// As a special case, the number 0 is used to indicate "invalid batch".
//
// SEQUENCE NUMBERS
//
// If you allocate IDs from a player pool rapidly, you will exhaust the fifo in
// the player pool.  As a result, ID allocation will fall back to the global
// pool, which will cause prediction failures.
//
// Depending on your requirements, you may be able to use sequence numbers
// instead of IDs.  The player pool contains a monotonically increasing counter
// for generating sequence numbers.  These can be allocated extremely fast
// and they'll never run out.  These are highly predictable as long as the
// only person fetching sequence numbers is the player himself.  The downside
// is that sequence numbers are not globally unique - they're only locally
// unique for a single player.  That may be enough for your requirements.
// 
// Currently, sequence numbers are used by the random number generator.
//
///////////////////////////////////////////////////////////////////

#pragma once

#include "wrap-map.hpp"
#include "wrap-sstream.hpp"
#include "wrap-deque.hpp"
#include "luastack.hpp"
#include "streambuffer.hpp"
#include "debugcollector.hpp"

#include <cstdint>
#include <ostream>

class IdGlobalPool : public eng::nevernew {
public:
    // Construct and destroy global pools.  Note that after constructing
    // a global pool, it is generally also necessary to initialize it
    // for Master or Synchronous operation using init_master or init_synch.
    // Any attempt to use a pool that hasn't been init_master or init_synch
    // will fail.
    IdGlobalPool();
    ~IdGlobalPool();

    // Initialize the pool for use in a master model.
    void init_master();

    // Initialize the pool for use in a synchronous model.
    void init_synch();

    // Create a single unique ID.  Ids allocated this way are
    // unlikely to be predicted correctly.
    int64_t get_one();

    // Obtain a batch of IDs from the global pool.  In a master
    // model, the batch is guaranteed to contain at least 128 IDs.
    // In a synchronous model, the batch is always zero (invalid).
    int64_t get_batch();

    // Try to return the specified batch to the global pool.
    // The salvage operation quietly does nothing if the batch is
    // zero, or the batch contains fewer than 128 IDs.
    void salvage(int64_t batch);

    // Get a global sequence number.  These are not the same as IDs.
    // Sequence numbers are unlikely to be predicted correctly.
    int64_t get_seqno() { return next_seqno_++; }

    // Serialize to or deserialize from a streambuffer.
    void serialize(StreamBuffer *sb) const;
    void deserialize(StreamBuffer *sb);

    // Generate a debug string.
    eng::string debug_string() const;

private:
    eng::vector<int64_t> salvaged_;
    int64_t next_batch_;
    int64_t next_id_;
    int64_t next_seqno_;
    friend int unittests_idalloc(lua_State *L);
};

class IdPlayerPool : public eng::nevernew {
public:
    // Construct a player pool.
    //
    // The fifo is initially in the disabled state.  In the disabled state, the
    // fifo is not kept filled.  You can still use the allocator when the fifo is
    // disabled - doing so just fetches a batch from the global pool.
    //
    IdPlayerPool(IdGlobalPool *g);
    ~IdPlayerPool();

    // Set the fifo capacity.  Max=255.
    void set_fifo_capacity(int n);
    int get_fifo_capacity() const { return fifo_capacity_; }

    // Return all batches from the fifo to the global pool.
    void unqueue();

    // Refill the fifo of batches from the global pool.
    void refill();

    // Get a single ID from the fifo.  Also refills the fifo.
    int64_t get_one();

    // Get a player sequence number.  This is not the same as an ID: player
    // sequence numbers are unique per player, but not globally.
    int64_t get_seqno() { return next_seqno_++; }

    // Return the size of the queue.
    int size() { return ranges_.size(); }

    // Return true if the two pools are identical.
    bool exactly_equal(const IdPlayerPool &other) const;
    
    // Check that the pool is valid.
    bool valid() const;

    // unit testing functions.
    //
    // These let you manipulate the deque explicitly.  But that's
    // only useful for unit testing.
    //
    void test_push_back(int64_t range);
    void test_pop_front();
    void test_clear_ranges();

    // Serialize to or deserialize from a streambuffer.
    // Caution: the pointer to the global pool is not serialized or deserialized.
    void serialize(StreamBuffer *sb) const;
    void deserialize(StreamBuffer *sb);

    // Difference transmission
    //
    void diff(const IdPlayerPool &auth, StreamBuffer *sb) const;
    void patch(StreamBuffer *sb, DebugCollector *dbc);

    // Debug string.
    eng::string debug_string() const;

private:
    IdGlobalPool *global_;
    int fifo_capacity_;
    int64_t next_seqno_;
    eng::deque<int64_t> ranges_;
    friend int lfn_unittests_idalloc(lua_State *L);
};


