////////////////////////////////////////////////////////////////////////////////
//
// enginewrapper.hpp
//
// This header file contains driver's interface to class DrivenEngine.
// This is meant to be used across a DLL boundary.  Since the DLL may have
// been compiled by a different compiler than the driver, we use only simple
// POD types and we only use C calling conventions.
//
// When calling a wrapper function, you must always pass in the wrapper as
// the first parameter.
//
////////////////////////////////////////////////////////////////////////////////

#pragma once

#include <cstdint>
#include <cstddef>

#define DRV_MAX_CHAN 256
#define DRV_MAX_LISTEN_PORTS 256
#define DRV_ERRMSG_SIZE 8192
#define DRV_SHORTSTRING_SIZE 65536

enum class AccessKind {
    INVALID,
    INVOKE_LUA_CALL,
    INVOKE_LUA_EXPR,
    INVOKE_FLUSH_PRINTS,
    INVOKE_TICK,
    INVOKE_LUA_SOURCE,
    PROBE_LUA_CALL,
    CONNECT_TO_SERVER,
    VALIDATE_LUA_EXPR,
    CHANNEL_PRINTS,
    SLASH_COMMAND,
};

class DrivenEngine;
class PlayLogfile;
class ReplayLogfile;
class World;

struct EngineWrapper {
    char error[DRV_ERRMSG_SIZE];
    char databuffer[DRV_SHORTSTRING_SIZE];
    DrivenEngine *engine;
    World *world;
    PlayLogfile *wlog;
    ReplayLogfile *rlog;

    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // CONSTRUCTION
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    
    // Of course, there's no constructor, since this is a C struct.
    // To initialize it, you use 'dlsym' or 'GetProcAddress' to get the
    // address of the function 'init_engine_wrapper'.  Then, you call
    // the function init_engine_wrapper(&wrapper).  It is OK to call
    // init_engine_wrapper more than once.
    
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // GETTERS
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////

    // Get a list of all the listening ports.  The driver is expected
    // to fetch this set shortly after the event_init callback is invoked.
    //
    void (*get_listen_ports)(EngineWrapper *w, uint32_t *nports, const uint32_t **ports);

    // Get a list of all recently-opened channels that were created using
    // new_outgoing_channel.  The driver should initiate outgoing
    // connections for these channels.
    //
    void (*get_new_outgoing)(EngineWrapper *w, uint32_t *nchanids, const uint32_t **chanids);

    // Get a string_view of the target of a channel. A target is a string like
    // "cert:whatever.com:80" or "nocert:whatever.com:80".
    // The first word indicate whether or not a valid SSL certificate
    // is required.  The second word is the hostname.  The third word is
    // the port number.  The char string returned here is valid until
    // the channel is closed.
    //
    const char *(*get_target)(EngineWrapper *w, uint32_t chid);
        
    // Return true if the user has released all references to this channel.
    // In this case, the driver should initiate shutdown of the channel,
    // and the driver should eventually call notify_close.
    //
    bool (*get_channel_released)(EngineWrapper *w, uint32_t chid);

    // Get a pointer to the bytes in the outgoing buffer.  The char pointer
    // returned here is naturally only valid until the buffer is changed.
    // This function is used for all channels, including sockets and stdio.
    //
    void (*get_outgoing)(EngineWrapper *w, uint32_t chid, uint32_t *len, const char **data);

    // Return true if the outgoing buffer is empty.
    //
    bool (*get_outgoing_empty)(EngineWrapper *w, uint32_t chid);

    // Get the clock.
    //
    // Get the current time.  This is equal to the last value passed
    // in by invoke_event_update.
    //
    double (*get_clock)(EngineWrapper *w);

    // Check the 'have prints' flag.  If true, then the engine
    // wants the driver to use CHANNEL_PRINTS to obtain prints.
    //
    bool (*get_have_prints)(EngineWrapper *w);

    // Check the 'rescan_lua_source' flag.  If this flag is set, it means
    // that the engine wants the driver to rescan the lua source code.
    // When the driver sees this flag, it should rescan the source and call
    // set_lua_source_pack.
    //
    bool (*get_rescan_lua_source)(EngineWrapper *w);

    // If true, the engine is done.  Stop the driver.
    //
    bool (*get_stop_driver)(EngineWrapper *w);

    // Get the actor ID.  May return zero if the server is down.
    //
    int64_t (*get_actor_id)(EngineWrapper *w);

    // Do a scan to find tangibles near the specified player.
    //
    // Returns a count and a pointer to an array of tangible IDs.  The returned
    // pointer is valid until the next call to get_tangibles_near.
    //
    void (*get_tangibles_near)(EngineWrapper *w, uint64_t tanid, uint32_t *count, int64_t **ids);

