/*
 * This file is part of OpenTTD.
 * OpenTTD 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, version 2.
 * OpenTTD 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 OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
 */

/** @file tcp_admin.h Basic functions to receive and send TCP packets to and from the admin network. */

#ifndef NETWORK_CORE_TCP_ADMIN_H
#define NETWORK_CORE_TCP_ADMIN_H

#include "os_abstraction.h"
#include "tcp.h"
#include "../network_type.h"

/**
 * Enum with types of TCP packets specific to the admin network.
 * This protocol may only be extended to ensure stability.
 * @important The order MUST not be changed.
 */
enum class PacketAdminType : uint8_t {
	AdminJoin, ///< The admin announces and authenticates itself to the server using an unsecured passwords.
	AdminQuit, ///< The admin tells the server that it is quitting.
	AdminUpdateFrequency, ///< The admin tells the server the update frequency of a particular piece of information.
	AdminPoll, ///< The admin explicitly polls for a piece of information.
	AdminChat, ///< The admin sends a chat message to be distributed.
	AdminRemoteConsoleCommand, ///< The admin sends a remote console command.
	AdminGameScript, ///< The admin sends a JSON string for the GameScript.
	AdminPing, ///< The admin sends a ping to the server, expecting a ping-reply (PONG) packet.
	AdminExternalChat, ///< The admin sends a chat message from external source.
	AdminJoinSecure, ///< The admin announces and starts a secure authentication handshake.
	AdminAuthenticationResponse, ///< The admin responds to the authentication request.

	ServerFull = 100, ///< The server tells the admin it cannot accept the admin.
	ServerBanned, ///< The server tells the admin it is banned.
	ServerError, ///< The server tells the admin an error has occurred.
	ServerProtocol, ///< The server tells the admin its protocol version.
	ServerWelcome, ///< The server welcomes the admin to a game.
	ServerNewGame, ///< The server tells the admin its going to start a new game.
	ServerShutdown, ///< The server tells the admin its shutting down.

	ServerDate, ///< The server tells the admin what the current game date is.
	ServerClientJoin, ///< The server tells the admin that a client has joined.
	ServerClientInfo, ///< The server gives the admin information about a client.
	ServerClientUpdate, ///< The server gives the admin an information update on a client.
	ServerClientQuit, ///< The server tells the admin that a client quit.
	ServerClientError, ///< The server tells the admin that a client caused an error.
	ServerCompanyNew, ///< The server tells the admin that a new company has started.
	ServerCompanyInfo, ///< The server gives the admin information about a company.
	ServerCompanyUpdate, ///< The server gives the admin an information update on a company.
	ServerCompanyRemove, ///< The server tells the admin that a company was removed.
	ServerCompanyEconomy, ///< The server gives the admin some economy related company information.
	ServerCompanyStatistics, ///< The server gives the admin some statistics about a company.
	ServerChat, ///< The server received a chat message and relays it.
	ServerRemoteConsoleCommand, ///< The server's reply to a remote console command.
	ServerConsole, ///< The server gives the admin the data that got printed to its console.
	ServerCommandNames, ///< The server sends out the names of the DoCommands to the admins.
	ServerCommandLoggingOld, ///< Used to be the type ID of \c PacketAdminType::ServerCommandLogging in \c NETWORK_GAME_ADMIN_VERSION 1.
	ServerGameScript, ///< The server gives the admin information from the GameScript in JSON.
	ServerRemoteConsoleCommandEnd, ///< The server indicates that the remote console command has completed.
	ServerPong, ///< The server replies to a ping request from the admin.
	ServerCommandLogging, ///< The server gives the admin copies of incoming command packets.
	ServerAuthenticationRequest, ///< The server gives the admin the used authentication method and required parameters.
	ServerEnableEncryption, ///< The server tells that authentication has completed and requests to enable encryption with the keys of the last \c PacketAdminType::AdminAuthenticationResponse.
};
/** Mark PacketAdminType as a PacketType. */
template <> struct IsEnumPacketType<PacketAdminType> {
	static constexpr bool value = true; ///< This is an enumeration of a PacketType.
};

/** Status of an admin. */
enum class AdminStatus : uint8_t {
	Inactive, ///< The admin is not connected nor active.
	Authenticate, ///< The admin is connected and working on authentication.
	Active, ///< The admin is active.
};

