#include "socket.hpp"
#include <stdexcept>
#include <unistd.h>

namespace Server::ListeningSocket
{
	int Create(sockaddr_in & socketAddress, int const connectionLimit)
	{
		int socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
		if (socketFileDescriptor < 0)
		{
			return -1;
		}

		int enable = 1;
		if (setsockopt(socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
		{
			close(socketFileDescriptor);
			return -1;
		}

		int const bindResult = bind(
			socketFileDescriptor,
			reinterpret_cast<sockaddr*>(&socketAddress),
			sizeof(sockaddr_in));
		if (bindResult < 0)
		{
			close(socketFileDescriptor);
			return -1;
		}

		int const listenResult = listen(socketFileDescriptor, connectionLimit);
		if (listenResult < 0)
		{
			close(socketFileDescriptor);
			return -1;
		}

		return socketFileDescriptor;
	}
}

namespace Server::Socket
{
	std::vector<char> ReadBytes(int fd, size_t limit)
	{
		size_t const readChunkSize = 128;

		std::vector<char> buffer;
		ssize_t totalBytesRead = 0;
		ssize_t bytesRead = 0;
		do
		{
			buffer.resize(buffer.size() + readChunkSize);
			bytesRead = read(fd, &buffer[totalBytesRead], readChunkSize);
			if (bytesRead < 0)
			{
				throw std::runtime_error("Error reading from filedescriptor");
			}

			totalBytesRead += bytesRead;
		} while (bytesRead == readChunkSize && bytesRead < limit);

		buffer.resize(totalBytesRead);
		return buffer;
	}

	size_t WriteBytes(int fd, std::vector<char> const & bytes)
	{
		ssize_t totalBytesWritten = 0;
		size_t const sizeToWrite = bytes.size();
		while (totalBytesWritten < sizeToWrite)
		{
			ssize_t bytesWritten = write(fd, &bytes[totalBytesWritten], sizeToWrite - totalBytesWritten);
			if (bytesWritten <= 0)
			{
				throw std::runtime_error("Error writing to filedescriptor");
			}

			totalBytesWritten += bytesWritten;
		}

		return totalBytesWritten;
	}
}