// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: 4a144180172bbc515d20bf385a4f2c685247e488 $
//
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 2006-2025 by The Odamex Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// 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.
//
// DESCRIPTION:
//	Store and serialize input commands between client and server
//
//-----------------------------------------------------------------------------

#pragma once

#include "i_net.h"
#include "m_fixed.h"

// Forward declaration avoids circular reference
class player_s;
typedef player_s player_t;

static constexpr short CENTERVIEW = -32768;
//
// NetCommand
//
// A class that contains the input commands from a player and can
// serialize/deserialize to a buf_t for delivery over the network.
// NetCommand uses absolute angles for player_t::mo::angle and pitch instead
// of delta angles like usercmd_t::yaw and patch since too many dropped packets
// will cause desynchronization with delta angles.

class NetCommand
{
public:
	NetCommand();

	[[nodiscard]] bool	hasButtons() const		{ return ((mFields & CMD_BUTTONS) != 0); }
	[[nodiscard]] bool	hasAngle() const		{ return ((mFields & CMD_ANGLE) != 0); }
	[[nodiscard]] bool	hasPitch() const		{ return ((mFields & CMD_PITCH) != 0); }
	[[nodiscard]] bool	hasForwardMove() const	{ return ((mFields & CMD_FORWARD) != 0); }
	[[nodiscard]] bool	hasSideMove() const		{ return ((mFields & CMD_SIDE) != 0); }
	[[nodiscard]] bool	hasUpMove() const		{ return ((mFields & CMD_UP) != 0); }
	[[nodiscard]] bool	hasImpulse() const		{ return ((mFields & CMD_IMPULSE) != 0); }
	[[nodiscard]] bool	hasDeltaYaw() const		{ return ((mFields & CMD_DELTAYAW) != 0); }
	[[nodiscard]] bool	hasDeltaPitch() const	{ return ((mFields & CMD_DELTAPITCH) != 0); }

	[[nodiscard]] int		getTic() const			{ return mTic; }
	[[nodiscard]] int		getWorldIndex() const	{ return mWorldIndex; }
	[[nodiscard]] byte	getButtons() const		{ return mButtons; }
	[[nodiscard]] fixed_t	getAngle() const 		{ return mAngle; }
	[[nodiscard]] fixed_t	getPitch() const		{ return mPitch; }
	[[nodiscard]] short	getForwardMove() const	{ return mForwardMove; }
	[[nodiscard]] short	getSideMove() const		{ return mSideMove; }
	[[nodiscard]] short	getUpMove() const		{ return mUpMove; }
	[[nodiscard]] byte	getImpulse() const		{ return mImpulse; }
	[[nodiscard]] short	getDeltaYaw() const		{ return mDeltaYaw; }
	[[nodiscard]] short	getDeltaPitch() const	{ return mDeltaPitch; }

	void setTic(int val)
	{
		mTic = val;
	}

	void setWorldIndex(int val)
	{
		mWorldIndex = val;
	}

	void setButtons(byte val)
	{
		updateFields(CMD_BUTTONS, val);
		mButtons = val;
	}

	void setAngle(fixed_t val)
	{
		updateFields(CMD_ANGLE, val);
		mAngle = val;
	}

	void setPitch(fixed_t val)
	{
		updateFields(CMD_PITCH, val);
		mPitch = val;
	}

	void setForwardMove(short val)
	{
		updateFields(CMD_FORWARD, val);
		mForwardMove = val;
	}

	void setSideMove(short val)
	{
		updateFields(CMD_SIDE, val);
		mSideMove = val;
	}

	void setUpMove(short val)
	{
		updateFields(CMD_UP, val);
		mUpMove = val;
	}

	void setImpulse(byte val)
	{
		updateFields(CMD_IMPULSE, val);
		mImpulse = val;
	}

	void setDeltaYaw(short val)
	{
		updateFields(CMD_DELTAYAW, val);
		mDeltaYaw = val;
	}

	void setDeltaPitch(short val)
	{
		updateFields(CMD_DELTAPITCH, val);
		mDeltaPitch = val;
	}

	void clear();
	void write(buf_t *buf);
	void read(buf_t *buf);

	void toPlayer(player_t *player) const;
	void fromPlayer(player_t *player);

private:
	static constexpr int CMD_BUTTONS		= 0x0001;
	static constexpr int CMD_ANGLE			= 0x0002;
	static constexpr int CMD_PITCH			= 0x0004;
	static constexpr int CMD_FORWARD		= 0x0008;
	static constexpr int CMD_SIDE			= 0x0010;
	static constexpr int CMD_UP				= 0x0020;
	static constexpr int CMD_IMPULSE		= 0x0040;
	static constexpr int CMD_DELTAYAW		= 0x0080;
	static constexpr int CMD_DELTAPITCH		= 0x0100;

	int			mTic;
	int			mWorldIndex;
	int			mFields;
	byte		mButtons;
	fixed_t		mAngle;
	fixed_t		mPitch;
	short		mForwardMove;
	short		mSideMove;
	short		mUpMove;
	byte		mImpulse;
	short		mDeltaYaw;
	short		mDeltaPitch;

	int getSerializedFields();

	void updateFields(int flag, int value)
	{
		if (value == 0)
			mFields &= ~flag;
		else
			mFields |= flag;
	}
};