/** Update types an admin can register a frequency for */
enum class AdminUpdateType : uint8_t {
	Date, ///< Updates about the date of the game.
	ClientInfo, ///< Updates about the information of clients.
	CompanyInfo, ///< Updates about the generic information of companies.
	CompanyEconomy, ///< Updates about the economy of companies.
	CompanyStats, ///< Updates about the statistics of companies.
	Chat, ///< The admin would like to have chat messages.
	Console, ///< The admin would like to have console messages.
	CmdNames, ///< The admin would like a list of all DoCommand names.
	CmdLogging, ///< The admin would like to have DoCommand information.
	Gamescript, ///< The admin would like to have gamescript messages.
	End, ///< Must ALWAYS be on the end of this list!! (period)
};

/** Update frequencies an admin can register. */
enum class AdminUpdateFrequency : uint8_t {
	Poll, ///< The admin can poll this.
	Daily, ///< The admin gets information about this on a daily basis.
	Weekly, ///< The admin gets information about this on a weekly basis.
	Monthly, ///< The admin gets information about this on a monthly basis.
	Quarterly, ///< The admin gets information about this on a quarterly basis.
	Annually, ///< The admin gets information about this on a yearly basis.
	Automatic, ///< The admin gets information about this when it changes.
};
using AdminUpdateFrequencies = EnumBitSet<AdminUpdateFrequency, uint8_t>; ///< Bitset of chosen update frequencies.

/** Reasons for removing a company - communicated to admins. */
enum class AdminCompanyRemoveReason : uint8_t {
	Manual, ///< The company is manually removed.
	Autoclean, ///< The company is removed due to autoclean.
	Bankrupt, ///< The company went belly-up.

	End, ///< Sentinel for end.
};

/** Main socket handler for admin related connections. */
class NetworkAdminSocketHandler : public NetworkTCPSocketHandler {
protected:
	std::string admin_name{}; ///< Name of the admin.
	std::string admin_version{}; ///< Version string of the admin.
	AdminStatus status = AdminStatus::Inactive; ///< Status of this admin.

	NetworkRecvStatus ReceiveInvalidPacket(PacketAdminType type);

