// File: /workspace/github_cpp_makefile_star2_O2/devanshdalal[P]cloud-computing-concepts-part-2/MP1Node.cpp
// Function: MP1Node::recvCallBack(void*, char*, int)
bool MP1Node::recvCallBack(void *env, char *data, int size)
{
    /*
	 * Your code goes here
	 */
    MessageHdr *msg = (MessageHdr *)data;
    const auto current_time = par->getcurrtime();
    // multicast the membership list.
    vector<MemberListEntry> &memberList = memberNode->memberList;
    switch (msg->msgType)
    {
    case JOINREQ: // this is the introducer.
    {
        AddNode(*(int *)(msg + 1));

        size_t msgsize = sizeof(MessageHdr) + memberList.size() * sizeof(int);
        // cout << "msgsize: " << sizeof(MessageHdr) << " " << sizeof(joinaddr->addr) << " " << sizeof(long) << " " << 1 << "\n";
        MessageHdr *message = (MessageHdr *)malloc(msgsize * sizeof(char));
        message->msgType = JOINREP;
        int *fields = (int *)(message + 1);
        for (int i = 0; i < memberList.size(); ++i)
        {
            fields[i] = memberList[i].getid();
        }
        // multicast to the group.
        PublishToAll(*message, msgsize);
    }
    break;

    case JOINREP:
    {
        // std::cout << "JOINREP " << this_node_ << "\n";
        int nodes = (size - sizeof(MessageHdr)) / sizeof(int);
        int *fields = (int *)(msg + 1);
        for (int i = 0; i < nodes; ++i)
        {
            if (m_.find(fields[i]) != m_.end())
                continue;
            AddNode(fields[i]);
        }
    }
    break;

    case PING:
    {
        const int &src = *(int *)(msg + 1);
        const int &dest = *((int *)(msg + 1) + 1);
        // std::cout << "PING " << this_node_ << " " << src << " " << dest << "\n";
        if (dest == this_node_)
        {
            // send a PONG to the requesting node.
            auto *message = CreateMessage(PONG, this_node_, src);
            Address a(std::to_string(src) + ":0");
            // memcpy(&a.addr, (char *)(msg + 1), sizeof(a.addr));
            emulNet->ENsend(&memberNode->addr, &a, (char *)message, MSGSIZE); // no need to check status
            // local_state_[src] = {FINALWAITING, current_time + WAITING_TIMEOUT, -1};
            free(msg);
        }
        else
        {
            // std::cout << "Indirect PING\n";
            // send a PING to the requested node and later send PONG back to the client to the requesting node.
            auto *message = CreateMessage(PING, this_node_, dest);
            Address a(std::to_string(dest) + ":0");
            emulNet->ENsend(&memberNode->addr, &a, (char *)message, MSGSIZE); // no need to check status
            local_state_[dest] = {FINALWAITING, current_time + WAITING_TIMEOUT, src};
            free(msg);
        }
    }
    break;

    case PONG:
    {
        const int &src = *(int *)(msg + 1);
        // const int &dest = *((int *)(msg + 1) + 1);
        // std::cout << "PONG " << this_node_ << " " << src << " " << dest << "\n";
        auto it = local_state_.find(src);
        if (it != local_state_.end())
        {
            NodeStatus &status = it->second;
            // std::cout << "PONG2 " << status.state << "a\n";
            switch (status.state)
            {
            case TIMEOUTWAITING:
                break;
            case SWIMWAITING:
                break;
            case FINALWAITING:
            {
                // std::cout << "FINALWAITING\n";
                auto *message = CreateMessage(PONG, src, status.node);
                Address a(std::to_string(status.node) + ":0");
                emulNet->ENsend(&memberNode->addr, &a, (char *)message, MSGSIZE);
                free(msg);
            }
            break;
            default:
                break;
            }
        }
        local_state_.erase(src);
        for (auto jt = local_state_.begin(); jt != local_state_.end(); ++jt)
        {
            // std::cout << "local_stateeee: " << jt->first << " " << jt->second.state << " " << jt->second.timeout << jt->second.node << "\n";
        }
        if (m_.find(src) == m_.end())
        {
            // std::cout << "Special case\n";
            // AddNode(src);
        }
    }
    break;

    case FAILED:
    {
        const int &failed = *(int *)(msg + 1);
        // std::cout << "FAILED " << failed << " " << this_node_ << " " << (m_.find(failed) != m_.end()) << " " << m_.size() << " " << memberList.size() << "\n";
        if (m_.find(failed) != m_.end())
        {
            m_.erase(failed);
            for (int i = 0; i < memberList.size(); ++i)
            {
                if (memberList[i].getid() == failed)
                {
                    memberList.erase(memberList.begin() + i);
                    break;
                }
            }
            Address a(std::to_string(failed) + ":0");
#ifdef DEBUGLOG
            log->logNodeRemove(&memberNode->addr, &a);
#endif
        }
    }
    break;

    default:
        break;
    }
    return true;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xb8, %rsp
movl %ecx, %r15d
movq %rdx, %r14
movq %rdi, %r13
movq 0x18(%rdi), %rdi
callq 0xa4d8
movl %eax, %r12d
movl (%r14), %eax
cmpq $0x4, %rax
ja 0x4197
movq 0x20(%r13), %rbp
leaq 0xe244(%rip), %rcx      # 0x12004
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl 0x4(%r14), %esi
movq %r13, 0x8(%rsp)
movq %r13, %rdi
callq 0x3a74
movq 0x28(%rbp), %r15
movq 0x30(%rbp), %r13
movq %r13, %rax
subq %r15, %rax
pushq $0x18
popq %rcx
cqto
idivq %rcx
leaq 0x4(,%rax,4), %rdi
movq %rdi, 0x30(%rsp)
callq 0x3360
movq %rax, %r12
movl $0x1, (%rax)
xorl %ebx, %ebx
xorl %r14d, %r14d
subq %r15, %r13
movq %r13, %rax
cqto
pushq $0x18
popq %rcx
idivq %rcx
cmpq %r14, %rax
jbe 0x408a
addq %rbx, %r15
movq %r15, %rdi
callq 0xa560
movl %eax, 0x4(%r12,%r14,4)
incq %r14
movq 0x28(%rbp), %r15
movq 0x30(%rbp), %r13
addq $0x18, %rbx
jmp 0x3e10
leaq 0x4(%r14), %r15
leaq 0x68(%r13), %r12
movq %r12, %rdi
movq %r15, %rsi
callq 0x570c
testq %rax, %rax
je 0x3ef4
movq %rax, %rbp
cmpl $0x2, 0x10(%rax)
jne 0x3ef4
movq %r13, %rbx
movl (%r15), %esi
movl 0x20(%rbp), %edx
pushq $0x4
popq %rdi
callq 0x4370
movq %rax, %r13
movl 0x20(%rbp), %esi
leaq 0x10(%rsp), %rbp
movq %rbp, %rdi
callq 0x46ad
leaq 0xe1ac(%rip), %rdx      # 0x12048
leaq 0x58(%rsp), %rdi
movq %rbp, %rsi
callq 0x468e
leaq 0x2(%rsp), %rdi
leaq 0x58(%rsp), %rsi
callq 0x4706
leaq 0x58(%rsp), %rdi
callq 0x3110
leaq 0x10(%rsp), %rdi
callq 0x3110
movq 0x8(%rbx), %rdi
movq 0x20(%rbx), %rsi
addq $0x8, %rsi
leaq 0x2(%rsp), %rdx
pushq $0xc
popq %r8
movq %r13, %rcx
movq %rbx, %r13
callq 0x5b9a
movq %r14, %rdi
callq 0x3300
movq %r12, %rdi
movq %r15, %rsi
callq 0x572a
addq $0x30, %r13
movq %r13, %rdi
movq %r15, %rsi
callq 0x4dca
jmp 0x4197
addq $0x4, %r14
leaq 0x30(%r13), %r12
movq %r12, %rdi
movq %r14, %rsi
callq 0x4dca
testq %rax, %rax
je 0x4197
movq %r13, 0x8(%rsp)
leaq 0x28(%rbp), %r15
movq %r12, %rdi
movq %r14, %rsi
callq 0x582c
pushq $-0x18
popq %r12
pushq $-0x1
popq %rbx
pushq $0x18
popq %r13
movq 0x28(%rbp), %rcx
movq 0x30(%rbp), %rax
subq %rcx, %rax
cqto
idivq %r13
incq %rbx
cmpq %rbx, %rax
jbe 0x3f8a
leaq (%rcx,%r12), %rdi
addq $0x18, %rdi
addq $0x18, %r12
callq 0xa560
cmpl (%r14), %eax
jne 0x3f4e
addq (%r15), %r12
movq %r15, %rdi
movq %r12, %rsi
callq 0x47b4
movl (%r14), %esi
leaq 0x10(%rsp), %r14
movq %r14, %rdi
callq 0x46ad
leaq 0xe0a7(%rip), %rdx      # 0x12048
leaq 0x38(%rsp), %rdi
movq %r14, %rsi
callq 0x468e
leaq 0x2(%rsp), %rdi
leaq 0x38(%rsp), %rsi
callq 0x4706
movq 0x8(%rsp), %rbx
leaq 0x38(%rsp), %rdi
callq 0x3110
leaq 0x10(%rsp), %rdi
callq 0x3110
movq 0x10(%rbx), %rdi
movq 0x20(%rbx), %rsi
addq $0x8, %rsi
leaq 0x2(%rsp), %rdx
callq 0x9d42
jmp 0x4197
movl 0x8(%r14), %edx
movl 0xa4(%r13), %esi
cmpl %esi, %edx
jne 0x40ef
movl 0x4(%r14), %eax
pushq $0x4
popq %rdi
movl %edx, %esi
movl %eax, %edx
callq 0x4370
movq %rax, %r15
movl 0x4(%r14), %esi
leaq 0x10(%rsp), %r12
movq %r12, %rdi
callq 0x46ad
leaq 0xe019(%rip), %rdx      # 0x12048
leaq 0x98(%rsp), %rdi
movq %r12, %rsi
callq 0x468e
leaq 0x2(%rsp), %rdi
leaq 0x98(%rsp), %rsi
callq 0x4706
leaq 0x98(%rsp), %rdi
callq 0x3110
leaq 0x10(%rsp), %rdi
callq 0x3110
movq 0x8(%r13), %rdi
movq 0x20(%r13), %rsi
addq $0x8, %rsi
leaq 0x2(%rsp), %rdx
pushq $0xc
popq %r8
movq %r15, %rcx
callq 0x5b9a
jmp 0x418f
movq 0x8(%rsp), %rdi
movq %r12, %rsi
movq 0x30(%rsp), %rdx
callq 0x41f6
jmp 0x4197
movslq %r15d, %rax
movabsq $0x3fffffffc, %rcx      # imm = 0x3FFFFFFFC
addq %rax, %rcx
shrq $0x2, %rcx
addq $0x4, %r14
leaq 0x30(%r13), %r15
xorl %ebx, %ebx
testl %ecx, %ecx
cmovgl %ecx, %ebx
subq $0x1, %rbx
jb 0x4197
movq %r15, %rdi
movq %r14, %rsi
callq 0x4dca
testq %rax, %rax
jne 0x40e9
movl (%r14), %esi
movq %r13, %rdi
callq 0x3a74
addq $0x4, %r14
jmp 0x40c4
movq %r13, %rbx
leaq 0x8(%r14), %r15
pushq $0x3
popq %rdi
callq 0x4370
movq %rax, %rbp
movl (%r15), %esi
leaq 0x10(%rsp), %r13
movq %r13, %rdi
callq 0x46ad
leaq 0xdf30(%rip), %rdx      # 0x12048
leaq 0x78(%rsp), %rdi
movq %r13, %rsi
callq 0x468e
leaq 0x2(%rsp), %rdi
leaq 0x78(%rsp), %rsi
callq 0x4706
movq %rbx, %r13
leaq 0x78(%rsp), %rdi
callq 0x3110
leaq 0x10(%rsp), %rdi
callq 0x3110
movq 0x8(%rbx), %rdi
movq 0x20(%rbx), %rsi
addq $0x8, %rsi
leaq 0x2(%rsp), %rdx
pushq $0xc
popq %r8
movq %rbp, %rcx
callq 0x5b9a
addl $0xa, %r12d
movslq %r12d, %rbx
movl 0x4(%r14), %ebp
addq $0x68, %r13
movq %r13, %rdi
movq %r15, %rsi
callq 0x53c0
movl $0x2, (%rax)
movq %rbx, 0x8(%rax)
movl %ebp, 0x10(%rax)
movq %r14, %rdi
callq 0x3300
movb $0x1, %al
addq $0xb8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
leaq 0x58(%rsp), %rdi
jmp 0x41da
jmp 0x41e1
movq %rax, %rbx
leaq 0x78(%rsp), %rdi
jmp 0x41da
movq %rax, %rbx
leaq 0x98(%rsp), %rdi
jmp 0x41da
jmp 0x41e1
jmp 0x41e1
movq %rax, %rbx
leaq 0x38(%rsp), %rdi
callq 0x3110
jmp 0x41e4
movq %rax, %rbx
leaq 0x10(%rsp), %rdi
callq 0x3110
movq %rbx, %rdi
callq 0x3450