/************************************************************************************
 *
 * D++, A Lightweight C++ library for Discord
 *
 * SPDX-License-Identifier: Apache-2.0
 * Copyright 2021 Craig Edwards and D++ contributors
 * (https://github.com/brainboxdotcc/DPP/graphs/contributors)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ************************************************************************************/
#pragma once

#include <dpp/export.h>
#ifdef _WIN32
	#include <WinSock2.h>
	#include <WS2tcpip.h>
	#include <io.h>
	#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
	#define pollfd WSAPOLLFD
#else
	#include <netinet/in.h>
	#include <sys/socket.h>
	#include <arpa/inet.h>
#endif
#include <string_view>
#include <cstdint>


namespace dpp
{
/**
 * @brief Represents a socket file descriptor.
 * This is used to ensure parity between windows and unix-like systems.
 */
#ifndef _WIN32
	using socket = int;
#else
	using socket = SOCKET;
#endif

#ifndef SOCKET_ERROR
/**
 * @brief Represents a socket in error state
 */
#define SOCKET_ERROR -1
#endif

#ifndef INVALID_SOCKET
/**
 * @brief Represents a socket which is not yet assigned
 */
#define INVALID_SOCKET ~0
#endif

/**
 * @brief Represents an IPv4 address for use with socket functions such as
 * bind().
 *
 * Avoids type punning with C style casts from sockaddr_in to sockaddr pointers.
 */
class DPP_EXPORT address_t {
	/**
	 * @brief Internal sockaddr struct
	 */
	sockaddr socket_addr{};

public:

	/**
	 * @brief Create a new address_t
	 * @param ip IPv4 address
	 * @param port Port number
	 * @note Leave both as defaults to create a default bind-to-any setting
	 */
	address_t(const std::string_view ip = "0.0.0.0", uint16_t port = 0);

	/**
	 * @brief Get sockaddr
	 * @return sockaddr pointer
	 */
	[[nodiscard]] sockaddr *get_socket_address();

	/**
	 * @brief Returns size of sockaddr_in
	 * @return sockaddr_in size
	 * @note It is important the size this returns is sizeof(sockaddr_in) not
	 * sizeof(sockaddr), this is NOT a bug but requirement of C socket functions.
	 */
	[[nodiscard]] size_t size();

	/**
	 * @brief Get the port bound to a file descriptor
	 * @param fd File descriptor
	 * @return Port number, or 0 if no port bound
	 */
	[[nodiscard]] uint16_t get_port(socket fd);
};

/**
 * @brief Allocates a dpp::socket, closing it on destruction
 */
struct DPP_EXPORT raii_socket {
	/**
	 * @brief File descriptor
	 */
	socket fd;

	/**
	 * @brief Construct a socket.
	 * Calls socket() and returns a new file descriptor
	 */
	raii_socket();

	/**
	 * @brief Non-copyable
	 */
	raii_socket(raii_socket&) = delete;

	/**
	 * @brief Non-movable
	 */
	raii_socket(raii_socket&&) = delete;

	/**
	 * @brief Non-copyable
	 */
	raii_socket operator=(raii_socket&) = delete;

	/**
	 * @brief Non-movable
	 */
	raii_socket operator=(raii_socket&&) = delete;

	/**
	 * @brief Destructor
	 * Frees the socket by closing it
	 */
	~raii_socket();
};


}
