//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#ifndef _NETOBJECT_H_
#define _NETOBJECT_H_

#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif


//-----------------------------------------------------------------------------
class NetConnection;
class NetObject;

//-----------------------------------------------------------------------------

struct CameraScopeQuery
{
   NetObject *camera;       ///< Pointer to the viewing object.
   Point3F pos;             ///< Position in world space
   Point3F orientation;     ///< Viewing vector in world space
   F32 fov;                 ///< Viewing angle/2
   F32 sinFov;              ///< sin(fov/2);
   F32 cosFov;              ///< cos(fov/2);
   F32 visibleDistance;     ///< Visible distance.
};

struct GhostInfo;


//-----------------------------------------------------------------------------
/// Superclass for ghostable networked objects.
///
/// @section NetObject_intro Introduction To NetObject And Ghosting
///
/// One of the most powerful aspects of Torque's networking code is its support
/// for ghosting and prioritized, most-recent-state network updates. The way
/// this works is a bit complex, but it is immensely efficient. Let's run
/// through the steps that the server goes through for each client in this part
/// of Torque's networking:
///      - First, the server determines what objects are in-scope for the client.
///        This is done by calling onCameraScopeQuery() on the object which is
///        considered the "scope" object. This is usually the player object, but
///        it can be something else. (For instance, the current vehicle, or a
///        object we're remote controlling.)
///      - Second, it ghosts them to the client; this is implemented in netGhost.cc.
///      - Finally, it sends updates as needed, by checking the dirty list and packing
///        updates.
///
/// There several significant advantages to using this networking system:
///      - Efficient network usage, since we only send data that has changed. In addition,
///        since we only care about most-recent data, if a packet is dropped, we don't waste
///        effort trying to deliver stale data.
///      - Cheating protection; since we don't deliver information about game objects which
///        aren't in scope, we dramatically reduce the ability of clients to hack the game and
///        gain a meaningful advantage. (For instance, they can't find out about things behind
///        them, since objects behind them don't fall in scope.) In addition, since ghost IDs are
///        assigned per-client, it's difficult for any sort of co-ordination between cheaters to
///        occur.
///
/// NetConnection contains the Ghost Manager implementation, which deals with transferring data to
/// the appropriate clients and keeping state in synch.
///
/// @section NetObject_Implementation An Example Implementation
///
/// The basis of the ghost implementation in Torque is NetObject. It tracks the dirty flags for the
/// various states that the object trackers, and does some other book-keeping to allow more efficient
/// operation of the networking layer.
///
/// Using a NetObject is very simple; let's go through a simple example implementation:
///
/// @code
/// class SimpleNetObject : public NetObject
/// {
/// public:
///   typedef NetObject Parent;
///   DECLARE_CONOBJECT(SimpleNetObject);
/// @endcode
///
/// Above is the standard boilerplate code for a Torque class. You can find out more about this in SimObject.
///
/// @code
///    char message1[256];
///    char message2[256];
///    enum States {
///       Message1Mask = BIT(0),
///       Message2Mask = BIT(1),
///    };
/// @endcode
///
/// For our example, we're having two "states" that we keep track of, message1 and message2. In a real
/// object, we might map our states to health and position, or some other set of fields. You have 32
/// bits to work with, so it's possible to be very specific when defining states. In general, you
/// should try to use as few states as possible (you never know when you'll need to expand your object's
/// functionality!), and in fact, most of your fields will end up changing all at once, so it's not worth
/// it to be too fine-grained. (As an example, position and velocity on Player are controlled by the same
/// bit, as one rarely changes without the other changing, too.)
///
/// @code
///    SimpleNetObject()
///    {
///       // in order for an object to be considered by the network system,
///       // the Ghostable net flag must be set.
///       // the ScopeAlways flag indicates that the object is always scoped
///       // on all active connections.
///       mNetFlags.set(ScopeAlways | Ghostable);
///       dStrcpy(message1, "Hello World 1!");
///       dStrcpy(message2, "Hello World 2!");
///    }
/// @endcode
///
/// Here is the constructor. Here, you see that we initialize our net flags to show that
/// we should always be scoped, and that we're to be taken into consideration for ghosting. We
/// also provide some initial values for the message fields.
///
/// @code
///    U32 packUpdate(NetConnection *, U32 mask, BitStream *stream)
///    {
///       // check which states need to be updated, and update them
///       if(stream->writeFlag(mask & Message1Mask))
///          stream->writeString(message1);
///       if(stream->writeFlag(mask & Message2Mask))
///          stream->writeString(message2);
///
///       // the return value from packUpdate can set which states still
///       // need to be updated for this object.
///       return 0;
///    }
/// @endcode
///
/// Here's half of the meat of the networking code, the packUpdate() function. (The other half, unpackUpdate(),
/// we'll get to in a second.) The comments in the code pretty much explain everything, however, notice that the
/// code follows a pattern of if(writeFlag(mask & StateMask)) { ... write data ... }. The packUpdate()/unpackUpdate()
/// functions are responsible for reading and writing the dirty bits to the bitstream by themselves.
///
/// @code
///    void unpackUpdate(NetConnection *, BitStream *stream)
///    {
///       // the unpackUpdate function must be symmetrical to packUpdate
///       if(stream->readFlag())
///       {
///          stream->readString(message1);
///          Con::printf("Got message1: %s", message1);
///       }
///       if(stream->readFlag())
///       {
///          stream->readString(message2);
///          Con::printf("Got message2: %s", message2);
///       }
///    }
/// @endcode
///
/// The other half of the networking code in any NetObject, unpackUpdate(). In our simple example, all that
/// the code does is print the new messages to the console; however, in a more advanced object, you might
/// trigger animations, update complex object properties, or even spawn new objects, based on what packet
/// data you unpack.
///
/// @code
///    void setMessage1(const char *msg)
///    {
///       setMaskBits(Message1Mask);
///       dStrcpy(message1, msg);
///    }
///    void setMessage2(const char *msg)
///    {
///       setMaskBits(Message2Mask);
///       dStrcpy(message2, msg);
///    }
/// @endcode
///
/// Here are the accessors for the two properties. It is good to encapsulate your state
/// variables, so that you don't have to remember to make a call to setMaskBits every time you change
/// anything; the accessors can do it for you. In a more complex object, you might need to set
/// multiple mask bits when you change something; this can be done using the | operator, for instance,
/// setMaskBits( Message1Mask | Message2Mask ); if you changed both messages.
///
/// @code
/// IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject);
///
/// ConsoleMethod(SimpleNetObject, setMessage1, void, 3, 3, "(string msg) Set message 1.")
/// {
///    object->setMessage1(argv[2]);
/// }
///
/// ConsoleMethod(SimpleNetObject, setMessage2, void, 3, 3, "(string msg) Set message 2.")
/// {
///    object->setMessage2(argv[2]);
/// }
/// @endcode
///
/// Finally, we use the NetObject implementation macro, IMPLEMENT_CO_NETOBJECT_V1(), to implement our
/// NetObject. It is important that we use this, as it makes Torque perform certain initialization tasks
/// that allow us to send the object over the network. IMPLEMENT_CONOBJECT() doesn't perform these tasks, see
/// the documentation on AbstractClassRep for more details.
///
/// @nosubgrouping
class NetObject: public SimObject
{
   // The Ghost Manager needs read/write access
   friend class  NetConnection;
   friend struct GhostInfo;
   friend class  ProcessList;