	/**
	 * Join the admin network using an unsecured password exchange:
	 * string  Unsecured password the server is expecting for this network.
	 * string  Name of the application being used to connect.
	 * string  Version string of the application being used to connect.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminJoin(Packet &p);

	/**
	 * Notification to the server that this admin is quitting.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminQuit(Packet &p);

	/**
	 * Register updates to be sent at certain frequencies (as announced in the PROTOCOL packet):
	 * uint16_t  Update type (see #AdminUpdateType). Note integer type - see "Certain Packet Information" in docs/admin_network.md.
	 * uint16_t  Update frequency (see #AdminUpdateFrequency), setting #AdminUpdateFrequency::Poll is always ignored.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminUpdateFrequency(Packet &p);

	/**
	 * Poll the server for certain updates, an invalid poll (e.g. not existent id) gets silently dropped:
	 * uint8_t   #AdminUpdateType the server should answer for, only if #AdminUpdateFrequency::Poll is advertised in the PROTOCOL packet. Note integer type - see "Certain Packet Information" in docs/admin_network.md.
	 * uint32_t  ID relevant to the packet type, e.g.
	 *          - the client ID for #AdminUpdateType::ClientInfo. Use UINT32_MAX to show all clients.
	 *          - the company ID for #AdminUpdateType::CompanyInfo. Use UINT32_MAX to show all companies.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminPoll(Packet &p);

	/**
	 * Send chat as the server:
	 * uint8_t   Action such as NetworkAction::ChatClient (see #NetworkAction).
	 * uint8_t   Destination type such as NetworkChatDestinationType::Broadcast (see #NetworkChatDestinationType).
	 * uint32_t  ID of the destination such as company or client id.
	 * string  Message.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminChat(Packet &p);

	/**
	 * Send chat from the external source:
	 * string  Name of the source this message came from.
	 * uint16_t  TextColour to use for the message.
	 * string  Name of the user who sent the message.
	 * string  Message.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminExternalChat(Packet &p);

	/**
	 * Execute a command on the servers console:
	 * string  Command to be executed.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminRemoteConsoleCommand(Packet &p);

	/**
	 * Send a JSON string to the current active GameScript.
	 * json  JSON string for the GameScript.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminGameScript(Packet &p);

	/**
	 * Ping the server, requiring the server to reply with a pong packet.
	 * uint32_t Integer value to pass to the server, which is quoted in the reply.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminPing(Packet &p);

	/**
	 * Join the admin network using a secure authentication method:
	 * string Name of the application being used to connect.
	 * string Version string of the application being used to connect.
	 * uint16_t Bitmask of supported authentication methods. See \c NetworkAuthenticationMethod for the supported methods.
	 *
	 * The server will determine which of the authentication methods supplied by the client will be used.
	 * When there is no supported authentication method, an \c PacketAdminType::ServerError packet will be
	 * sent with \c NetworkErrorCode::NoAuthenticationMethodAvailable as error.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminJoinSecure(Packet &p);

	/**
	 * Admin responds to \c PacketAdminType::ServerAuthenticationRequest with the appropriate
	 * data given the agreed upon \c NetworkAuthenticationMethod.
	 * With \c NETWORK_AUTH_METHOD_X25519_PAKE and \c NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY:
	 *   32 * uint8_t Public key of the client.
	 *   16 * uint8_t Message authentication code (mac).
	 *    8 * uint8_t Encrypted message of the authentication (just random bytes).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveAdminAuthenticationResponse(Packet &p);

	/**
	 * The server is full (connection gets closed).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerFull(Packet &p);

	/**
	 * The source IP address is banned (connection gets closed).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerBanned(Packet &p);

	/**
	 * An error was caused by this admin connection (connection gets closed).
	 * uint8_t  NetworkErrorCode the error caused.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerError(Packet &p);

	/**
	 * Inform a just joined admin about the protocol specifics:
	 * uint8_t   Protocol version.
	 * bool    Further protocol data follows (repeats through all update packet types).
	 * uint16_t  Update packet type.
	 * uint16_t  Frequencies allowed for this update packet (bitwise).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerProtocol(Packet &p);

	/**
	 * Welcome a connected admin to the game:
	 * string  Name of the Server.
	 * string  OpenTTD version string.
	 * bool    Server is dedicated.
	 * string  Name of the Map.
	 * uint32_t  Random seed of the Map.
	 * uint8_t   Landscape of the Map.
	 * uint32_t  Start date of the Map.
	 * uint16_t  Map width.
	 * uint16_t  Map height.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerWelcome(Packet &p);

	/**
	 * Notification about a newgame.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerNewGame(Packet &p);

	/**
	 * Notification about the server shutting down.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerShutdown(Packet &p);

	/**
	 * Send the current date of the game:
	 * uint32_t  Current game date.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerDate(Packet &p);

	/**
	 * Notification of a new client:
	 * uint32_t  ID of the new client.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerClientJoin(Packet &p);

	/**
	 * Client information of a specific client:
	 * uint32_t  ID of the client.
	 * string  Network address of the client.
	 * string  Name of the client.
	 * uint8_t   Language of the client.
	 * uint32_t  Date the client joined the game.
	 * uint8_t   ID of the company the client is playing as (255 for spectators).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerClientInfo(Packet &p);

	/**
	 * Client update details on a specific client (e.g. after rename or move):
	 * uint32_t  ID of the client.
	 * string  Name of the client.
	 * uint8_t   ID of the company the client is playing as (255 for spectators).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerClientUpdate(Packet &p);

	/**
	 * Notification about a client leaving the game.
	 * uint32_t  ID of the client that just left.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerClientQuit(Packet &p);

	/**
	 * Notification about a client error (and thus the clients disconnection).
	 * uint32_t  ID of the client that made the error.
	 * uint8_t   Error the client made (see NetworkErrorCode).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerClientError(Packet &p);

	/**
	 * Notification of a new company:
	 * uint8_t   ID of the new company.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCompanyNew(Packet &p);

	/**
	 * Company information on a specific company:
	 * uint8_t   ID of the company.
	 * string  Name of the company.
	 * string  Name of the companies manager.
	 * uint8_t   Main company colour.
	 * bool    Company is protected.
	 * uint32_t  Year the company was inaugurated.
	 * bool    Company is an AI.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCompanyInfo(Packet &p);

	/**
	 * Company information of a specific company:
	 * uint8_t   ID of the company.
	 * string  Name of the company.
	 * string  Name of the companies manager.
	 * uint8_t   Main company colour.
	 * bool    Company is protected.
	 * uint8_t   Quarters of bankruptcy.
	 * uint8_t   Owner of share 1.
	 * uint8_t   Owner of share 2.
	 * uint8_t   Owner of share 3.
	 * uint8_t   Owner of share 4.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCompanyUpdate(Packet &p);

	/**
	 * Notification about a removed company (e.g. due to bankruptcy).
	 * uint8_t   ID of the company.
	 * uint8_t   Reason for being removed (see #AdminCompanyRemoveReason).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCompanyRemove(Packet &p);

	/**
	 * Economy update of a specific company:
	 * uint8_t   ID of the company.
	 * uint64_t  Money.
	 * uint64_t  Loan.
	 * int64_t   Income.
	 * uint16_t  Delivered cargo (this quarter).
	 * uint64_t  Company value (last quarter).
	 * uint16_t  Performance (last quarter).
	 * uint16_t  Delivered cargo (last quarter).
	 * uint64_t  Company value (previous quarter).
	 * uint16_t  Performance (previous quarter).
	 * uint16_t  Delivered cargo (previous quarter).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCompanyEconomy(Packet &p);

	/**
	 * Company statistics on stations and vehicles:
	 * uint8_t   ID of the company.
	 * uint16_t  Number of trains.
	 * uint16_t  Number of lorries.
	 * uint16_t  Number of busses.
	 * uint16_t  Number of planes.
	 * uint16_t  Number of ships.
	 * uint16_t  Number of train stations.
	 * uint16_t  Number of lorry stations.
	 * uint16_t  Number of bus stops.
	 * uint16_t  Number of airports and heliports.
	 * uint16_t  Number of harbours.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCompanyStatistics(Packet &p);

	/**
	 * Send chat from the game into the admin network:
	 * uint8_t   Action such as NetworkAction::ChatClient (see #NetworkAction).
	 * uint8_t   Destination type such as NetworkChatDestinationType::Broadcast (see #NetworkChatDestinationType).
	 * uint32_t  ID of the client who sent this message.
	 * string  Message.
	 * uint64_t  Money (only when it is a 'give money' action).
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerChat(Packet &p);

	/**
	 * Result of an rcon command:
	 * uint16_t  Colour as it would be used on the server or a client.
	 * string  Output of the executed command.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerRemoteConsoleCommand(Packet &p);

	/**
	 * Send what would be printed on the server's console also into the admin network.
	 * string  The origin of the text, e.g. "console" for console, or "net" for network related (debug) messages.
	 * string  Text as found on the console of the server.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerConsole(Packet &p);

	/**
	 * Send DoCommand names to the bot upon request only.
	 * Multiple of these packets can follow each other in order to provide
	 * all known DoCommand names.
	 *
	 * NOTICE: Data provided with this packet is not stable and will not be
	 *         treated as such. Do not rely on IDs or names to be constant
	 *         across different versions / revisions of OpenTTD.
	 *         Data provided in this packet is for logging purposes only.
	 *
	 * These three fields are repeated until the packet is full:
	 * bool    Data to follow.
	 * uint16_t  ID of the DoCommand.
	 * string  Name of DoCommand.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCommandNames(Packet &p);

	/**
	 * Send incoming command packets to the admin network.
	 * This is for logging purposes only.
	 *
	 * NOTICE: Data provided with this packet is not stable and will not be
	 *         treated as such. Do not rely on IDs or names to be constant
	 *         across different versions / revisions of OpenTTD.
	 *         Data provided in this packet is for logging purposes only.
	 *
	 * uint32_t  ID of the client sending the command.
	 * uint8_t   ID of the company (0..MAX_COMPANIES-1).
	 * uint16_t  ID of the command.
	 * `<var>` Command specific buffer with encoded parameters of variable length.
	 *         The content differs per command and can change without notification.
	 * uint32_t  Frame of execution.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerCommandLogging(Packet &p);

	/**
	 * Server requests authentication challenge from the admin.
	 * uint8_t The chosen authentication method from \c NetworkAuthenticationMethod.
	 * With \c NETWORK_AUTH_METHOD_X25519_PAKE and \c NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY:
	 *   32 * uint8_t Public key of the server.
	 *   24 * uint8_t Nonce to use for the encryption.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerAuthenticationRequest(Packet &p);

	/**
	 * Indication to the client that authentication is complete and encryption has to be used from here on forward.
	 * The encryption uses the shared keys generated by the last AUTH_REQUEST key exchange.
	 * 24 * uint8_t Nonce for encrypted connection.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerEnableEncryption(Packet &p);

	/**
	 * Send a ping-reply (pong) to the admin that sent us the ping packet.
	 * uint32_t  Integer identifier - should be the same as read from the admins ping packet.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerPong(Packet &p);

	/**
	 * Notify the admin connection that the rcon command has finished.
	 * string The command as requested by the admin connection.
	 * @param p The packet that was just received.
	 * @return The state the network should have.
	 */
	virtual NetworkRecvStatus ReceiveServerRemoteConsoleCommandEnd(Packet &p);

	NetworkRecvStatus HandlePacket(Packet &p);
public:
	NetworkRecvStatus CloseConnection(bool error = true) override;

	/**
	 * Create the admin handler for the given socket.
	 * @param s The socket to communicate over.
	 */
	NetworkAdminSocketHandler(SOCKET s) : NetworkTCPSocketHandler(s) {}

	NetworkRecvStatus ReceivePackets();

	/**
	 * Get the status of the admin.
	 * @return The status of the admin.
	 */
	AdminStatus GetAdminStatus() const
	{
		return this->status;
	}
};

#endif /* NETWORK_CORE_TCP_ADMIN_H */
