// File: renzibei[P]TSProxy/proxyclient.cpp
// Function: readAndHandleFromServer(int, std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<int const, int>>>&, ClientContext*, fd_set&, int&)
int readAndHandleFromServer(int serverFd, LocalFdMap &localFdMap, ClientContext *ctx, fd_set &master_set, int& max_sd) {
    static char readBuf[constants::MAX_MSG_DATA_LENGTH];
    int tempLocalFd = 0;
    uint8_t msgType = 0;
    // ssize_t recvCode = recvReplyMsg(serverFd, (uint8_t*)readBuf, sizeof(readBuf), ctx, &tempLocalFd);
    ssize_t recvCode = recvMessage(serverFd, (uint8_t*)readBuf, sizeof(readBuf), ctx, &msgType);
    if (recvCode < 0) {
        LogHelper::log(Error, "fail to recv msg from the server");
        return recvCode;
    }
    if (msgType == SocksTrafficReplyMsg) {
        ntoh_copy4bytes(&tempLocalFd, readBuf);
        if (localFdMap.find(tempLocalFd) == localFdMap.end()) {
            LogHelper::log(Warn, "Failed to recv traffic reply, localFd :%d not in localFdMap", tempLocalFd);
            return -1;
        }
        if (localFdMap[tempLocalFd] != LocalFdPrepared) {
            LogHelper::log(Warn, "Failed to recv traffic reply, localFd status: %d, not prepared", localFdMap[tempLocalFd]);
            return -2;
        }
        ssize_t sendCode = writeNBytes(tempLocalFd, readBuf + sizeof(tempLocalFd), recvCode - sizeof(tempLocalFd), constants::clientNonBlocking);
        if (sendCode < 0) {
            LogHelper::log(Warn, "Fail to send data to localFd :%d, close it", tempLocalFd);
            close(tempLocalFd);
            FD_CLR(tempLocalFd, &master_set);
            if (tempLocalFd == max_sd) {
                while (FD_ISSET(max_sd, &master_set) == 0)
                    max_sd -= 1;
            }
            localFdMap.erase(tempLocalFd);
            return sendCode;
        }
    }
    else if (msgType == SocksTcpReplyMsg) {
        // static uint8_t socksBuffer[sizeof(SocksTcpReply)];
        // memset(socksBuffer, 0, totalLen + 1 + sizeof(localFd));
        // TODO: modify
        // int recvLen = recvMessage(serverFd, socksBuffer, sizeof(socksBuffer), ctx, 7);
        // if (recvLen < 0) {
        //     LogHelper::log(Error, "Fail to recv socks handshake forth reply from server");
        //     return recvLen;
        // }
        int recvLen = recvCode;
        if (recvLen < 2) {
            LogHelper::log(Error, "Received len less than header in socks handshake forth reply from server");
            return -3;
        }
        uint8_t respCode = readBuf[0];

        int localFd = 0;
        static uint8_t addrBuffer[constants::SocksDomainMaxLength + 2];
        memset(addrBuffer, 0, sizeof(addrBuffer));
        memcpy(addrBuffer, readBuf + 1, recvLen - 1 - sizeof(localFd));
        
        ntoh_copy4bytes(&localFd, readBuf + recvLen - sizeof(localFd));
        // if (localFd != localFd) {
        //     LogHelper::log(Error, "receive localFd not equals the localFd, server localFd:%d, localFd: %d", localFd, localFd);
        //     return -4;
        // }
        if (localFdMap.find(localFd) == localFdMap.end()) {
            LogHelper::log(Warn, "Failed in forth socks handshake from server, cannot find localFd: %d in localFdMap", localFd);
            return -4;
        }
        if (localFdMap[localFd] != LocalFdWaiting) {
            LogHelper::log(Warn, "Failed in forth socks handshake, localFd %d status is not waiting but %d", localFd, localFdMap[localFd]);
            return -5;
        }
        localFdMap[localFd] = LocalFdPrepared;
        memset(readBuf, 0, recvLen);
        readBuf[0] = constants::SocksVersion;
        readBuf[1] = respCode;
        readBuf[2] = 0x00;
        memcpy(readBuf + 3, addrBuffer, recvLen - 1 - sizeof(localFd));
        int sendCode = writeNBytes(localFd, readBuf, 3 + recvLen - 1 - sizeof(localFd), constants::clientNonBlocking);
        if (sendCode < 0) {
            LogHelper::log(Warn, "Fail in socks forth handshake, couldn't send reply to localFd: %d", localFd);
            return sendCode;
        }
        LogHelper::log(Debug, "client socks handshake end");
    }
    else {
        LogHelper::log(Warn, "Unknown msgType: %d", msgType);
        return -2;
    }
    return 0;
}

