#pragma once

#include <cstdint>
#include <deque>

#include <netinet/in.h>

struct tb_cli_args;

// Simple UART-over-TCP bridge used by the Verilator/CXXRTL testbenches.
//
// Each UART is exposed as a raw TCP byte stream (one client at a time).
// - CPU TX: MMIO write -> queued -> non-blocking send() to connected client.
// - CPU RX: client -> queued -> MMIO read pops one byte (poll STATUS first).
struct tb_uart_state {
	static constexpr uint32_t N_UARTS = 2;

	struct uart {
		uint16_t port = 0;
		int server_fd = -1;
		int client_fd = -1;
		bool overrun = false;

		// Bounded FIFOs to avoid unbounded memory growth if the CPU or client
		// isn't keeping up. When full, RX drops new data and TX drops old data.
		std::deque<uint8_t> rx_fifo;
		std::deque<uint8_t> tx_fifo;
		size_t rx_capacity = 4096;
		size_t tx_capacity = 4096;

		sockaddr_in bind_addr {};

		void init(uint16_t port_);
		void close();
		void poll_accept(uint32_t uart_idx);
		void poll_rx(uint32_t uart_idx);
		void poll_tx(uint32_t uart_idx);
		bool connected() const { return client_fd >= 0; }
	};

	uart uarts[N_UARTS];

	tb_uart_state() = default;
	explicit tb_uart_state(const tb_cli_args &args);
	~tb_uart_state();

	void step();

	uint32_t read_status(uint32_t uart_idx);
	uint32_t read_data(uint32_t uart_idx);
	void write_data(uint32_t uart_idx, uint8_t byte);
	void write_ctrl(uint32_t uart_idx, uint32_t value);
};

