////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// netinput.h
// Project: RSPiX
//
//	History:
//		09/02/97 MJR	Started.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef NETINPUT_H
#define NETINPUT_H

#include "input.h"


////////////////////////////////////////////////////////////////////////////////
//
// CNetInput handle the buffering of inputs received from other players.
//
// The Net::SEQ values are unsigned, and last I checked were 16-bit values,
// but this same logic would work for 32-bit, too.
//
// The sequence values will increment steadly until they wrap-around.  We assume
// that the amount of time that it will take for them to wrap-around is greater
// than the amount of time a network packet can live for and still manage to get
// delivered.  With a 16-bit value at 30 frames per second, this allow for over
// 30 minutes -- I doubt a network packet can survive that long.
//
// This class impliments a "sliding window" of input values.  The window must
// be at least 3 * Net::MaxAheadSeq in size.  (A detailed explanation can be
// found in where that value is defined).
//
// Assuming Net::SEQ ranges from 0 to 65535, the window looks like this:
//
//       0-------------[ooonnnnnn]-----------------------------65535
//                         ^
//                         F
//
// The dashes represent values we don't care about -- any values that are
// NOT within the window are ignored.
//
// The "F" represents the current frame number, which determines the overall
// position of the window.  This is the LOCAL PLAYER'S frame number, not the
// frame number of the player whose data we are dealing with!
//
// As the frame number increases, the window moves to the right, eventually
// wrapping around and starting over at the left side.
//
// The window contains 3 sets of Net::MaxAheadSeq values.  The first set is
// to the left of the current frame number, and is represented by o's.  These
// are "old" values we must keep around in case this player drops from the game
// and we are the only player that received (and used!) his input values.  In
// that case, we will be asked to supply these values to the other players.
//
// The second two sets are represented by n's, and are new values we might
// have received from this player, but that we haven't yet used.
//
// Inputs may arrive out of order, which means we might get a few values,
// then get a few more values further along in the window but leaving a "hole"
// of unknown values in between.  We assume that we'll eventually get those
// values, too, but in the meantime, we can't move our frame number past that
// hole.
//
// In order to properly detect which values we did and did not get, we start
// out with the entire window filled with invalid values, and whenever we move
// the window, we mark any "newly uncovered" values as invalid, too.  As we
// receive inputs, we put them in the appropriate spots in the array,
// overwriting the invalid values in the process.  Whenever an attempt is made
// to move the frame forward, we check if the value for that frame is valid.
// If so, we can move forward.  If not, it means we haven't gotten that value
// yet, so we can't move forward yet.
//
//
// All that cool theory aside, the array is actually implimented in a somewhat
// different manner.
//
// By making it a power of 2 in size (making sure it's at least as
// large as 3 * Net::MaxAheadSeq), everything gets harder to think about, but
// very efficient.  Normally, in order to impliment a sliding window buffer,
// you need to slide around the values contained in the buffer as it moves
// forward.  By making it a power of 2 in size, we can use simple bit-masks
// to get all the indices into to the buffer to automatically wrap-around as
// needed, and suddenly we don't need to move the values anymore -- instead we
// are changing how we look at the buffer.  We are merely moving what we think
// of as the start and end of the buffer around, rather than moving everything
// within the buffer.  Kind of hard to explain, but it DOES work!
//
// Let's assume Net::SEQ is only 4-bits, so it ranges from 0 to 15.  Now let's
// say Net::MaxAheadSeq is 1, so our buffer needs to 3 times that, but instead
// we go with 4 entries, which is the next power-of-2 after 3.  The diagram
// below shows how the various input sequences will map into our buffer.  In
// order to arrive at the index into our buffer, we simply take the sequence
// number and mark out all the bits but the bottom 2, which yields a number
// between 0 and 3, and happens to be the correct index into our buffer.  The
// diagram shows how our buffer "slides along" without moving the actual
// contents -- instead, it's all just a matter of how you think of it.
//
//       0 1 2 3 4 5 6 7 8 9 A B C D E F
//       - - - - - - - - - - - - - - - -
//       0 1 2 3
//         1 2 3 0
//           2 3 0 1
//             3 0 1 2
//               0 1 2 3
//
////////////////////////////////////////////////////////////////////////////////
class CNetInput
	{
	//------------------------------------------------------------------------------
	// Types, enums, etc.
	//------------------------------------------------------------------------------
	public:
		enum
			{
			// Maximum total entries (see elsewhere for in-depth explanation)
			MaxTotalEntries	= 3 * Net::MaxAheadSeq,

			// Maxumum new entries (see elsewhere for in-depth explanation)
			MaxNewEntries     = 2 * Net::MaxAheadSeq,

			// Maximum old entries (see elsewhere for in-depth explanation)
			MaxOldEntries		= 1 * Net::MaxAheadSeq,

			// The size must be a power of two, and the mask must correspond to it.
			// Remember that this must be at LEAST as large as MaxTotalEntries!
			Size				= 256,
			Mask				= Size - 1,

			// Invalid input value
			Invalid = 0xffffffff
			};

	//------------------------------------------------------------------------------
	// Variables
	//------------------------------------------------------------------------------
	protected:
		UINPUT					m_aInputs[Size];						// Inputs
		U8						m_aFrameTimes[Size];					// Game time for the frames *SPA
		Net::SEQ				m_seqFrame;								// Local player's current frame number
		Net::SEQ				m_seqOldest;							// Oldest sequence we have

	//------------------------------------------------------------------------------
	// Functions
	//------------------------------------------------------------------------------
	public:
		////////////////////////////////////////////////////////////////////////////////
		// Constructor
		////////////////////////////////////////////////////////////////////////////////
		CNetInput()
			{
			ASSERT(Size >= MaxTotalEntries);
			Reset();
			}


		////////////////////////////////////////////////////////////////////////////////
		// Destructor
		////////////////////////////////////////////////////////////////////////////////
		~CNetInput()
			{
			Reset();
			}

		////////////////////////////////////////////////////////////////////////////////
		// Reset to post-construction state
		////////////////////////////////////////////////////////////////////////////////
		void Reset(void)
			{
			int16_t i = 0;
			// Clear the entire window to "invalid" values
			for (i = 0; i < Size; i++)
				m_aInputs[i] = Invalid;

			// Clear the entire window to initail values *SPA !!Eventually should input from prefs!!
			for (i = 0; i < Size; i++)
				m_aFrameTimes[i] = 100;

			// Start the frame at 0.  The oldest value always lags by a fixed distance.
			m_seqFrame = 0;
			m_seqOldest = m_seqFrame - MaxOldEntries;
			}

		////////////////////////////////////////////////////////////////////////////////
		// Move the frame forward
		// IT IS ASSUMED THAT THE CALLER WILL NOT BE STUPID!!!
		// Don't ever move the frame forward unless the input for the current frame
		// is valid, as reported by GetInput()!
		////////////////////////////////////////////////////////////////////////////////
		void IncFrame(void)
			{
			// Make sure this makes sense!
			ASSERT(m_aInputs[m_seqFrame & Mask] != Invalid);

			// Invalidate oldest sequence
			m_aInputs[m_seqOldest & Mask] = Invalid;

			// Move forward
			m_seqFrame++;
			m_seqOldest++;
			}

		////////////////////////////////////////////////////////////////////////////////
		// Put a new input value.
		// If the specified seq is outside of the "new value window", it is ignored.
		////////////////////////////////////////////////////////////////////////////////
		void Put(
			Net::SEQ seq,
			UINPUT input)
			{
			// If the seq falls within the "new values" window, we use it.  Note that
			// the "new values" window starts at the current frame.
			// The excessive casting is to make sure the compiler does this all as
			// unsigned math and comparisons.
			if ((Net::SEQ)(seq - m_seqFrame) < (Net::SEQ)MaxNewEntries)
				m_aInputs[seq & Mask] = input;
			}

		////////////////////////////////////////////////////////////////////////////////
		// Put a new frame time value.  *SPA
		// If the specified seq is outside of the "new value window", it is ignored.
		////////////////////////////////////////////////////////////////////////////////
		void PutFrameTime(
			Net::SEQ seq,
			U8 frameTime)
			{
			// If the seq falls within the "new values" window, we use it.  Note that
			// the "new values" window starts at the current frame.
			// The excessive casting is to make sure the compiler does this all as
			// unsigned math and comparisons.
			if ((Net::SEQ)(seq - m_seqFrame) < (Net::SEQ)MaxNewEntries)
				{
				m_aFrameTimes[seq & Mask] = frameTime;
				}
			}

		////////////////////////////////////////////////////////////////////////////////
		// Find first invalid value starting at the specified seq and continuing to
		// the end of the "new" window.
		////////////////////////////////////////////////////////////////////////////////
		Net::SEQ FindFirstInvalid(
			Net::SEQ seq)
			{
			Net::SEQ offset = (Net::SEQ)(seq - m_seqFrame);
			while (offset < (Net::SEQ)MaxNewEntries)
				{
				if (m_aInputs[seq & Mask] == Invalid)
					break;
				seq++;
				offset++;
				}
			return seq;
			}

		// This alternative version starts at the current frame
		Net::SEQ FindFirstInvalid(void)
			{
			return FindFirstInvalid(m_seqFrame);
			}


		////////////////////////////////////////////////////////////////////////////////
		// Get the specified input value.
		// A return value of CNetInput::Invalid means that input was not available.
		////////////////////////////////////////////////////////////////////////////////
		UINPUT Get(
			Net::SEQ seq)
			{
			// If the seq falls within the total window, we get it.  Note that this
			// window starts at the oldest value, which is always Net::MaxAheadSeq less
			// than the current frame.
			// The excessive casting is to make sure the compiler does this all as
			// unsigned math and comparisons.
			if ((Net::SEQ)(seq - m_seqOldest) < (Net::SEQ)MaxTotalEntries)
				return m_aInputs[seq & Mask];
			else
				return Invalid;
			}

		////////////////////////////////////////////////////////////////////////////////
		// Get the specified frame time value. *SPA
		// A return value of CNetInput::Invalid means that input was not available.
		////////////////////////////////////////////////////////////////////////////////
		U8 GetFrameTime(
			Net::SEQ seq)
			{
			// If the seq falls within the total window, we get it.  Note that this
			// window starts at the oldest value, which is always Net::MaxAheadSeq less
			// than the current frame.
			// The excessive casting is to make sure the compiler does this all as
			// unsigned math and comparisons.
			if ((Net::SEQ)(seq - m_seqOldest) < (Net::SEQ)MaxTotalEntries)
				return m_aFrameTimes[seq & Mask];
			else
				return Invalid;
			}


		////////////////////////////////////////////////////////////////////////////////
		// Get current frame seq
		////////////////////////////////////////////////////////////////////////////////
		Net::SEQ GetFrameSeq(void)
			{
			return m_seqFrame;
			}


		////////////////////////////////////////////////////////////////////////////////
		// Get oldest frame seq
		////////////////////////////////////////////////////////////////////////////////
		Net::SEQ GetOldestSeq(void)
			{
			return m_seqOldest;
			}
	};


#endif //NETINPUT_H
////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