    // Get the animation queues for the specified tangibles.
    //
    // You must supply an array of tangible IDs.  For each tangible, returns the
    // animation queue as a serialized string.  The serialized format is
    // documented in header file animqueue.hpp.
    //
    // You must also supply a buffer for the string lengths, and a buffer for
    // the string pointers.  Both buffers must have space for number of
    // tangibles specified in the call.
    //
    // The returned character pointers remain valid until the next call to
    // get_animation_queues.
    //
    void (*get_animation_queues)(EngineWrapper *w, uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings);
    
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // MUTATORS USED ONLY IN PLAY MODE
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    
    // Create the driven engine.  You must specify the type of engine you
    // want (typically 'lpxserver' or 'lpxclient'), and a filename for the
    // logfile.
    //
    void (*play_initialize)(EngineWrapper *w, const char *engtype, const char *logfn);

    // Clear the list of recently-opened channels.  You are meant to fetch
    // new outgoing channels using get_new_outgoing, then you call
    // clear_new_outgoing after you've opened those channels.
    //
    void (*play_clear_new_outgoing)(EngineWrapper *w);
        
    // Notifies the channel that some bytes were transmitted.  This causes those
    // bytes to be removed from the outgoing buffer.  This function is used for
    // all channels, including sockets and stdio.
    //
    void (*play_sent_outgoing)(EngineWrapper *w, uint32_t chid, uint32_t nbytes);
        
    // Notifies the channel that some bytes were received.  This causes those
    // bytes to be appended to the incoming buffer.  This function is used for
    // all channels, including sockets and stdio.
    //
    void (*play_recv_incoming)(EngineWrapper *w, uint32_t chid, uint32_t len, const char *data);

    // Notify the channel that the connection was closed.  This includes all
    // sorts of closes, including friendly termination, all the way to network
    // failure.  Closing the channel doesn't delete it.  The engine is
    // responsible for noticing that the channel closed and the engine must
    // delete it.  Closing a channel prevents it from showing up in
    // 'list_channels'.  
    //
    void (*play_notify_close)(EngineWrapper *w, uint32_t chid, uint32_t len, const char *data);
    
    // Notify the DrivenEngine that somebody connected to an incoming port.
    // This will cause the DrivenEngine to allocate a new channel and put the
    // new channel into the incoming channels queue.  Returns the new channel
    // ID.  The new incoming channel appears in the 'list_channels' list,
    // even before the engine pops the channel from the incoming channels queue.
    //
    uint32_t (*play_notify_accept)(EngineWrapper *w, uint32_t port);
        
    // Invoke the update event.
    //
    // The clock value must absolutely be monotonically increasing,
    // and it should roughly be equal to the number of seconds since
    // the program started.
    //
    void (*play_update)(EngineWrapper *w, double clock);

    // Send an invoke.
    //
    // This is the main pathway for blueprints, or the unreal C++ code,
    // to change the state of the world.
    //
    void (*play_access)(EngineWrapper *w, AccessKind kind, int64_t place, uint32_t datapklen, const char *datapk, uint32_t *retpklen, const char **retpk);

    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // MUTATORS USED ONLY IN REPLAY MODE
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    
    // Begin a replay.
    //
    // Opens the logfile and prepares to replay the log.
    // If an error occurs, the error buffer contains a message,
    // and the done flag is set to true.
    //
    void (*replay_initialize)(EngineWrapper *w, const char *logfn);
        
    // Execute a single step from the replay log.
    //
    // Calling this when 'done' is true is a no-op.
    //
    void (*replay_step)(EngineWrapper *w);

    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // CALLBACKS USED ONLY IN REPLAY MODE
    //
    // The driver can store function pointers here.  If it does so, these
    // functions will get called during replay_step operations. 
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////

    void (*replay_cb_sent_outgoing)(void *vp, int chid, int nbytes, const char *data);

    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // VOID POINTER USED IN REPLAY CALLBACKS
    //
    // The driver can store a void pointer here.  This void pointer will get passed
    // to all the replay callbacks. 
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////

    void *replay_cb_vp;

    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
    //
    // FUNCTIONS THAT CAN BE USED AT ANY TIME
    //
    //////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////

    // Hook dprintf
    //
    // The engine provides a function 'util::dprintf' to print debugging
    // messages. It does not use stderr or std::cerr, because in windows, those
    // go to the bit-bucket.
    //
    // This routine hooks dprintf to change where the output goes. Ideally,
    // the intent is to send the output somewhere easily accessible, like the
    // visual studio debug output log, or the unreal editor output log, or
    // something like that.  Or, better yet, to all those places at once.
    //
    // The hook function will get passed one line of output at a time. The line
    // will only contain printable characters.  The line will not contain a
    // newline - the newline is implied.
    //
    void (*hook_dprint)(void (*func)(const char *oneline, size_t size));
    
    // Release: delete the engine object and close log files.
    //
    // Note that the wrapper must have already been initialized using
    // init_engine_wrapper.  Otherwise, the 'release' function pointer would not
    // be initialized.  If writing a logfile, this stores a 'clean exit' marker
    // in the logfile, indicating that the engine exited cleanly, as opposed to
    // crashing.
    //
    // If the wrapper is already in its clear state, this is a no-op.
    //
    void (*release)(EngineWrapper *w);
};