   // Not the best way to do this, but the event needs access to mNetFlags
   friend class GhostAlwaysObjectEvent;

private:
   typedef SimObject Parent;

   /// Mask indicating which states are dirty and need to be retransmitted on this
   /// object.
   U32 mDirtyMaskBits;

   /// @name Dirty List
   ///
   /// Whenever a NetObject becomes "dirty", we add it to the dirty list.
   /// We also remove ourselves on the destructor.
   ///
   /// This is done so that when we want to send updates (in NetConnection),
   /// it's very fast to find the objects that need to be updated.
   /// @{

   /// Static pointer to the head of the dirty NetObject list.
   static NetObject *mDirtyList;

   /// Next item in the dirty list...
   NetObject *mPrevDirtyList;

   /// Previous item in the dirty list...
   NetObject *mNextDirtyList;

   /// @}
protected:

   /// Pointer to the server object; used only when we are doing "short-circuited" networking.
   ///
   /// When we are running with client and server on the same system (which can happen be either
   /// when we are doing a single player game, or if we're hosting a multiplayer game and having
   /// someone playing on the same instance), we can do some short circuited code to enhance
   /// performance.
   ///
   /// This variable is used to make it simpler; if we are running in short-circuited mode, it's set
   /// to the object on the server that this NetObject is ghosting.
   ///
   /// @note "Premature optimization is the root of all evil" - Donald Knuth. The current codebase
   ///       uses this feature in three small places, mostly for non-speed-related purposes.
   SimObjectPtr<NetObject> mServerObject;

   enum NetFlags
   {
      IsGhost           =  BIT(1),   ///< This is a ghost.
      ScopeAlways       =  BIT(6),  ///< Object always ghosts to clients.
      ScopeLocal        =  BIT(7),  ///< Ghost only to local client.
      Ghostable         =  BIT(8),  ///< Set if this object CAN ghost.

      MaxNetFlagBit     =  15
   };

