#include <fstream>
#include <cstdint>

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "tb.h"
#include "tb_cli.h"

#define TCP_BUF_SIZE 256

struct tb_jtag_state {
	enum class transport_t {
		none,
		remote_bitbang,
		jtag_vpi
	};

	tb_cli_args args;
	transport_t transport;

	int server_fd;
	int sock_fd;
	struct sockaddr_in sock_addr;
	int sock_opt;
	socklen_t sock_addr_len;
	char txbuf[TCP_BUF_SIZE], rxbuf[TCP_BUF_SIZE];
	int rx_ptr;
	int rx_remaining;
	int tx_ptr;

	static constexpr int VPI_XFERT_MAX_SIZE = 512;
	static constexpr int VPI_PKT_SIZE = 4 + VPI_XFERT_MAX_SIZE + VPI_XFERT_MAX_SIZE + 4 + 4;
	uint8_t vpi_rxbuf[VPI_PKT_SIZE];
	int vpi_rx_count;
	uint32_t vpi_poll_ctr;
	uint32_t vpi_poll_backoff;

	std::ofstream jtag_dump_fd;
	std::ifstream jtag_replay_fd;

	tb_jtag_state(const tb_cli_args &_args);

	// Returns true if an exit command was received from the JTAG socket
	// If memio/cycle_count/timed_out are provided, the testbench may advance
	// the core clock while processing JTAG_VPI TMS sequences (idle/wait cycles),
	// so the DMI/APB CDC can make progress and OpenOCD doesn't spin on BUSY.
	bool step(
		tb_top &tb,
		mem_io_state *memio = nullptr,
		int64_t *cycle_count = nullptr,
		bool *timed_out = nullptr,
		uint32_t *core_cycles_advanced = nullptr
	);

	void close();
};
