// vtest flaky: true
// vtest retry: 8
import net
import os

const (
	test_port = 45123
)

fn handle_conn(mut c net.TcpConn) {
	for {
		mut buf := []u8{len: 100, init: 0}
		read := c.read(mut buf) or {
			println('Server: connection dropped')
			return
		}
		c.write(buf[..read]) or {
			println('Server: connection dropped')
			return
		}
	}
}

fn one_shot_echo_server(mut l net.TcpListener, ch_started chan int) ! {
	eprintln('> one_shot_echo_server')
	ch_started <- 1
	mut new_conn := l.accept() or { return error('could not accept') }
	eprintln('    > new_conn: ${new_conn}')
	handle_conn(mut new_conn)
	new_conn.close() or {}
}

fn echo(address string) ! {
	mut c := net.dial_tcp(address)!
	defer {
		c.close() or {}
	}

	println('local: ' + c.addr()!.str())
	println(' peer: ' + c.peer_addr()!.str())

	data := 'Hello from vlib/net!'
	c.write_string(data)!
	mut buf := []u8{len: 4096}
	read := c.read(mut buf) or { panic(err) }
	assert read == data.len
	for i := 0; i < read; i++ {
		assert buf[i] == data[i]
	}
	println('Got "${buf.bytestr()}"')
}

fn test_tcp_ip6() {
	eprintln('\n>>> ${@FN}')
	address := 'localhost:${test_port}'
	mut l := net.listen_tcp(.ip6, ':${test_port}') or { panic(err) }
	dump(l)
	start_echo_server(mut l)
	echo(address) or { panic(err) }
	l.close() or {}
	// ensure there is at least one new socket created before the next test
	l = net.listen_tcp(.ip6, ':${test_port + 1}') or { panic(err) }
}

fn start_echo_server(mut l net.TcpListener) {
	ch_server_started := chan int{}
	spawn one_shot_echo_server(mut l, ch_server_started)
	_ := <-ch_server_started
}

fn test_tcp_ip() {
	eprintln('\n>>> ${@FN}')
	address := 'localhost:${test_port}'
	mut l := net.listen_tcp(.ip, address) or { panic(err) }
	dump(l)
	start_echo_server(mut l)
	echo(address) or { panic(err) }
	l.close() or {}
}

fn test_tcp_unix() {
	eprintln('\n>>> ${@FN}')
	// TODO(emily):
	// whilst windows supposedly supports unix sockets
	// this doesnt work (wsaeopnotsupp at the call to bind())
	$if !windows {
		address := os.real_path('tcp-test.sock')
		// address := 'tcp-test.sock'
		println('${address}')

		mut l := net.listen_tcp(.unix, address) or { panic(err) }
		start_echo_server(mut l)
		echo(address) or { panic(err) }
		l.close() or {}

		os.rm(address) or { panic('failed to remove socket file') }
	}
}

fn testsuite_end() {
	eprintln('\ndone')
}

fn test_bind() {
	$if !network ? {
		return
	}
	mut conn := net.dial_tcp_with_bind('vlang.io:80', '127.0.0.1:0')!
	conn.close()!
}