   BitSet32 mNetFlags;              ///< Flag values from NetFlags
   U32 mNetIndex;                   ///< The index of this ghost in the GhostManager on the server.

   GhostInfo *mFirstObjectRef;      ///< Head of a linked list storing GhostInfos referencing this NetObject.

public:
   NetObject();
   ~NetObject();

   /// @name Miscellaneous
   /// @{
   DECLARE_CONOBJECT(NetObject);
   static void initPersistFields();
   bool onAdd();
   void onRemove();
   /// @}

   static void collapseDirtyList();

   /// Used to mark a bit as dirty; ie, that its corresponding set of fields need to be transmitted next update.
   ///
   /// @param   orMask   Bit(s) to set
   void setMaskBits(U32 orMask);

   /// Clear the specified bits from the dirty mask.
   ///
   /// @param   orMask   Bits to clear
   void clearMaskBits(U32 orMask);

   ///  Scope the object to all connections.
   ///
   ///  The object is marked as ScopeAlways and is immediately ghosted to
   ///  all active connections.  This function has no effect if the object
   ///  is not marked as Ghostable.
   void setScopeAlways();

   /// Stop scoping the object to all connections.
   ///
   /// The object's ScopeAlways flag is cleared and the object is removed from
   /// all current active connections.
   void clearScopeAlways();

   /// This returns a value which is used to prioritize which objects need to be updated.
   ///
   /// In NetObject, our returned priority is 0.1 * updateSkips, so that less recently
   /// updated objects are more likely to be updated.
   ///
   /// In subclasses, this can be adjusted. For instance, ShapeBase provides priority
   /// based on proximity to the camera.
   ///
   /// @param  focusObject    Information from a previous call to onCameraScopeQuery.
   /// @param  updateMask     Current update mask.
   /// @param  updateSkips    Number of ticks we haven't been updated for.
   /// @returns A floating point value indicating priority. These are typically < 5.0.
   virtual F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips);

   /// Instructs this object to pack its state for transfer over the network.
   ///
   /// @param   conn    Net connection being used
   /// @param   mask    Mask indicating fields to transmit.
   /// @param   stream  Bitstream to pack data to
   ///
   /// @returns Any bits which were not dealt with. The value is stored by the networking
   ///          system. Don't set bits you weren't passed.
   virtual U32  packUpdate(NetConnection * conn, U32 mask, BitStream *stream);

   /// Instructs this object to read state data previously packed with packUpdate.
   ///
   /// @param   conn    Net connection being used
   /// @param   stream  stream to read from
   virtual void unpackUpdate(NetConnection * conn, BitStream *stream);

   /// Queries the object about information used to determine scope.
   ///
   /// Something that is 'in scope' is somehow interesting to the client.
   ///
   /// If we are a NetConnection's scope object, it calls this method to determine
   /// how things should be scoped; basically, we tell it our field of view with camInfo,
   /// and have the opportunity to manually mark items as "in scope" as we see fit.
   ///
   /// By default, we just mark all ghostable objects as in scope.
   ///
   /// @param   cr         Net connection requesting scope information.
   /// @param   camInfo    Information about what this object can see.
   virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);

   /// Get the ghost index of this object.
   U32 getNetIndex() { return mNetIndex; }

   bool isServerObject() const;  ///< Is this a server object?
   bool isClientObject() const;  ///< Is this a client object?

   bool isGhost() const;         ///< Is this is a ghost?
   bool isScopeLocal() const;    ///< Should this object only be visible to the client which created it?
   bool isScopeable() const;     ///< Is this object subject to scoping?
   bool isGhostable() const;     ///< Is this object ghostable?
   bool isGhostAlways() const;   ///< Should this object always be ghosted?
};

//-----------------------------------------------------------------------------

inline bool NetObject::isGhost() const
{
   return mNetFlags.test(IsGhost);
}

inline bool NetObject::isClientObject() const
{
   return mNetFlags.test(IsGhost);
}

inline bool NetObject::isServerObject() const
{
   return !mNetFlags.test(IsGhost);
}

inline bool NetObject::isScopeLocal() const
{
   return mNetFlags.test(ScopeLocal);
}

inline bool NetObject::isScopeable() const
{
   return mNetFlags.test(Ghostable) && !mNetFlags.test(ScopeAlways);
}

inline bool NetObject::isGhostable() const
{
   return mNetFlags.test(Ghostable);
}

inline bool NetObject::isGhostAlways() const
{
   AssertFatal(mNetFlags.test(Ghostable) || mNetFlags.test(ScopeAlways) == false,
               "That's strange, a ScopeAlways non-ghostable object?  Something wrong here");
   return mNetFlags.test(Ghostable) && mNetFlags.test(ScopeAlways);
}

#endif
