// File: /workspace/github_cpp_makefile_star2_O2/maclellant[P]ReliableGoBackN/client.cpp
// Function: receive_func(int, char*, sockaddr_in)
void receive_func(int sockfd, char* filename, sockaddr_in server)
{
    socklen_t slen = sizeof(server);
    Packet packet;
    int cur_seq;
    int exp_seq = 0;
    bool send_ack = false;
    bool send_nak = false;
    FILE *outfile;
    outfile = fopen(filename, "wb");

    bool running = true;
    int timeout_amount = 0;
    while(running)
    {

        if (recvfrom(sockfd, packet.buffer, PACKET_SIZE, 0, (struct sockaddr*)&server, &slen)==-1)
        {
            if (errno == EWOULDBLOCK)
            {
                errno = 0;
                std::cout << "TIMEOUT: Server not responding...\n\n";
                timeout_amount++;
                if (timeout_amount > 15)
                {
                    fprintf(stderr, "Error: Server not responding...ending program\n");
                    close(sockfd);
                    exit(EXIT_FAILURE);
                }
                continue;
            }
            else
            {
                fprintf(stderr, "Error: Could not receive from client\n");
                close(sockfd);
                exit(EXIT_FAILURE);
            }
        }

        timeout_amount = 0;

        uint8_t packet_type = packet.type();
        uint8_t seq_num = packet.sequence();
        uint16_t checksum = packet.checksum();
        uint16_t data_size = packet.size();
        char* data = packet.data();
        cur_seq = (int) seq_num;

        send_ack = false;
        send_nak = false;

        // Switch behavior based on packet type
        switch(packet_type) {
            case ACK:
                std::cout << "ACKNOWLEDGE: Packet discarded" << std::endl << std::endl;
            break;
            case NAK:
                std::cout << "NOT ACKNOWLEDGE: Packet discarded" << std::endl << std::endl;
            break;
            case GET:
                std::cout << "GET: Packet discarded" << std::endl << std::endl;
            break;
            break;
            break;
            case TRN:
                if(exp_seq == cur_seq) {
                    if(checksum == calc_checksum(packet)) {
                        if(data_size > 0)
                        {
                            std::cout << "RECEIVED: sequence " << (int)cur_seq << "\n";
                            std::cout << "DATA:\n\n";
                            std::cout << packet_string(packet, 48) << "\n\n";
                            fwrite(data, 1, data_size, outfile);
                            //Updating the expected sequence number.
                            exp_seq = (exp_seq + 1) % SEQ_NUM;
                        }
                        else
                        {
                            fclose(outfile);
                            exp_seq = (exp_seq + 1) % SEQ_NUM;
                            std::cout << "RECEIVED: close packet for file transfer: closing transfer" 
                                << std::endl << std::endl;
                            running = false;
                            send_ack = true;
                            break;
                        }
                        send_ack = true;
                    }
                    else {
                        std::cout << "DAMAGED: sequence " << (int)cur_seq << ": damaged packet"
                            << std::endl << std::endl;
                        send_nak = true;
                    }
                }
                else {
                    std::cout << "OUT OF ORDER: sequence " << (int)cur_seq << ": incorrect sequence number" 
                        << std::endl << std::endl;
                    send_ack = true;
                }
            break;
            default:
                printf("UNKNOWN PACKET TYPE: Packet discarded");
            break;
        }

        // Send ACK for received packet
        if(send_ack) {
            packet = Packet(exp_seq, ACK);
            std::cout << "SENDING ACK: sequence " << (int)exp_seq << std::endl << std::endl;
        
            if (sendto(sockfd, packet.buffer, PACKET_SIZE, 0, (struct sockaddr*) &server, slen) == -1)
            {
                perror("Error: could not send acknowledge to server\n");
                close(sockfd);
                exit(EXIT_FAILURE);
            }
        }
        // Send NAK for damaged packet
        else if (send_nak) {
            packet = Packet(exp_seq, NAK);            
            std::cout << "SENDING NAK: sequence " << (int)exp_seq << std::endl << std::endl;
        
            if (sendto(sockfd, packet.buffer, PACKET_SIZE, 0, (struct sockaddr*) &server, slen) == -1)
            {
                perror("Error: could not send acknowledge to server\n");
                close(sockfd);
                exit(EXIT_FAILURE);
            }
        }

    }
    // Sending success message for completion
    char* msg = SUCCESS_MSG;
    std::string ttemp(SUCCESS_MSG);
    packet = Packet(msg, ttemp.size(), 0, GET);
    std::cout << "SENDING SUCCESS MSG" << std::endl;
    if (sendto(sockfd, packet.buffer, PACKET_SIZE, 0, (struct sockaddr*) &server, slen) == -1)
    {
        perror("Error: could not send acknowledge to server\n");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x458, %rsp            # imm = 0x458
movq %rsi, %r14
movl %edi, %ebx
leaq -0x58(%rbp), %rax
movq %rdx, (%rax)
movq %rcx, 0x8(%rax)
leaq -0x30(%rbp), %rax
movl $0x10, (%rax)
leaq -0x478(%rbp), %r12
movq %r12, %rdi
callq 0x2d28
leaq 0x9b8(%rip), %rsi       # 0x315e
movq %r14, %rdi
callq 0x2160
movq %rax, -0x48(%rbp)
movb $0x1, %al
movq 0x280d(%rip), %r13      # 0x4fc8
leaq 0x99f(%rip), %r14       # 0x3161
movl $0x0, -0x2c(%rbp)
testb $0x1, %al
je 0x2b2d
movzbl -0x2c(%rbp), %eax
movl %eax, -0x3c(%rbp)
pushq $0x10
popq %r15
movl $0x200, %edx            # imm = 0x200
movl %ebx, %edi
movq %r12, %rsi
xorl %ecx, %ecx
leaq -0x58(%rbp), %r8
leaq -0x30(%rbp), %r9
callq 0x20f0
cmpq $-0x1, %rax
jne 0x2821
callq 0x2030
cmpl $0xb, (%rax)
jne 0x2bd2
andl $0x0, (%rax)
movq %r13, %rdi
movq %r14, %rsi
callq 0x2140
decl %r15d
jne 0x27dc
jmp 0x2be7
movzbl -0x478(%rbp), %eax
cmpq $0x3, %rax
ja 0x2969
leaq 0x7cb(%rip), %rcx       # 0x3004
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq %r13, %rdi
leaq 0x992(%rip), %rsi       # 0x31de
jmp 0x294f
movq %r13, %rdi
leaq 0x9a1(%rip), %rsi       # 0x31fc
jmp 0x294f
movzbl -0x477(%rbp), %r15d
cmpl %r15d, -0x2c(%rbp)
jne 0x297c
movzwl -0x476(%rbp), %eax
movl %eax, -0x38(%rbp)
movzwl -0x474(%rbp), %r15d
movq %r12, %rdi
callq 0x2dfb
cmpl -0x38(%rbp), %eax
je 0x29c2
movq %r13, %rdi
leaq 0x9ca(%rip), %rsi       # 0x3269
callq 0x2140
movq %rax, %rdi
movl -0x2c(%rbp), %esi
callq 0x21b0
movq %rax, %rdi
leaq 0x9c3(%rip), %rsi       # 0x327c
callq 0x2140
movq %rax, %rdi
callq 0x20b0
movq %rax, %rdi
callq 0x20b0
leaq -0x278(%rbp), %r15
movq %r15, %rdi
movl -0x3c(%rbp), %esi
pushq $0x1
popq %rdx
callq 0x2d2e
movl $0x200, %edx            # imm = 0x200
movq %r12, %rdi
movq %r15, %rsi
callq 0x2100
movq %r13, %rdi
leaq 0xa2e(%rip), %rsi       # 0x332b
callq 0x2140
movq %rax, %rdi
movl -0x2c(%rbp), %esi
callq 0x21b0
movq %rax, %rdi
callq 0x20b0
movq %rax, %rdi
callq 0x20b0
movl -0x30(%rbp), %r9d
movl $0x200, %edx            # imm = 0x200
movl %ebx, %edi
movq %r12, %rsi
xorl %ecx, %ecx
leaq -0x58(%rbp), %r8
callq 0x2050
cmpq $-0x1, %rax
jne 0x27d8
jmp 0x2b1c
movq %r13, %rdi
leaq 0x88b(%rip), %rsi       # 0x31da
callq 0x2140
movq %rax, %rdi
callq 0x20b0
movq %rax, %rdi
callq 0x20b0
jmp 0x27d8
leaq 0x951(%rip), %rdi       # 0x32c1
xorl %eax, %eax
callq 0x2040
jmp 0x27d8
movq %r13, %rdi
leaq 0x907(%rip), %rsi       # 0x328d
callq 0x2140
movq %rax, %rdi
movl %r15d, %esi
callq 0x21b0
movq %rax, %rdi
leaq 0x905(%rip), %rsi       # 0x32a5
callq 0x2140
movq %rax, %rdi
callq 0x20b0
movq %rax, %rdi
callq 0x20b0
movb $0x1, %al
movl %eax, -0x38(%rbp)
movl -0x2c(%rbp), %eax
jmp 0x2aa5
testq %r15, %r15
je 0x2a68
movq %r15, -0x38(%rbp)
movq %r13, %rdi
leaq 0x839(%rip), %rsi       # 0x3212
callq 0x2140
movq %rax, %rdi
movl -0x2c(%rbp), %esi
callq 0x21b0
movq %rax, %rdi
leaq 0x790(%rip), %rsi       # 0x3183
callq 0x2140
movq %r13, %rdi
leaq 0x824(%rip), %rsi       # 0x3226
callq 0x2140
leaq -0x278(%rbp), %r15
movq %r15, %rdi
movq %r12, %rsi
pushq $0x30
popq %rdx
callq 0x23f0
movq %r13, %rdi
movq %r15, %rsi
callq 0x2130
movq %rax, %rdi
leaq 0x751(%rip), %rsi       # 0x3182
callq 0x2140
leaq -0x278(%rbp), %rdi
callq 0x20a0
leaq -0x472(%rbp), %rdi
pushq $0x1
popq %rsi
movq -0x38(%rbp), %rdx
movq -0x48(%rbp), %rcx
callq 0x21f0
movl -0x2c(%rbp), %eax
incl %eax
andl $0x1f, %eax
movb $0x1, %cl
movl %ecx, -0x38(%rbp)
jmp 0x2aa5
movq -0x48(%rbp), %rdi
callq 0x2150
movl -0x2c(%rbp), %r15d
incl %r15d
andl $0x1f, %r15d
movq %r13, %rdi
leaq 0x7a8(%rip), %rsi       # 0x322e
callq 0x2140
movq %rax, %rdi
callq 0x20b0
movq %rax, %rdi
callq 0x20b0
movl %r15d, %eax
movl $0x0, -0x38(%rbp)
movl %eax, -0x2c(%rbp)
movzbl %al, %esi
leaq -0x278(%rbp), %r15
movq %r15, %rdi
xorl %edx, %edx
callq 0x2d2e
movl $0x200, %edx            # imm = 0x200
movq %r12, %rdi
movq %r15, %rsi
callq 0x2100
movq %r13, %rdi
leaq 0x811(%rip), %rsi       # 0x32e7
callq 0x2140
movq %rax, %rdi
movl -0x2c(%rbp), %esi
callq 0x21b0
movq %rax, %rdi
callq 0x20b0
movq %rax, %rdi
callq 0x20b0
movl -0x30(%rbp), %r9d
movl $0x200, %edx            # imm = 0x200
movl %ebx, %edi
movq %r12, %rsi
xorl %ecx, %ecx
leaq -0x58(%rbp), %r8
callq 0x2050
cmpq $-0x1, %rax
movl -0x38(%rbp), %eax
jne 0x27c9
leaq 0x7db(%rip), %rdi       # 0x32fe
callq 0x2110
jmp 0x2c03
leaq 0x80e(%rip), %rsi       # 0x3342
leaq -0x78(%rbp), %rdi
leaq -0x278(%rbp), %rdx
callq 0x2c4e
movzwl -0x70(%rbp), %edx
leaq 0x7f3(%rip), %rsi       # 0x3342
leaq -0x278(%rbp), %rdi
pushq $0x2
popq %r8
xorl %ecx, %ecx
callq 0x2d8e
leaq -0x478(%rbp), %rdi
leaq -0x278(%rbp), %rsi
movl $0x200, %edx            # imm = 0x200
callq 0x2100
movq 0x2448(%rip), %rdi      # 0x4fc8
leaq 0x7d2(%rip), %rsi       # 0x3359
callq 0x2140
movq %rax, %rdi
callq 0x20b0
movl -0x30(%rbp), %r9d
leaq -0x478(%rbp), %rsi
leaq -0x58(%rbp), %r8
movl $0x200, %edx            # imm = 0x200
movl %ebx, %edi
xorl %ecx, %ecx
callq 0x2050
cmpq $-0x1, %rax
je 0x2c12
leaq -0x78(%rbp), %rdi
callq 0x20a0
addq $0x458, %rsp            # imm = 0x458
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x23f7(%rip), %rax      # 0x4fd0
movq (%rax), %rcx
leaq 0x5d1(%rip), %rdi       # 0x31b4
pushq $0x25
jmp 0x2bfa
movq 0x23e2(%rip), %rax      # 0x4fd0
movq (%rax), %rcx
leaq 0x58d(%rip), %rdi       # 0x3185
pushq $0x2e
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
movl %ebx, %edi
callq 0x2200
pushq $0x1
popq %rdi
callq 0x2170
leaq 0x6e5(%rip), %rdi       # 0x32fe
callq 0x2110
movl %ebx, %edi
callq 0x2200
jmp 0x2c0a
jmp 0x2c3a
movq %rax, %rbx
jmp 0x2c46
movq %rax, %rbx
leaq -0x278(%rbp), %rdi
jmp 0x2c41
movq %rax, %rbx
leaq -0x78(%rbp), %rdi
callq 0x20a0
movq %rbx, %rdi
callq 0x21c0