////////////////////////////////////////////////////////////////////////////////
//
// PRINTBUFFER
//
// Lua has a 'print' statement - in client-server mode, where does the output of
// the 'print' statement go?  We need to be able to handle print-statements on
// the server (and forward them to the client), and we need for predictive
// prints to be handled gracefully.
//
// Class PrintBuffer is a buffer for storing the output of the print statement.
// It is part of the actor tangible.  When a lua thread calls 'print', the print
// goes into stringstream lthread_prints.  When the thread stops or yields, the
// contents of lthread_prints are converted to lines and transferred to the
// PrintBuffer of the actor. From there, it gets difference transmitted, and the
// client can probe it.
//
// Suppose, for example, that the player logs in and invokes a plan that prints
// six lines from a Robert Frost poem.  That plan is executed by both the master
// model and the synchronous model.  When the thread finishes, the PrintBuffer
// in the actor in the master model will contain this:
//
//    * Line 0: Whose woods these are I think I know.  [authoritative]
//    * Line 1: His house is in the village though;    [authoritative]
//    * Line 2: He will not see me stopping here       [authoritative]
//    * Line 3: To watch his woods fill up with snow.  [authoritative]
//    * Line 4: My little horse must think it queer    [authoritative]
//    * Line 5: To stop without a farmhouse near.      [authoritative]
//
// Note that the buffer stores line numbers, which start from zero the moment
// the player logs in.  In the master model, all lines are always authoritative
// because everything in the master model is authoritative.  In the synchronous
// model, the PrintBuffer is likely to contain the same strings, but the lines
// will all be marked as [prediction] instead of [authoritative].
//
// When the server difference transmits, the difference transmission will fix up
// any mistakes in the PrintBuffer in the synchronous model.  In the process, it
// will also fix up any [prediction] changing it to [authoritative].
//
// Periodically, the oldest lines in the buffer will get discarded.  More on
// this later.  When lines get discarded, the line numbers don't change.  For
// example, if we were to discard three lines from the buffer above, this is
// what would remain:
//
//    * Line 3: To watch his woods fill up with snow.  [authoritative]
//    * Line 4: My little horse must think it queer    [authoritative]
//    * Line 5: To stop without a farmhouse near.      [authoritative]
//
// The client will periodically probe the PrintBuffer.  Suppose it sees all six
// lines of the Robert Frost poem.  The next time it probes the buffer, it will
// still see those same six lines.  The client will continue to see those six
// lines until it takes explicit steps.
//
// Here's how we keep the buffer from growing forever.  The client probes the
// PrintBuffer, and sees some authoritative lines.  The client downloads and
// stores those lines locally. Once the lines are stored locally in the client,
// the client injects a command into the command stream: "delete from
// PrintBuffer up to line N" into the command stream.  This will cause the
// engine to discard the lines that the client no longer needs.
//
// Note that when the client injects a "delete from PrintBuffer" into the
// command stream, that's an invoke-command that has to follow the same process
// as any other client invoke command.  It can take time for it to get
// processed. Therefore, the client must be prepared that it might see some
// redundant lines for a little while.
//
// MEMORY EFFICIENCY.
//
// Every tangible will contain a printbuffer.  To avoid memory waste, the
// implementation of PrintBuffer stores everything inside a dynamically
// allocated "core" struct.  All printbuffers that contain nothing share a
// common empty core.  That way, the empty printbuffers that will exist in 99% of
// all tangibles will take up very little space.
//
////////////////////////////////////////////////////////////////////////////////


#pragma once

#include "wrap-deque.hpp"
#include "wrap-string.hpp"
#include <ostream>
#include "streambuffer.hpp"
#include "util.hpp"
#include "invocation.hpp"
#include "debugcollector.hpp"

#include <memory>

struct PrintBufferCore;

class PrintBuffer : public eng::nevernew {
private:
    // A pointer to a PrintBufferCore that stores the data.
    //
    // This pointer can be in two states: it can point
    // to a heap-allocated PrintBufferCore which is owned,
    // or it can point to a global variable "empty_core"
    // which is always empty.  
    //
    // Any attempt to write into the printbuffer will
    // first check if this points to empty_core.  If so,
    // a PrintBufferCore will be allocated and stored
    // here, only then will the write happen.
    //
    // Clearing the printbuffer will cause any owned
    // PrintBufferCore to be freed, and the pointer to
    // empty_core will be restored.
    //
    // The advantage of this design is that the empty state
    // is stored very efficiently, but it's not a null
    // pointer: it's still a valid core.  You never have
    // to check for nullptr here.
    //
    PrintBufferCore *core_;

public:
    PrintBuffer();
    ~PrintBuffer();

    // Get the current first line.
    int first_line() const;
    
    // Return the line number beyond the end of the buffer.
    int last_line() const;
    
    // Get the current first unchecked line.
    // "Unchecked" lines are lines in a non-authoritative model
    // that haven't been verified through difference transmission.
    // Guaranteed to be between first_line and last_line inclusive.
    int first_unchecked() const;
    
    // Return true if the printbuffer is in the initial state.
    // Note: if you clear the buffer, it's back in the initial state.
    bool never_printed() const;

    // Get the specified line number.
    const eng::string &nth(int n) const;
    
    // Print the entire contents of the buffer to a string (for unit testing).
    eng::string debug_string() const;

    // Clear the buffer
    void clear();

    // Add a string.  If the string doesn't end in a newline, a newline
    // is added.  The string is broken into lines, and the lines are added
    // to the PrintBuffer.
    void add_string(const eng::string &s, bool auth);

    // Discard lines up to but not including line N.
    void discard_upto(int n);

    // Serialization and deserialization
    void serialize(StreamBuffer *sb) const;
    void deserialize(StreamBuffer *sb);

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


class PrintChanneler : public eng::nevernew {
private:
    int64_t line_;
public:
    PrintChanneler() { line_ = 0; }

    // Reset the print channeler.
    void reset() { line_ = 0; }
    
    // Check if there's anything to channel.
    bool have_prints(const PrintBuffer *pb) const;

    // Copy any new lines from the printbuffer to the stream buffer.
    // Update the current line number.  Return true if any data
    // was channeled.
    bool channel(const PrintBuffer *pb, StreamBuffer *sb);

    // Generate an invocation that removes unnecessary lines from the
    // printbuffer.  You should only do this if you just channeled
    // some new output.
    Invocation invocation(int64_t actor_id);
};

