// File: /workspace/github_c_makefile_star2_O2/sprt[P]lossless-transport/src/receiver.c
// Function: main_loop
void main_loop(void) {
	log_msg("---------- Waiting for a packet...\n");
	log_msg("Window: [%zu, %zu], buffer: %zu/%zu\n", window_start(w),
		window_end(w), window_buffer_size(w), window_get_size(w));

	/* Read the datagram received into a buffer */
	char buf[MAX_PACKET_SIZE];
	int len = recv(sockfd, buf, MAX_PACKET_SIZE, 0);
	if (len == -1) {
		exit_perror("recv");
	}

	/* Decode the datagram into a packet */
	pkt_t *pkt = pkt_new();
	if (pkt == NULL) {
		exit_msg("Could not allocate packet\n");
	}

	pkt_status_code decerr = pkt_decode(buf, len, pkt);
	if (decerr != PKT_OK) {
		log_msg("Error decoding packet (%s), ignoring\n", pkt_code_to_str(decerr));
		pkt_del(pkt);
		return;
	}

	log_msg("< %s\n", pkt_repr(pkt));

	if (!window_has(w, pkt_get_seqnum(pkt))) {
		log_msg("Out of window, ignoring\n");
		pkt_del(pkt);
		return;
	}

	/* We're gonna send a (N)ACK so let's allocate a new packet to reply */
	pkt_t *reply = pkt_new();
	if (reply == NULL) {
		exit_msg("Could not allocate reply packet\n");
	}

	if (pkt_get_tr(pkt)) {
		/* Send a NACK if we receive a truncated packet */
		pkt_status_code err = PKT_OK;
		err = err || pkt_set_type(reply, PTYPE_NACK);
		/* We don't store truncated packets so the window size doesn't change */
		err = err || pkt_set_window(reply, window_available(w));
		err = err || pkt_set_seqnum(reply, pkt_get_seqnum(pkt));

		if (err != PKT_OK) {
			exit_msg("Could not create packet: %s\n",
				pkt_code_to_str(err));
		}

		if (send_packet(sockfd, reply) == -1) {
			exit_perror("Could not send NACK: send:");
		}

		pkt_del(pkt);
		log_msg("> %s\n", pkt_repr(reply));
	} else {
		/* We're gonna send an ACK, but first we store the received
		 * packet in the buffer, and then we try to write out packets to
		 * the file, so that we can reply with an accurate window size. */

		if (window_find_seqnum(w, pkt_get_seqnum(pkt)) != NULL) {
			log_msg("Already in buffer\n");
		} else {
			if (window_push(w, pkt) == -1) {
				if (window_full(w)) {
					log_msg("Buffer full, not adding\n");
					// pkt_del(pkt);
					// pkt_del(reply);
					// return;
				} else {
					exit_msg("Could not add packet to buffer\n");
				}
			} else {
				log_msg("%d\n", pkt_get_seqnum(pkt));
			}
		}

		/* Save the timestamp of the packet we just received in case
		 * it's immediately removed from the buffer and freed. */
		uint32_t ack_timestamp = pkt_get_timestamp(pkt);

		/* Find the next in-sequence packet. If it's not in the buffer,
		 * we can't acknowledge any packet. */
		pkt_t *next_pkt = window_find_seqnum(w, window_start(w));
		while (next_pkt != NULL) {
			/* Try to write the packet to the file. Iff this succeeds,
			 * we can pop it from the buffer and slide the window. */

			const char *payload = pkt_get_payload(next_pkt);
			size_t payload_len = pkt_get_length(next_pkt);

			if (fwrite(payload, sizeof (*payload), payload_len, outfile) < payload_len) {
				exit_msg("Error writing to file\n");
			}

			log_msg("Wrote packet #%d\n", pkt_get_seqnum(next_pkt));
			assert(window_pop_timestamp(w, pkt_get_timestamp(next_pkt)) == next_pkt);

			/* We don't store truncated packets in the buffer so no
			* need to check for that */
			if (pkt_get_length(next_pkt) == 0) {
				log_msg("Received EOF packet, ready to quit\n");
			} else {
				/* Don't slide the window when we receive the
				* EOF packet. Rationale: the ACK may get lost
				* and when the sender retransmits the EOF packet,
				* it would fall outside our window. */
				window_slide(w);
			}

			pkt_del(next_pkt);
			next_pkt = window_find_seqnum(w, window_start(w));
		}

		assert(fflush(outfile) == 0);

		/* There's no duplication so we can just send an ACK */

		pkt_status_code err = PKT_OK;
		err = err || pkt_set_type(reply, PTYPE_ACK);
		err = err || pkt_set_window(reply, window_available(w));
		err = err || pkt_set_seqnum(reply, window_start(w));
		err = err || pkt_set_timestamp(reply, ack_timestamp);

		if (err != PKT_OK) {
			exit_msg("Could not create packet: %s\n",
				pkt_code_to_str(err));
		}

		if (send_packet(sockfd, reply) == -1) {
			exit_perror("Could not send ACK: send:");
		}

		log_msg("> %s\n", pkt_repr(reply));

		log_msg("Window: [%zu, %zu], buffer: %zu/%zu\n", window_start(w),
			window_end(w), window_buffer_size(w), window_get_size(w));
	}

	pkt_del(reply);
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x228, %rsp            # imm = 0x228
leaq 0x1138(%rip), %rdi      # 0x3204
xorl %eax, %eax
callq 0x16fe
movq 0x447e(%rip), %rdi      # 0x6558
callq 0x1d14
movq %rax, %rbx
movq 0x446f(%rip), %rdi      # 0x6558
callq 0x1d19
movq %rax, %r14
movq 0x4460(%rip), %rdi      # 0x6558
callq 0x1e4c
movq %rax, %r15
movq 0x4451(%rip), %rdi      # 0x6558
callq 0x1dcf
leaq 0x1115(%rip), %rdi      # 0x3228
movq %rbx, %rsi
movq %r14, %rdx
movq %r15, %rcx
movq %rax, %r8
xorl %eax, %eax
callq 0x16fe
movl 0x3fe4(%rip), %edi      # 0x6110
leaq 0x10(%rsp), %rsi
movl $0x210, %edx            # imm = 0x210
xorl %ecx, %ecx
callq 0x1040
movq %rax, %rbx
cmpl $-0x1, %ebx
jne 0x2151
leaq 0x1101(%rip), %rdi      # 0x324d
callq 0x1853
xorl %eax, %eax
callq 0x12fc
movq %rax, %r14
testq %rax, %rax
jne 0x216e
leaq 0x10eb(%rip), %rdi      # 0x3252
xorl %eax, %eax
callq 0x17b3
movslq %ebx, %rsi
leaq 0x10(%rsp), %rdi
movq %r14, %rdx
callq 0x137f
testl %eax, %eax
je 0x219f
movl %eax, %edi
callq 0x1561
leaq 0x10dd(%rip), %rdi      # 0x326d
movq %rax, %rsi
xorl %eax, %eax
callq 0x16fe
jmp 0x22b1
movq %r14, %rdi
callq 0x18bb
leaq 0x10e5(%rip), %rdi      # 0x3293
movq %rax, %rsi
xorl %eax, %eax
callq 0x16fe
movq 0x4399(%rip), %rbx      # 0x6558
movq %r14, %rdi
callq 0x157f
movzbl %al, %esi
movq %rbx, %rdi
callq 0x1d75
testb %al, %al
je 0x22a3
xorl %eax, %eax
callq 0x12fc
movq %rax, %rbx
testq %rax, %rax
jne 0x21f7
leaq 0x10c2(%rip), %rdi      # 0x32b2
xorl %eax, %eax
callq 0x17b3
movq %r14, %rdi
callq 0x145e
testb %al, %al
je 0x22ce
pushq $0x3
popq %rsi
movq %rbx, %rdi
callq 0x15a0
testl %eax, %eax
jne 0x2248
movq 0x433b(%rip), %rdi      # 0x6558
callq 0x1e3d
movzbl %al, %esi
movq %rbx, %rdi
callq 0x15d6
testl %eax, %eax
jne 0x2248
movq %r14, %rdi
callq 0x157f
movzbl %al, %esi
movq %rbx, %rdi
callq 0x15ec
testl %eax, %eax
je 0x2261
pushq $0x1
popq %rdi
callq 0x1561
leaq 0x107c(%rip), %rdi      # 0x32d3
movq %rax, %rsi
xorl %eax, %eax
callq 0x17b3
movl 0x3ea9(%rip), %edi      # 0x6110
movq %rbx, %rsi
callq 0x1b6d
cmpl $-0x1, %eax
jne 0x2280
leaq 0x1075(%rip), %rdi      # 0x32f0
callq 0x1853
movq %r14, %rdi
callq 0x1309
movq %rbx, %rdi
callq 0x18bb
leaq 0x1074(%rip), %rdi      # 0x330b
movq %rax, %rsi
xorl %eax, %eax
callq 0x16fe
jmp 0x22b4
leaq 0xfef(%rip), %rdi       # 0x3299
xorl %eax, %eax
callq 0x16fe
movq %r14, %rbx
movq %rbx, %rdi
callq 0x1309
addq $0x228, %rsp            # imm = 0x228
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x4283(%rip), %r15      # 0x6558
movq %r14, %rdi
callq 0x157f
movzbl %al, %esi
movq %r15, %rdi
callq 0x1e5a
testq %rax, %rax
je 0x22fd
leaq 0x101d(%rip), %rdi      # 0x3311
xorl %eax, %eax
callq 0x16fe
jmp 0x2353
movq 0x4254(%rip), %rdi      # 0x6558
movq %r14, %rsi
callq 0x1dd4
cmpl $-0x1, %eax
je 0x232c
movq %r14, %rdi
callq 0x157f
movzbl %al, %esi
leaq 0xe6e(%rip), %rdi       # 0x3191
xorl %eax, %eax
callq 0x16fe
jmp 0x2353
movq 0x4225(%rip), %rdi      # 0x6558
callq 0x1e29
testb %al, %al
je 0x2345
leaq 0xfe1(%rip), %rdi       # 0x3324
jmp 0x22f4
leaq 0xff1(%rip), %rdi       # 0x333d
xorl %eax, %eax
callq 0x17b3
movq %r14, %rdi
callq 0x1583
movl %eax, 0xc(%rsp)
pushq $0x1
popq %r14
leaq 0x100a(%rip), %r12      # 0x3374
movq 0x41e7(%rip), %rbp      # 0x6558
movq %rbp, %rdi
callq 0x1d14
movq %rbp, %rdi
movq %rax, %rsi
callq 0x1e5a
testq %rax, %rax
je 0x243c
movq %rax, %rbp
movq %rax, %rdi
callq 0x1589
movq %rax, %r13
movq %rbp, %rdi
callq 0x136e
movzwl %ax, %r15d
movq 0x41b2(%rip), %rcx      # 0x6560
movq %r13, %rdi
movq %r14, %rsi
movq %r15, %rdx
callq 0x11b0
cmpq %r15, %rax
jae 0x23cf
leaq 0xf95(%rip), %rdi       # 0x335d
xorl %eax, %eax
callq 0x17b3
movq %rbp, %rdi
callq 0x157f
movzbl %al, %esi
movq %r12, %rdi
xorl %eax, %eax
callq 0x16fe
movq 0x416d(%rip), %r15      # 0x6558
movq %rbp, %rdi
callq 0x1583
movq %r15, %rdi
movl %eax, %esi
callq 0x1f10
cmpq %rbp, %rax
jne 0x254e
movq %rbp, %rdi
callq 0x136e
testw %ax, %ax
je 0x2421
movq 0x413e(%rip), %rdi      # 0x6558
callq 0x1d36
jmp 0x242f
leaq 0xfc3(%rip), %rdi       # 0x33eb
xorl %eax, %eax
callq 0x16fe
movq %rbp, %rdi
callq 0x1309
jmp 0x236a
movq 0x411d(%rip), %rdi      # 0x6560
callq 0x1100
testl %eax, %eax
jne 0x256d
pushq $0x2
popq %rsi
movq %rbx, %rdi
callq 0x15a0
testl %eax, %eax
jne 0x24a5
movq 0x40f2(%rip), %rdi      # 0x6558
callq 0x1e3d
movzbl %al, %esi
movq %rbx, %rdi
callq 0x15d6
testl %eax, %eax
jne 0x24a5
movq 0x40d7(%rip), %rdi      # 0x6558
callq 0x1d14
movzbl %al, %esi
movq %rbx, %rdi
callq 0x15ec
testl %eax, %eax
jne 0x24a5
movq %rbx, %rdi
movl 0xc(%rsp), %esi
callq 0x1635
testl %eax, %eax
je 0x24be
pushq $0x1
popq %rdi
callq 0x1561
leaq 0xe1f(%rip), %rdi       # 0x32d3
movq %rax, %rsi
xorl %eax, %eax
callq 0x17b3
movl 0x3c4c(%rip), %edi      # 0x6110
movq %rbx, %rsi
callq 0x1b6d
cmpl $-0x1, %eax
jne 0x24dd
leaq 0xf4c(%rip), %rdi       # 0x3424
callq 0x1853
movq %rbx, %rdi
callq 0x18bb
leaq 0xe1f(%rip), %rdi       # 0x330b
movq %rax, %rsi
xorl %eax, %eax
callq 0x16fe
movq 0x405b(%rip), %rdi      # 0x6558
callq 0x1d14
movq %rax, %r14
movq 0x404c(%rip), %rdi      # 0x6558
callq 0x1d19
movq %rax, %r15
movq 0x403d(%rip), %rdi      # 0x6558
callq 0x1e4c
movq %rax, %r12
movq 0x402e(%rip), %rdi      # 0x6558
callq 0x1dcf
leaq 0xcf2(%rip), %rdi       # 0x3228
movq %r14, %rsi
movq %r15, %rdx
movq %r12, %rcx
movq %rax, %r8
xorl %eax, %eax
callq 0x16fe
jmp 0x22b4
leaq 0xe31(%rip), %rdi       # 0x3386
leaq 0xe6b(%rip), %rsi       # 0x33c7
leaq 0xe73(%rip), %rcx       # 0x33d6
movl $0x92, %edx
callq 0x10b0
leaq 0xe9b(%rip), %rdi       # 0x340f
leaq 0xe4c(%rip), %rsi       # 0x33c7
leaq 0xe54(%rip), %rcx       # 0x33d6
movl $0xa4, %edx
callq 0x10b0