package hello

import "core:fmt"
import "core:strings"
import "core:c"
import "../../bsd"

port :: #config(port, 4000)

main :: proc() {
	hints: bsd.addrinfo
	servinfo: ^bsd.addrinfo
	their_addr: bsd.sockaddr_storage
	server, client: bsd.Socket
	err: bsd.error

	hints.family = bsd.AF_UNSPEC
	hints.socktype = bsd.SOCK_STREAM
	hints.flags = bsd.AF_PASSIVE

	portstr := strings.unsafe_string_to_cstring(fmt.tprintf("%d\x00", port))
	if err = bsd.errno(bsd.getaddrinfo(nil, portstr, &hints, &servinfo)); err != 0 {
		fmt.eprintf("getaddrinfo error: %d\n", err)
		return
	}

	p := servinfo
	for ; p != nil; p = p.next {
		if server, err = bsd.errno(bsd.socket(bsd.Family(p.family), p.socktype, p.protocol));
		   err != 0 {
			fmt.eprintf("socket error: %d\n", err)
			continue
		}

		yes: c.int = 1
		if err = bsd.errno(
			   bsd.setsockopt(server, bsd.SOL_SOCKET, bsd.SO_REUSEADDR, &yes, size_of(c.int)),
		   ); err != 0 {
			fmt.eprintf("setsockopt error: %d\n", err)
			bsd.freeaddrinfo(servinfo)
			return
		}

		if err = bsd.errno(bsd.bind(server, p.addr, p.addrlen)); err != 0 {
			_ = bsd.close(server)
			fmt.eprintf("server error: %d\n", err)
			continue
		}

		break
	}
	bsd.freeaddrinfo(servinfo)

	if p == nil {
		fmt.eprintln("server: failed to bind")
		return
	}

	if err = bsd.errno(bsd.listen(server, 4)); err != 0 {
		fmt.printf("listen error: %d\n", err)
		return
	}

	fmt.println("listening on port", port)

	// main accept() loop
	sin_size := c.uint(size_of(type_of(their_addr)))
	whobuf: [40]u8
	for {
		if client, err = bsd.errno(
			   bsd.accept(server, transmute(^bsd.sockaddr)&their_addr, &sin_size),
		   ); err != 0 {
			fmt.eprintf("accept error: %d\n", err)
			continue
		}

		who: cstring
		if who, err = bsd.errno(
			   bsd.inet_ntop(
				   (transmute(^bsd.sockaddr)&their_addr).family,
				   bsd.get_in_addr(transmute(^bsd.sockaddr)&their_addr),
				   &whobuf[0],
				   len(whobuf),
			   ),
		   ); err != 0 {
			fmt.eprintf("inet_ntop error: %d\n", err)
			who = "(unknown)"
		}
		fmt.println("got connection from", who)

		sent: uint
		msg :: "Hello, world!\n"
		if sent, err = bsd.errno(bsd.send(client, raw_data(string(msg)), len(msg), 0)); err != 0 {
			fmt.eprintf("send error: %d\n", err)
		}
		_ = bsd.close(client)
		fmt.printf("sent %d bytes\n", sent)
	}
}