// Assembly:
subq $0xa8, %rsp
movl %edi, 0xa0(%rsp)
movq %rsi, 0x98(%rsp)
movq %rdx, 0x90(%rsp)
movq %rcx, 0x88(%rsp)
movq %r8, 0x80(%rsp)
movl $0x0, 0x7c(%rsp)
movb $0x0, 0x7b(%rsp)
movl 0xa0(%rsp), %edi
movq 0x90(%rsp), %rcx
leaq 0x6d97f(%rip), %rsi     # 0x71450
movl $0xffec, %edx           # imm = 0xFFEC
leaq 0x7b(%rsp), %r8
callq 0x2fb0
movq %rax, 0x70(%rsp)
cmpq $0x0, 0x70(%rsp)
jge 0x3b0e
xorl %edi, %edi
leaq 0x7bee(%rip), %rsi      # 0xb6e4
movb $0x0, %al
callq 0x2a50
movq 0x70(%rsp), %rax
movl %eax, 0xa4(%rsp)
jmp 0x3f9d
movzbl 0x7b(%rsp), %eax
cmpl $0x9, %eax
jne 0x3d39
movl 0x6d92e(%rip), %edi     # 0x71450
callq 0x22a0
movl %eax, 0x7c(%rsp)
movq 0x98(%rsp), %rdi
leaq 0x7c(%rsp), %rsi
callq 0x4f30
movq %rax, 0x68(%rsp)
movq 0x98(%rsp), %rdi
callq 0x4f60
movq %rax, 0x60(%rsp)
leaq 0x68(%rsp), %rdi
leaq 0x60(%rsp), %rsi
callq 0x4f10
testb $0x1, %al
jne 0x3b69
jmp 0x3b90
movl 0x7c(%rsp), %edx
movl $0x1, %edi
leaq 0x7b8c(%rip), %rsi      # 0xb705
movb $0x0, %al
callq 0x2a50
movl $0xffffffff, 0xa4(%rsp) # imm = 0xFFFFFFFF
jmp 0x3f9d
movq 0x98(%rsp), %rdi
leaq 0x7c(%rsp), %rsi
callq 0x4ee0
cmpl $0x2, (%rax)
je 0x3bde
movq 0x98(%rsp), %rdi
leaq 0x7c(%rsp), %rsi
callq 0x4ee0
movl (%rax), %edx
movl $0x1, %edi
leaq 0x7b7a(%rip), %rsi      # 0xb741
movb $0x0, %al
callq 0x2a50
movl $0xfffffffe, 0xa4(%rsp) # imm = 0xFFFFFFFE
jmp 0x3f9d
movl 0x7c(%rsp), %edi
movq 0x70(%rsp), %rdx
subq $0x4, %rdx
leaq 0x6d85e(%rip), %rsi     # 0x71450
addq $0x4, %rsi
movl $0x1, %ecx
callq 0x7f90
movq %rax, 0x58(%rsp)
cmpq $0x0, 0x58(%rsp)
jge 0x3d34
movl 0x7c(%rsp), %edx
movl $0x1, %edi
leaq 0x7b5f(%rip), %rsi      # 0xb780
movb $0x0, %al
callq 0x2a50
movl 0x7c(%rsp), %edi
callq 0x23e0
movl 0x7c(%rsp), %eax
movl $0x40, %ecx
cltd
idivl %ecx
movl %edx, %eax
movl %eax, %ecx
movl $0x1, %eax
shlq %cl, %rax
xorq $-0x1, %rax
movq %rax, 0x20(%rsp)
movq 0x88(%rsp), %rax
movq %rax, 0x28(%rsp)
movl 0x7c(%rsp), %eax
movl $0x40, %ecx
cltd
idivl %ecx
movq 0x20(%rsp), %rdx
movl %eax, %ecx
movq 0x28(%rsp), %rax
movslq %ecx, %rcx
andq (%rax,%rcx,8), %rdx
movq %rdx, (%rax,%rcx,8)
movl 0x7c(%rsp), %eax
movq 0x80(%rsp), %rcx
cmpl (%rcx), %eax
jne 0x3d11
jmp 0x3c94
movq 0x88(%rsp), %rax
movq %rax, 0x10(%rsp)
movq 0x80(%rsp), %rax
movl (%rax), %eax
movl $0x40, %ecx
cltd
idivl %ecx
movl %eax, %ecx
movq 0x10(%rsp), %rax
movslq %ecx, %rcx
movq (%rax,%rcx,8), %rax
movq %rax, 0x18(%rsp)
movq 0x80(%rsp), %rax
movl (%rax), %eax
movl $0x40, %ecx
cltd
idivl %ecx
movq 0x18(%rsp), %rax
movl %edx, %ecx
movl $0x1, %edx
shlq %cl, %rdx
movq %rdx, %rcx
andq %rcx, %rax
cmpq $0x0, %rax
setne %al
andb $0x1, %al
movzbl %al, %eax
cmpl $0x0, %eax
jne 0x3d0f
movq 0x80(%rsp), %rax
movl (%rax), %ecx
subl $0x1, %ecx
movl %ecx, (%rax)
jmp 0x3c94
jmp 0x3d11
movq 0x98(%rsp), %rdi
leaq 0x7c(%rsp), %rsi
callq 0x4f90
movq 0x58(%rsp), %rax
movl %eax, 0xa4(%rsp)
jmp 0x3f9d
jmp 0x3f92
movzbl 0x7b(%rsp), %eax
cmpl $0x7, %eax
jne 0x3f6b
movq 0x70(%rsp), %rax
movl %eax, 0x54(%rsp)
cmpl $0x2, 0x54(%rsp)
jge 0x3d77
xorl %edi, %edi
leaq 0x7a4b(%rip), %rsi      # 0xb7ab
movb $0x0, %al
callq 0x2a50
movl $0xfffffffd, 0xa4(%rsp) # imm = 0xFFFFFFFD
jmp 0x3f9d
movb 0x6d6d3(%rip), %al      # 0x71450
movb %al, 0x53(%rsp)
movl $0x0, 0x4c(%rsp)
leaq 0x7d6b0(%rip), %rdi     # 0x81440
xorl %esi, %esi
movl $0x101, %edx            # imm = 0x101
callq 0x2150
movl 0x54(%rsp), %eax
subl $0x1, %eax
movslq %eax, %rdx
subq $0x4, %rdx
leaq 0x7d68f(%rip), %rdi     # 0x81440
leaq 0x6d698(%rip), %rsi     # 0x71450
addq $0x1, %rsi
callq 0x21c0
movslq 0x54(%rsp), %rcx
leaq 0x6d683(%rip), %rax     # 0x71450
movl -0x4(%rax,%rcx), %edi
callq 0x22a0
movl %eax, 0x4c(%rsp)
movq 0x98(%rsp), %rdi
leaq 0x4c(%rsp), %rsi
callq 0x4f30
movq %rax, 0x40(%rsp)
movq 0x98(%rsp), %rdi
callq 0x4f60
movq %rax, 0x38(%rsp)
leaq 0x40(%rsp), %rdi
leaq 0x38(%rsp), %rsi
callq 0x4f10
testb $0x1, %al
jne 0x3e18
jmp 0x3e3f
movl 0x4c(%rsp), %edx
movl $0x1, %edi
leaq 0x79cc(%rip), %rsi      # 0xb7f4
movb $0x0, %al
callq 0x2a50
movl $0xfffffffc, 0xa4(%rsp) # imm = 0xFFFFFFFC
jmp 0x3f9d
movq 0x98(%rsp), %rdi
leaq 0x4c(%rsp), %rsi
callq 0x4ee0
cmpl $0x1, (%rax)
je 0x3e99
movl 0x4c(%rsp), %eax
movl %eax, 0xc(%rsp)
movq 0x98(%rsp), %rdi
leaq 0x4c(%rsp), %rsi
callq 0x4ee0
movl 0xc(%rsp), %edx
movl (%rax), %ecx
movl $0x1, %edi
leaq 0x79c5(%rip), %rsi      # 0xb847
movb $0x0, %al
callq 0x2a50
movl $0xfffffffb, 0xa4(%rsp) # imm = 0xFFFFFFFB
jmp 0x3f9d
movq 0x98(%rsp), %rdi
leaq 0x4c(%rsp), %rsi
callq 0x4ee0
movl $0x2, (%rax)
movslq 0x54(%rsp), %rdx
leaq 0x6d593(%rip), %rdi     # 0x71450
xorl %esi, %esi
callq 0x2150
movb $0x5, 0x6d585(%rip)     # 0x71450
movb 0x53(%rsp), %al
movb %al, 0x6d57c(%rip)      # 0x71451
movb $0x0, 0x6d576(%rip)     # 0x71452
movl 0x54(%rsp), %eax
subl $0x1, %eax
movslq %eax, %rdx
subq $0x4, %rdx
leaq 0x6d55f(%rip), %rdi     # 0x71450
addq $0x3, %rdi
leaq 0x7d544(%rip), %rsi     # 0x81440
callq 0x21c0
movl 0x4c(%rsp), %edi
movl 0x54(%rsp), %eax
addl $0x3, %eax
subl $0x1, %eax
movslq %eax, %rdx
subq $0x4, %rdx
leaq 0x6d533(%rip), %rsi     # 0x71450
movl $0x1, %ecx
callq 0x7f90
movl %eax, 0x34(%rsp)
cmpl $0x0, 0x34(%rsp)
jge 0x3f56
movl 0x4c(%rsp), %edx
movl $0x1, %edi
leaq 0x794e(%rip), %rsi      # 0xb890
movb $0x0, %al
callq 0x2a50
movl 0x34(%rsp), %eax
movl %eax, 0xa4(%rsp)
jmp 0x3f9d
movl $0x3, %edi
leaq 0x7970(%rip), %rsi      # 0xb8d2
movb $0x0, %al
callq 0x2a50
jmp 0x3f90
movzbl 0x7b(%rsp), %edx
movl $0x1, %edi
leaq 0x7971(%rip), %rsi      # 0xb8ed
movb $0x0, %al
callq 0x2a50
movl $0xfffffffe, 0xa4(%rsp) # imm = 0xFFFFFFFE
jmp 0x3f9d
jmp 0x3f92
movl $0x0, 0xa4(%rsp)
movl 0xa4(%rsp), %eax
addq $0xa8, %rsp
retq
nopl (%rax)