// File: 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
subq $0x910, %rsp            # imm = 0x910
movq %rdx, -0x10(%rbp)
movq %rcx, -0x8(%rbp)
movl %edi, -0x14(%rbp)
movq %rsi, -0x20(%rbp)
movl $0x10, -0x24(%rbp)
leaq -0x224(%rbp), %rdi
callq 0x3800
movl $0x0, -0x22c(%rbp)
movb $0x0, -0x22d(%rbp)
movb $0x0, -0x22e(%rbp)
movq -0x20(%rbp), %rdi
leaq 0x151e(%rip), %rsi      # 0x415e
callq 0x2230
movq %rax, -0x238(%rbp)
movb $0x1, -0x239(%rbp)
movl $0x0, -0x240(%rbp)
testb $0x1, -0x239(%rbp)
je 0x328d
movl -0x14(%rbp), %edi
leaq -0x224(%rbp), %rsi
movl $0x200, %edx            # imm = 0x200
xorl %ecx, %ecx
leaq -0x10(%rbp), %r8
leaq -0x24(%rbp), %r9
callq 0x2150
cmpq $-0x1, %rax
jne 0x2d2b
callq 0x2030
cmpl $0xb, (%rax)
jne 0x2d01
callq 0x2030
movl $0x0, (%rax)
movq 0x331a(%rip), %rdi      # 0x5fc8
leaq 0x14ac(%rip), %rsi      # 0x4161
callq 0x21c0
movl -0x240(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x240(%rbp)
cmpl $0xf, -0x240(%rbp)
jle 0x2cfc
movq 0x32f7(%rip), %rax      # 0x5fd0
movq (%rax), %rdi
leaq 0x14a2(%rip), %rsi      # 0x4185
movb $0x0, %al
callq 0x2270
movl -0x14(%rbp), %edi
callq 0x2320
movl $0x1, %edi
callq 0x2240
jmp 0x2c5d
movq 0x32c8(%rip), %rax      # 0x5fd0
movq (%rax), %rdi
leaq 0x14a2(%rip), %rsi      # 0x41b4
movb $0x0, %al
callq 0x2270
movl -0x14(%rbp), %edi
callq 0x2320
movl $0x1, %edi
callq 0x2240
movl $0x0, -0x240(%rbp)
leaq -0x224(%rbp), %rdi
movq %rdi, -0x8c0(%rbp)
callq 0x3510
movq -0x8c0(%rbp), %rdi
movb %al, -0x241(%rbp)
callq 0x3520
movq -0x8c0(%rbp), %rdi
movb %al, -0x242(%rbp)
callq 0x3540
movq -0x8c0(%rbp), %rdi
movw %ax, -0x244(%rbp)
callq 0x3440
movq -0x8c0(%rbp), %rdi
movw %ax, -0x246(%rbp)
callq 0x3420
movq %rax, -0x250(%rbp)
movzbl -0x242(%rbp), %eax
movl %eax, -0x228(%rbp)
movb $0x0, -0x22d(%rbp)
movb $0x0, -0x22e(%rbp)
movzbl -0x241(%rbp), %eax
movq %rax, -0x8b8(%rbp)
subq $0x3, %rax
ja 0x30f4
movq -0x8b8(%rbp), %rax
leaq 0x122a(%rip), %rcx      # 0x4004
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x31de(%rip), %rdi      # 0x5fc8
leaq 0x13ed(%rip), %rsi      # 0x41de
callq 0x21c0
movq %rax, %rdi
movq 0x31b8(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x31a9(%rip), %rsi      # 0x5fb8
callq 0x21e0
jmp 0x3102
movq 0x31a8(%rip), %rdi      # 0x5fc8
leaq 0x13b3(%rip), %rsi      # 0x41da
callq 0x21c0
movq %rax, %rdi
movq 0x3182(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x3173(%rip), %rsi      # 0x5fb8
callq 0x21e0
jmp 0x3102
movq 0x3172(%rip), %rdi      # 0x5fc8
leaq 0x139f(%rip), %rsi      # 0x41fc
callq 0x21c0
movq %rax, %rdi
movq 0x314c(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x313d(%rip), %rsi      # 0x5fb8
callq 0x21e0
jmp 0x3102
movl -0x22c(%rbp), %eax
cmpl -0x228(%rbp), %eax
jne 0x309d
movzwl -0x244(%rbp), %eax
movl %eax, -0x8c4(%rbp)
leaq -0x224(%rbp), %rdi
callq 0x3990
movl %eax, %ecx
movl -0x8c4(%rbp), %eax
cmpl %ecx, %eax
jne 0x3046
movzwl -0x246(%rbp), %eax
cmpl $0x0, %eax
jle 0x2fd6
movq 0x30f1(%rip), %rdi      # 0x5fc8
movq %rdi, -0x8e0(%rbp)
leaq 0x132d(%rip), %rsi      # 0x4212
callq 0x21c0
movq %rax, %rdi
movl -0x228(%rbp), %esi
callq 0x22c0
movq %rax, %rdi
leaq 0x1281(%rip), %rsi      # 0x4183
callq 0x21c0
movq -0x8e0(%rbp), %rdi
leaq 0x1311(%rip), %rsi      # 0x4226
callq 0x21c0
leaq -0x270(%rbp), %rdi
movq %rdi, -0x8d8(%rbp)
leaq -0x224(%rbp), %rsi
movl $0x30, %edx
callq 0x2650
movq -0x8e0(%rbp), %rdi
movq -0x8d8(%rbp), %rsi
callq 0x21b0
movq %rax, -0x8d0(%rbp)
jmp 0x2f55
movq -0x8d0(%rbp), %rdi
leaq 0x121f(%rip), %rsi      # 0x4182
callq 0x21c0
jmp 0x2f6a
leaq -0x270(%rbp), %rdi
callq 0x20f0
movq -0x250(%rbp), %rdi
movzwl -0x246(%rbp), %eax
movl %eax, %edx
movq -0x238(%rbp), %rcx
movl $0x1, %esi
callq 0x2310
movl -0x22c(%rbp), %eax
addl $0x1, %eax
movl $0x20, %ecx
cltd
idivl %ecx
movl %edx, -0x22c(%rbp)
jmp 0x303d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x278(%rbp)
movl %eax, -0x27c(%rbp)
leaq -0x270(%rbp), %rdi
callq 0x20f0
jmp 0x340b
movq -0x238(%rbp), %rdi
callq 0x21d0
movl -0x22c(%rbp), %eax
addl $0x1, %eax
movl $0x20, %ecx
cltd
idivl %ecx
movl %edx, -0x22c(%rbp)
movq 0x2fc8(%rip), %rdi      # 0x5fc8
leaq 0x1227(%rip), %rsi      # 0x422e
callq 0x21c0
movq %rax, %rdi
movq 0x2fa2(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x2f93(%rip), %rsi      # 0x5fb8
callq 0x21e0
movb $0x0, -0x239(%rbp)
movb $0x1, -0x22d(%rbp)
jmp 0x3102
movb $0x1, -0x22d(%rbp)
jmp 0x309b
movq 0x2f7b(%rip), %rdi      # 0x5fc8
leaq 0x1215(%rip), %rsi      # 0x4269
callq 0x21c0
movq %rax, %rdi
movl -0x228(%rbp), %esi
callq 0x22c0
movq %rax, %rdi
leaq 0x120b(%rip), %rsi      # 0x427c
callq 0x21c0
movq %rax, %rdi
movq 0x2f38(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x2f29(%rip), %rsi      # 0x5fb8
callq 0x21e0
movb $0x1, -0x22e(%rbp)
jmp 0x30f2
movq 0x2f24(%rip), %rdi      # 0x5fc8
leaq 0x11e2(%rip), %rsi      # 0x428d
callq 0x21c0
movq %rax, %rdi
movl -0x228(%rbp), %esi
callq 0x22c0
movq %rax, %rdi
leaq 0x11dd(%rip), %rsi      # 0x42a5
callq 0x21c0
movq %rax, %rdi
movq 0x2ee1(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x2ed2(%rip), %rsi      # 0x5fb8
callq 0x21e0
movb $0x1, -0x22d(%rbp)
jmp 0x3102
leaq 0x11c6(%rip), %rdi      # 0x42c1
movb $0x0, %al
callq 0x2040
testb $0x1, -0x22d(%rbp)
je 0x31c4
movl -0x22c(%rbp), %eax
leaq -0x47c(%rbp), %rdi
xorl %edx, %edx
movzbl %al, %esi
callq 0x3810
leaq -0x224(%rbp), %rdi
leaq -0x47c(%rbp), %rsi
movl $0x200, %edx            # imm = 0x200
callq 0x2170
movq 0x2e83(%rip), %rdi      # 0x5fc8
leaq 0x119b(%rip), %rsi      # 0x42e7
callq 0x21c0
movq %rax, %rdi
movl -0x22c(%rbp), %esi
callq 0x22c0
movq %rax, %rdi
movq 0x2e4f(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x2e40(%rip), %rsi      # 0x5fb8
callq 0x21e0
movl -0x14(%rbp), %edi
leaq -0x224(%rbp), %rsi
movl -0x24(%rbp), %r9d
movl $0x200, %edx            # imm = 0x200
xorl %ecx, %ecx
leaq -0x10(%rbp), %r8
callq 0x2060
cmpq $-0x1, %rax
jne 0x31bf
leaq 0x1156(%rip), %rdi      # 0x42fe
callq 0x2180
movl -0x14(%rbp), %edi
callq 0x2320
movl $0x1, %edi
callq 0x2240
jmp 0x3288
testb $0x1, -0x22e(%rbp)
je 0x3286
movl -0x22c(%rbp), %eax
leaq -0x67c(%rbp), %rdi
movl $0x1, %edx
movzbl %al, %esi
callq 0x3810
leaq -0x224(%rbp), %rdi
leaq -0x67c(%rbp), %rsi
movl $0x200, %edx            # imm = 0x200
callq 0x2170
movq 0x2dbe(%rip), %rdi      # 0x5fc8
leaq 0x111a(%rip), %rsi      # 0x432b
callq 0x21c0
movq %rax, %rdi
movl -0x22c(%rbp), %esi
callq 0x22c0
movq %rax, %rdi
movq 0x2d8a(%rip), %rsi      # 0x5fb8
callq 0x21e0
movq %rax, %rdi
movq 0x2d7b(%rip), %rsi      # 0x5fb8
callq 0x21e0
movl -0x14(%rbp), %edi
leaq -0x224(%rbp), %rsi
movl -0x24(%rbp), %r9d
movl $0x200, %edx            # imm = 0x200
xorl %ecx, %ecx
leaq -0x10(%rbp), %r8
callq 0x2060
cmpq $-0x1, %rax
jne 0x3284
leaq 0x1091(%rip), %rdi      # 0x42fe
callq 0x2180
movl -0x14(%rbp), %edi
callq 0x2320
movl $0x1, %edi
callq 0x2240
jmp 0x3286
jmp 0x3288
jmp 0x2c5d
leaq 0x10ae(%rip), %rax      # 0x4342
movq %rax, -0x8f0(%rbp)
movq %rax, -0x688(%rbp)
leaq -0x6a9(%rbp), %rdi
movq %rdi, -0x8e8(%rbp)
callq 0x22e0
movq -0x8f0(%rbp), %rsi
movq -0x8e8(%rbp), %rdx
leaq -0x6a8(%rbp), %rdi
callq 0x3460
jmp 0x32d1
leaq -0x6a9(%rbp), %rdi
callq 0x2200
movq -0x688(%rbp), %rax
movq %rax, -0x8f8(%rbp)
leaq -0x6a8(%rbp), %rdi
callq 0x2110
movq -0x8f8(%rbp), %rsi
movzwl %ax, %edx
leaq -0x8a9(%rbp), %rdi
xorl %ecx, %ecx
movl $0x2, %r8d
callq 0x38e0
jmp 0x3317
leaq -0x224(%rbp), %rdi
leaq -0x8a9(%rbp), %rsi
movl $0x200, %edx            # imm = 0x200
callq 0x2170
movq 0x2c92(%rip), %rdi      # 0x5fc8
leaq 0x101c(%rip), %rsi      # 0x4359
callq 0x21c0
movq %rax, -0x900(%rbp)
jmp 0x334b
movq -0x900(%rbp), %rdi
movq 0x2c5f(%rip), %rsi      # 0x5fb8
callq 0x21e0
jmp 0x3360
movl -0x14(%rbp), %edi
movl -0x24(%rbp), %r9d
leaq -0x224(%rbp), %rsi
movl $0x200, %edx            # imm = 0x200
xorl %ecx, %ecx
leaq -0x10(%rbp), %r8
callq 0x2060
movq %rax, -0x908(%rbp)
jmp 0x3387
movq -0x908(%rbp), %rax
cmpq $-0x1, %rax
jne 0x33f6
leaq 0xf63(%rip), %rdi       # 0x42fe
callq 0x2180
jmp 0x33a2
movl -0x14(%rbp), %edi
callq 0x2320
jmp 0x33ac
movl $0x1, %edi
callq 0x2240
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x278(%rbp)
movl %eax, -0x27c(%rbp)
leaq -0x6a9(%rbp), %rdi
callq 0x2200
jmp 0x340b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x278(%rbp)
movl %eax, -0x27c(%rbp)
leaq -0x6a8(%rbp), %rdi
callq 0x20f0
jmp 0x340b
leaq -0x6a8(%rbp), %rdi
callq 0x20f0
addq $0x910, %rsp            # imm = 0x910
popq %rbp
retq
movq -0x278(%rbp), %rdi
callq 0x22d0
nopw (%rax,%rax)