// File: adhipradhana[P]sliding-window-protocol/src/recvfile.cpp
// Function: main
int main(int argc, char *argv[]) {
    // Initialize socket variables
	int sock, socket_length, window_size;
    unsigned int port;
    socklen_t fromlen;
    struct sockaddr_in server;
    struct sockaddr_in from;

    // Initialize sliding window variables
    int laf, lfr;
    int buffer_size, max_buffer_size;
    char *buffer;
    char *fname;
    FILE *file;
    bool eot;

    // Initialize packet variables
    unsigned int seq_num;
    bool is_check_sum_valid;
    size_t data_length;
    char ack[ACK_LENGTH];
    char packet[MAX_PACKET_LENGTH];
    char data[MAX_DATA_LENGTH];

    // Read data
    if (argc != 5) {
    	cerr << "usage: ./recvfile <filename> <window_size> <buffer_size> <port>" << endl;
    	exit(1);
    }
    fname = argv[1];
    window_size = atoi(argv[2]);
    max_buffer_size = (unsigned int)1024 * atoi(argv[3]);
    port = atoi(argv[4]);

    // Create socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
    	cerr << "Error creating socket" << endl;
    	exit(1);
    }

    // Assign server port and address
    socket_length = sizeof(server);
    bzero(&server, socket_length);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);

    // Bind socket
    if (bind(sock, (struct sockaddr *) &server, socket_length) < 0) {
        cerr << "Can't bind to address" << endl;
        exit(1);
    }

    // Write to file
    file = fopen(fname, "wb");

    bool recv_done = false;
    while (!recv_done) {
        buffer = new char[max_buffer_size];
        buffer_size = max_buffer_size;

        int seq_count = max_buffer_size / MAX_DATA_LENGTH;
        bool packet_received[window_size];
        for (int i = 0; i < window_size; i++) {
            packet_received[i] = false;
        }

        lfr = -1;
        laf = lfr + window_size;

        fromlen = sizeof(struct sockaddr_in);
        while (true) {
        	// Block receiving message
            int packet_size = recvfrom(sock, packet, MAX_PACKET_LENGTH, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
            if (packet_size < 0) {
                cout << "Error on receiving message\n";
                // exit(1);
            }

            // Get packet
            read_packet(packet, &seq_num, &data_length, data, &is_check_sum_valid, &eot);

            // Create ack
            create_ack(ack, seq_num, is_check_sum_valid);

            // Send ack
            int ack_size = sendto(sock, ack, ACK_LENGTH, MSG_WAITALL, (struct sockaddr *)&from, fromlen);
            if (ack_size < 0) {
                cout << "Fail sending ack\n";
            }

            if (seq_num <= laf) {
                
                if (is_check_sum_valid) {
                    int buffer_shift = seq_num * MAX_DATA_LENGTH;

                    if (seq_num == lfr + 1) {
                        memcpy(buffer + buffer_shift, data, data_length);
                        unsigned int shift = 1;
                        for (unsigned int i = 1; i < window_size; i++) {
                            if (!packet_received[i]) {
                                break;
                            }
                            shift++;
                        }
                        cout << "shift packet_received\n";
                        for (unsigned int i = 0; i < window_size - shift; i++) {
                            packet_received[i] = packet_received[i + shift];
                        }
                        cout << "false-in packet_received\n";
                        for (unsigned int i = window_size - shift; i < window_size; i++) {
                            packet_received[i] = false;
                        }
                        lfr += shift;
                        laf = lfr + window_size;
                    } else if (seq_num > lfr + 1) {
                        if (!packet_received[seq_num - (lfr + 1)]) {
                            memcpy(buffer + buffer_shift, data, data_length);
                            packet_received[seq_num - (lfr + 1)] = true;

                        }
                    }

                    if (eot) {
                        buffer_size = buffer_shift + data_length;
                        seq_count = seq_num + 1;
                        recv_done = true;
                        cout << "Receive packet eot " << seq_num << endl;
                        cout << "Sending ack eot " << seq_num << endl;
                    } else {
                        cout << "Receive packet  " << seq_num << endl;
                        cout << "Sending ack  " << seq_num << endl;
                    }
                } else {
                    cout << "ERROR packet " << seq_num << endl;
                    cout << "Sending NAK : " << seq_num << endl;
                }
            } else {
                // Send negative ack
                cout << "lfr : " << lfr << " laf : " << laf << "\n";
                cout << "SeqNum out of range : " << seq_num << endl;
            }

            if (lfr >= seq_count - 1) {
                break;
            }
        }

        fwrite(buffer, 1, buffer_size, file);
    }

    fclose(file);
    return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x8e0, %rsp            # imm = 0x8E0
movl $0x0, -0x4(%rbp)
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
cmpl $0x5, -0x8(%rbp)
je 0x168b
movq 0x298a(%rip), %rdi      # 0x3ff0
leaq 0x997(%rip), %rsi       # 0x2004
callq 0x10d0
movq %rax, %rdi
movq 0x2944(%rip), %rsi      # 0x3fc0
callq 0x10f0
movl $0x1, %edi
callq 0x1110
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x68(%rbp)
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rdi
callq 0x1150
movl %eax, -0x1c(%rbp)
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rdi
callq 0x1150
shll $0xa, %eax
movl %eax, -0x54(%rbp)
movq -0x10(%rbp), %rax
movq 0x20(%rax), %rdi
callq 0x1150
movl %eax, -0x20(%rbp)
movl $0x2, %esi
xorl %edx, %edx
movl %esi, %edi
callq 0x1070
movl %eax, -0x14(%rbp)
cmpl $0x0, -0x14(%rbp)
jge 0x170d
movq 0x2908(%rip), %rdi      # 0x3ff0
leaq 0x955(%rip), %rsi       # 0x2044
callq 0x10d0
movq %rax, %rdi
movq 0x28c2(%rip), %rsi      # 0x3fc0
callq 0x10f0
movl $0x1, %edi
callq 0x1110
movl $0x10, -0x18(%rbp)
movslq -0x18(%rbp), %rdx
leaq -0x34(%rbp), %rdi
xorl %esi, %esi
callq 0x1080
movw $0x2, -0x34(%rbp)
movl $0x0, -0x30(%rbp)
movl -0x20(%rbp), %eax
movzwl %ax, %edi
callq 0x1040
movw %ax, -0x32(%rbp)
movl -0x14(%rbp), %edi
movl -0x18(%rbp), %edx
leaq -0x34(%rbp), %rsi
callq 0x1060
cmpl $0x0, %eax
jge 0x177f
movq 0x2896(%rip), %rdi      # 0x3ff0
leaq 0x8f9(%rip), %rsi       # 0x205a
callq 0x10d0
movq %rax, %rdi
movq 0x2850(%rip), %rsi      # 0x3fc0
callq 0x10f0
movl $0x1, %edi
callq 0x1110
movq -0x68(%rbp), %rdi
leaq 0x8e6(%rip), %rsi       # 0x2070
callq 0x1100
movq %rax, -0x70(%rbp)
movb $0x0, -0x8a1(%rbp)
movb -0x8a1(%rbp), %al
xorb $-0x1, %al
testb $0x1, %al
jne 0x17ab
jmp 0x1ce3
movslq -0x54(%rbp), %rdi
callq 0x1030
movq %rax, -0x60(%rbp)
movl -0x54(%rbp), %eax
movl %eax, -0x50(%rbp)
movl -0x54(%rbp), %eax
movl %eax, %ecx
addl $0x3ff, %ecx            # imm = 0x3FF
testl %eax, %eax
cmovsl %ecx, %eax
sarl $0xa, %eax
movl %eax, -0x8a8(%rbp)
movl -0x1c(%rbp), %eax
movq %rsp, %rcx
movq %rcx, -0x8b0(%rbp)
movq %rax, %rdx
addq $0xf, %rdx
andq $-0x10, %rdx
movq %rsp, %rcx
subq %rdx, %rcx
movq %rcx, -0x8e0(%rbp)
movq %rcx, %rsp
movq %rax, -0x8b8(%rbp)
movl $0x0, -0x8bc(%rbp)
movl -0x8bc(%rbp), %eax
cmpl -0x1c(%rbp), %eax
jge 0x183e
movq -0x8e0(%rbp), %rax
movslq -0x8bc(%rbp), %rcx
movb $0x0, (%rax,%rcx)
movl -0x8bc(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8bc(%rbp)
jmp 0x1810
movl $0xffffffff, -0x4c(%rbp) # imm = 0xFFFFFFFF
movl -0x4c(%rbp), %eax
addl -0x1c(%rbp), %eax
movl %eax, -0x48(%rbp)
movl $0x10, -0x24(%rbp)
jmp 0x1857
movl -0x14(%rbp), %edi
leaq -0x4a0(%rbp), %rsi
movl $0x40a, %edx            # imm = 0x40A
movl $0x100, %ecx            # imm = 0x100
leaq -0x44(%rbp), %r8
leaq -0x24(%rbp), %r9
callq 0x10a0
movl %eax, -0x8c0(%rbp)
cmpl $0x0, -0x8c0(%rbp)
jge 0x189a
movq 0x2742(%rip), %rdi      # 0x3fd0
leaq 0x7de(%rip), %rsi       # 0x2073
callq 0x10d0
leaq -0x4a0(%rbp), %rdi
leaq -0x8a0(%rbp), %rcx
leaq -0x78(%rbp), %rsi
leaq -0x88(%rbp), %rdx
leaq -0x79(%rbp), %r8
leaq -0x71(%rbp), %r9
callq 0x1480
leaq -0x8e(%rbp), %rdi
movl -0x78(%rbp), %esi
movb -0x79(%rbp), %al
andb $0x1, %al
movzbl %al, %edx
callq 0x1550
movl -0x14(%rbp), %edi
leaq -0x8e(%rbp), %rsi
movl -0x24(%rbp), %r9d
movl $0x6, %edx
movl $0x100, %ecx            # imm = 0x100
leaq -0x44(%rbp), %r8
callq 0x1050
movl %eax, -0x8c4(%rbp)
cmpl $0x0, -0x8c4(%rbp)
jge 0x191a
movq 0x26c2(%rip), %rdi      # 0x3fd0
leaq 0x77a(%rip), %rsi       # 0x208f
callq 0x10d0
movl -0x78(%rbp), %eax
cmpl -0x48(%rbp), %eax
ja 0x1c33
testb $0x1, -0x79(%rbp)
je 0x1bd7
movl -0x78(%rbp), %eax
shll $0xa, %eax
movl %eax, -0x8c8(%rbp)
movl -0x78(%rbp), %eax
movl -0x4c(%rbp), %ecx
addl $0x1, %ecx
cmpl %ecx, %eax
jne 0x1a8d
movq -0x60(%rbp), %rdi
movslq -0x8c8(%rbp), %rax
addq %rax, %rdi
leaq -0x8a0(%rbp), %rsi
movq -0x88(%rbp), %rdx
callq 0x10b0
movl $0x1, -0x8cc(%rbp)
movl $0x1, -0x8d0(%rbp)
movl -0x8d0(%rbp), %eax
cmpl -0x1c(%rbp), %eax
jae 0x19c2
movq -0x8e0(%rbp), %rax
movl -0x8d0(%rbp), %ecx
testb $0x1, (%rax,%rcx)
jne 0x19a2
jmp 0x19c2
movl -0x8cc(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8cc(%rbp)
movl -0x8d0(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8d0(%rbp)
jmp 0x1982
movq 0x2607(%rip), %rdi      # 0x3fd0
leaq 0x6d1(%rip), %rsi       # 0x20a1
callq 0x10d0
movl $0x0, -0x8d4(%rbp)
movl -0x8d4(%rbp), %eax
movl -0x1c(%rbp), %ecx
subl -0x8cc(%rbp), %ecx
cmpl %ecx, %eax
jae 0x1a27
movq -0x8e0(%rbp), %rax
movl -0x8d4(%rbp), %ecx
addl -0x8cc(%rbp), %ecx
movl %ecx, %ecx
movb (%rax,%rcx), %dl
movl -0x8d4(%rbp), %ecx
andb $0x1, %dl
movb %dl, (%rax,%rcx)
movl -0x8d4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8d4(%rbp)
jmp 0x19df
movq 0x25a2(%rip), %rdi      # 0x3fd0
leaq 0x683(%rip), %rsi       # 0x20b8
callq 0x10d0
movl -0x1c(%rbp), %eax
subl -0x8cc(%rbp), %eax
movl %eax, -0x8d8(%rbp)
movl -0x8d8(%rbp), %eax
cmpl -0x1c(%rbp), %eax
jae 0x1a76
movq -0x8e0(%rbp), %rax
movl -0x8d8(%rbp), %ecx
movb $0x0, (%rax,%rcx)
movl -0x8d8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8d8(%rbp)
jmp 0x1a49
movl -0x8cc(%rbp), %eax
addl -0x4c(%rbp), %eax
movl %eax, -0x4c(%rbp)
movl -0x4c(%rbp), %eax
addl -0x1c(%rbp), %eax
movl %eax, -0x48(%rbp)
jmp 0x1af1
movl -0x78(%rbp), %eax
movl -0x4c(%rbp), %ecx
addl $0x1, %ecx
cmpl %ecx, %eax
jbe 0x1aef
movq -0x8e0(%rbp), %rax
movl -0x78(%rbp), %ecx
movl -0x4c(%rbp), %edx
addl $0x1, %edx
subl %edx, %ecx
movl %ecx, %ecx
testb $0x1, (%rax,%rcx)
jne 0x1aed
movq -0x60(%rbp), %rdi
movslq -0x8c8(%rbp), %rax
addq %rax, %rdi
leaq -0x8a0(%rbp), %rsi
movq -0x88(%rbp), %rdx
callq 0x10b0
movq -0x8e0(%rbp), %rax
movl -0x78(%rbp), %ecx
movl -0x4c(%rbp), %edx
addl $0x1, %edx
subl %edx, %ecx
movl %ecx, %ecx
movb $0x1, (%rax,%rcx)
jmp 0x1aef
jmp 0x1af1
testb $0x1, -0x71(%rbp)
je 0x1b7b
movslq -0x8c8(%rbp), %rax
addq -0x88(%rbp), %rax
movl %eax, -0x50(%rbp)
movl -0x78(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x8a8(%rbp)
movb $0x1, -0x8a1(%rbp)
movq 0x24aa(%rip), %rdi      # 0x3fd0
leaq 0x5a5(%rip), %rsi       # 0x20d2
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x2479(%rip), %rsi      # 0x3fc0
callq 0x10f0
movq 0x247d(%rip), %rdi      # 0x3fd0
leaq 0x58c(%rip), %rsi       # 0x20e6
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x244c(%rip), %rsi      # 0x3fc0
callq 0x10f0
jmp 0x1bd5
movq 0x244e(%rip), %rdi      # 0x3fd0
leaq 0x56e(%rip), %rsi       # 0x20f7
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x241d(%rip), %rsi      # 0x3fc0
callq 0x10f0
movq 0x2421(%rip), %rdi      # 0x3fd0
leaq 0x552(%rip), %rsi       # 0x2108
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x23f0(%rip), %rsi      # 0x3fc0
callq 0x10f0
jmp 0x1c31
movq 0x23f2(%rip), %rdi      # 0x3fd0
leaq 0x531(%rip), %rsi       # 0x2116
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x23c1(%rip), %rsi      # 0x3fc0
callq 0x10f0
movq 0x23c5(%rip), %rdi      # 0x3fd0
leaq 0x512(%rip), %rsi       # 0x2124
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x2394(%rip), %rsi      # 0x3fc0
callq 0x10f0
jmp 0x1ca7
movq 0x2396(%rip), %rdi      # 0x3fd0
leaq 0x4f2(%rip), %rsi       # 0x2133
callq 0x10d0
movq %rax, %rdi
movl -0x4c(%rbp), %esi
callq 0x1160
movq %rax, %rdi
leaq 0x4df(%rip), %rsi       # 0x213a
callq 0x10d0
movq %rax, %rdi
movl -0x48(%rbp), %esi
callq 0x1160
movq %rax, %rdi
leaq 0x45b(%rip), %rsi       # 0x20d0
callq 0x10d0
movq 0x234f(%rip), %rdi      # 0x3fd0
leaq 0x4ba(%rip), %rsi       # 0x2142
callq 0x10d0
movq %rax, %rdi
movl -0x78(%rbp), %esi
callq 0x1130
movq %rax, %rdi
movq 0x231e(%rip), %rsi      # 0x3fc0
callq 0x10f0
movl -0x4c(%rbp), %eax
movl -0x8a8(%rbp), %ecx
subl $0x1, %ecx
cmpl %ecx, %eax
jl 0x1cb9
jmp 0x1cbe
jmp 0x1855
movq -0x60(%rbp), %rdi
movslq -0x50(%rbp), %rdx
movq -0x70(%rbp), %rcx
movl $0x1, %esi
callq 0x1170
movq -0x8b0(%rbp), %rax
movq %rax, %rsp
jmp 0x179a
movq -0x70(%rbp), %rdi
callq 0x10e0
xorl %eax, %eax
movq %rbp, %rsp
popq %rbp
retq