// File: /workspace/github_cpp_makefile_star2_O3/ruankotovich[P]B-Tree/src/primarybtree.cpp
// Function: PrimaryBTree::insertRecursive(int, PrimaryBTreeNodeReinterpret*, int, _IO_FILE*)
PrimaryBTreeRecursionResponse PrimaryBTree::insertRecursive(int key, PrimaryBTreeNodeReinterpret* nodeReinterpretation, int offset, FILE* indexFile)
{
    if (nodeReinterpretation->node.isLeaf()) {

        if (nodeReinterpretation->node.hasRoom()) {
            nodeReinterpretation->node.insert(key);
            writeBackNode(&nodeReinterpretation->node, offset, indexFile);
            return SUCCESSFUL_TREE_INSERTION;
        }

        PrimaryBTreeNode* splitLeaf = new PrimaryBTreeNode(PRIMARY_MAX_KEYS);
        int promoted;

        // elege o promoted e manipula os vetores resultantes da operação do splitLeaf.
        int leftMiddleKey = nodeReinterpretation->node.keys[PRIMARY_LEFT_MIDDLE_KEY];
        int rightMiddleKey = nodeReinterpretation->node.keys[PRIMARY_RIGHT_MIDDLE_KEY];

        splitLeaf->count = nodeReinterpretation->node.count = PRIMARY_HALF_MAX_KEYS;

        switch (relativeKeyPosition(key, leftMiddleKey, rightMiddleKey)) {
        case RELATIVE_LEFT: {
            promoted = leftMiddleKey;
            --nodeReinterpretation->node.count;

            nodeReinterpretation->node.insert(key);

            for (int i = PRIMARY_HALF_MAX_KEYS, j = 0; i < PRIMARY_MAX_KEYS; ++i, ++j) {
                splitLeaf->keys[j] = nodeReinterpretation->node.keys[i];
            }
        } // não esquecer de manipular os blockPointes para nós não-folha
        break;

        case RELATIVE_RIGHT: {
            promoted = rightMiddleKey;
            --splitLeaf->count;

            for (int i = PRIMARY_RIGHT_MIDDLE_KEY + 1, j = 0; i < PRIMARY_MAX_KEYS; ++i, ++j) {
                splitLeaf->keys[j] = nodeReinterpretation->node.keys[i];
            } // não esquecer de manipular os blockPointes para nós não-folha

            splitLeaf->insert(key);
        } break;

        case RELATIVE_MIDDLE: {
            promoted = key;

            for (int i = PRIMARY_HALF_MAX_KEYS, j = 0; i < PRIMARY_MAX_KEYS; ++i, ++j) {
                splitLeaf->keys[j] = nodeReinterpretation->node.keys[i];
            }
        } // não esquecer de manipular os blockPointes para nós não-folha

        break;
        }

        writeBackNode(&nodeReinterpretation->node, offset, indexFile);
        int offset = writeNewNode(splitLeaf, indexFile);

        delete splitLeaf; // deleta da memória principal pois já foi salvo no disco

        // return { true, { promoted, offset } };
        return PrimaryBTreeRecursionResponse(true, promoted, offset);
    }

    int result = upperBound(nodeReinterpretation->node.keys, nodeReinterpretation->node.count, key); // [TROCAR]

    unsigned short childNodeOffset;

    if (result >= nodeReinterpretation->node.count) {
        childNodeOffset = result;
        result = nodeReinterpretation->node.count - 1;
    }

    childNodeOffset = ((key < nodeReinterpretation->node.keys[result]) ? nodeReinterpretation->node.blockPointers[result] : nodeReinterpretation->node.blockPointers[result + 1]);

    PrimaryBTreeNodeReinterpret* retrieveNode = new PrimaryBTreeNodeReinterpret();
    fseek(indexFile, sizeof(AbstractBlock_t) * childNodeOffset, SEEK_SET);
    fread(&retrieveNode->block, sizeof(AbstractBlock_t), 1, indexFile);

    auto resultRecursion = insertRecursive(key, retrieveNode, childNodeOffset, indexFile);
    delete retrieveNode;

    if (resultRecursion.hasBeenSplit) { // caso em que veio um valor promovido e um blockPointer
        //caso 1 - tem espaço para inserir o promovido
        if (nodeReinterpretation->node.hasRoom()) {
            //o + 1 é para não realizar essa soma em toda iteração do for logo abaixo
            unsigned short rightIndexInserted = nodeReinterpretation->node.insert(resultRecursion.promotedKey) + 1;

            for (unsigned short i = nodeReinterpretation->node.countPointers; i > rightIndexInserted; --i) {
                nodeReinterpretation->node.blockPointers[i] = nodeReinterpretation->node.blockPointers[i - 1];
            }

            nodeReinterpretation->node.blockPointers[rightIndexInserted] = resultRecursion.newBlockOffset;
            ++nodeReinterpretation->node.countPointers;

            writeBackNode(&nodeReinterpretation->node, offset, indexFile);
        } else { // caso 2 - não tem espaço para inserir o promovido -> fazer o split com remanejamento de chaves
            PrimaryBTreeNodeReinterpret* split = new PrimaryBTreeNodeReinterpret();
            int promoted;

            // elege o promoted e manipula os vetores resultantes da operação do split.
            int leftMiddleKey = nodeReinterpretation->node.keys[PRIMARY_LEFT_MIDDLE_KEY];
            int rightMiddleKey = nodeReinterpretation->node.keys[PRIMARY_RIGHT_MIDDLE_KEY];

            split->node.count = nodeReinterpretation->node.count = PRIMARY_HALF_MAX_KEYS; // remoção lógica exatamente na metade

            //como está cheio, vai metade dos elementos para cada bloco, contnado com o split.
            // se vai metade, haverá metade + 1 ponteiros para bloco preenchidos.
            split->node.countPointers = nodeReinterpretation->node.countPointers = PRIMARY_HALF_MAX_KEYS + 1;

            switch (relativeKeyPosition(resultRecursion.promotedKey, leftMiddleKey, rightMiddleKey)) {
            case RELATIVE_LEFT: {
                promoted = leftMiddleKey;

                --nodeReinterpretation->node.count;

                // índice do ponteiro mais à direita do vetor blockPointers
                unsigned short greaterNodeBlockPointer = nodeReinterpretation->node.blockPointers[nodeReinterpretation->node.countPointers - 1]; // a subtração tem a ver com índice

                //índice em que foi inserido o promovido no nó já existente
                unsigned short rightIndexInserted = nodeReinterpretation->node.insert(resultRecursion.promotedKey) + 1;

                //remanejar os blockPoints
                for (unsigned short i = nodeReinterpretation->node.countPointers - 1; i > rightIndexInserted; --i) {
                    nodeReinterpretation->node.blockPointers[i] = nodeReinterpretation->node.blockPointers[i - 1];
                }

                nodeReinterpretation->node.blockPointers[rightIndexInserted] = resultRecursion.newBlockOffset;

                for (int i = PRIMARY_HALF_MAX_KEYS, j = 0; i < PRIMARY_MAX_KEYS; ++i, ++j) {
                    split->node.keys[j] = nodeReinterpretation->node.keys[i];
                    split->node.blockPointers[j + 1] = nodeReinterpretation->node.blockPointers[i + 1];
                } // não esquecer de manipular os blockPointes para nós não-folha

                split->node.blockPointers[0] = greaterNodeBlockPointer;
            } break;

            case RELATIVE_RIGHT: {
                promoted = rightMiddleKey;
                --split->node.count;
                // não é preciso modificar o countPointers nem o count do node aqui porque eles já foram atribuidos ali em cima,
                // então a remoção lógica já foi feita

                for (int i = PRIMARY_RIGHT_MIDDLE_KEY + 1, j = 0; i < PRIMARY_MAX_KEYS; ++i, ++j) {
                    split->node.keys[j] = nodeReinterpretation->node.keys[i];
                    split->node.blockPointers[j] = nodeReinterpretation->node.blockPointers[i];
                } // não esquecer de manipular os blockPointes para nós não-folha/

                split->node.blockPointers[PRIMARY_HALF_MAX_KEYS - 1] = nodeReinterpretation->node.blockPointers[PRIMARY_MAX_KEYS];
                unsigned short rightIndexInserted = split->node.insert(resultRecursion.promotedKey) + 1;

                //remanejar os blockPoints
                for (unsigned short i = split->node.countPointers - 1; i > rightIndexInserted; --i) {
                    split->node.blockPointers[i] = split->node.blockPointers[i - 1];
                }

                split->node.blockPointers[rightIndexInserted] = resultRecursion.newBlockOffset;
            } break;

            case RELATIVE_MIDDLE: {
                promoted = resultRecursion.promotedKey;

                for (int i = PRIMARY_HALF_MAX_KEYS, j = 0; i < PRIMARY_MAX_KEYS; ++i, ++j) {
                    split->node.keys[j] = nodeReinterpretation->node.keys[i];
                    split->node.blockPointers[j + 1] = nodeReinterpretation->node.blockPointers[i + 1];
                } // não esquecer de manipular os blockPointes para nós não-folha
                split->node.blockPointers[0] = resultRecursion.newBlockOffset;
            } break;
            }

            writeBackNode(&nodeReinterpretation->node, offset, indexFile);
            int offset = writeNewNode(&split->node, indexFile);
            delete split; // deleta da memória principal pois já foi salvo no disco
            return PrimaryBTreeRecursionResponse(true, promoted, offset);
        }
    }

    return SUCCESSFUL_TREE_INSERTION;
    // return PrimaryBTreeRecursionResponse(false, 0, 0);
    // return { false, { 0, 0 } };
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x28, %rsp
movq %r8, 0x10(%rsp)
movl %ecx, %r13d
movq %rdx, %r14
movl %esi, %r15d
movq %rdi, %r12
cmpw $0x0, 0x2(%rdx)
je 0x2cbf
movq %r12, 0x8(%rsp)
movl %r13d, (%rsp)
leaq 0x4(%r14), %rbx
movzwl (%r14), %eax
movq %rbx, %rcx
testl %eax, %eax
je 0x2bcc
movl %eax, %edx
movq %rbx, %rcx
movq %rdx, %rsi
shrq %rsi
movq %rsi, %rdi
notq %rdi
addq %rdx, %rdi
leaq (%rcx,%rsi,4), %rdx
addq $0x4, %rdx
cmpl %r15d, -0x4(%rdx)
cmovgq %rsi, %rdi
cmovleq %rdx, %rcx
movq %rdi, %rdx
testq %rdi, %rdi
jg 0x2ba1
subq %rbx, %rcx
shrq $0x2, %rcx
leal -0x1(%rax), %edx
cmpl %eax, %ecx
cmovll %ecx, %edx
movslq %edx, %rax
leal 0x1(%rax), %ecx
cmpl %r15d, 0x4(%r14,%rax,4)
movslq %ecx, %rcx
cmovgq %rax, %rcx
movzwl 0xaa4(%r14,%rcx,2), %r13d
movl $0x1000, %edi           # imm = 0x1000
callq 0x10b0
movq %rax, %rbp
movl $0x0, (%rax)
movl %r13d, %esi
shll $0xc, %esi
movq 0x10(%rsp), %r12
movq %r12, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %rbp, %rdi
movq %r12, %rcx
callq 0x1110
movq %r14, 0x20(%rsp)
movq 0x8(%rsp), %r14
movq %r14, %rdi
movl %r15d, %esi
movq %rbp, %rdx
movl %r13d, %ecx
movq %r12, %r8
callq 0x2b5a
movq %rax, %r13
movl %edx, %r15d
movl $0x1000, %esi           # imm = 0x1000
movq %rbp, %rdi
callq 0x10c0
testb $0x1, %r13b
je 0x30ee
movq 0x20(%rsp), %r14
movzwl (%r14), %edx
cmpl $0x2a7, %edx            # imm = 0x2A7
ja 0x2e02
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
shrq $0x20, %r13
movw $0x1, %cx
testw %dx, %dx
je 0x2cb7
movl %edx, %esi
movzwl %si, %edi
leal (%rdi,%rax), %r8d
movl (%rbx,%r8,4), %r8d
leaq (%rbx,%rdi,4), %rdi
cmpl %r13d, %r8d
jl 0x3062
movl %r8d, (%rdi)
decw %si
jne 0x2c97
movq %rbx, %rdi
jmp 0x3066
movzwl (%r14), %eax
cmpl $0x2a7, %eax            # imm = 0x2A7
ja 0x2d44
testw %ax, %ax
je 0x2cf6
movl $0xffffffff, %ecx       # imm = 0xFFFFFFFF
leaq 0x4(%r14), %rdx
movl %eax, %esi
movzwl %si, %edi
leal (%rdi,%rcx), %r8d
movl (%rdx,%r8,4), %r8d
leaq (%rdx,%rdi,4), %rdi
cmpl %r15d, %r8d
jl 0x2cfa
movl %r8d, (%rdi)
decw %si
jne 0x2cda
leaq 0x4(%r14), %rdi
movl %r15d, (%rdi)
incl %eax
movw %ax, (%r14)
movslq %r13d, %rsi
shlq $0xc, %rsi
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %r14, %rdi
movq %rbx, %rcx
callq 0x1180
movq (%r12), %r15
movq %r15, %rbp
shrq $0x20, %rbp
movzwl 0x8(%r12), %ebx
jmp 0x30fd
movl $0xff8, %edi            # imm = 0xFF8
callq 0x10b0
movq %rax, %r12
movw $0x0, 0x2(%rax)
movl 0x550(%r14), %ebp
movl 0x554(%r14), %ebx
leaq 0x554(%r14), %rsi
movw $0x154, %ax             # imm = 0x154
movw %ax, (%r14)
movw %ax, (%r12)
xorl %ecx, %ecx
cmpl %r15d, %ebx
setl %cl
cmpl %r15d, %ebp
movl $0xff, %eax
cmovlel %ecx, %eax
testb %al, %al
je 0x2f0f
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x2f25
movl %r13d, (%rsp)
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
movq %r12, %r13
addq $0x4, %r13
leaq 0x558(%r14), %rsi
movl $0x54c, %edx            # imm = 0x54C
movq %r13, %rdi
callq 0x1090
movw $0x153, %ax             # imm = 0x153
movzwl %ax, %ecx
leal (%rcx,%rbp), %edx
movl (%r13,%rdx,4), %edx
leaq (,%rcx,4), %rcx
addq %r13, %rcx
cmpl %r15d, %edx
jl 0x2ded
movl %edx, (%rcx)
decw %ax
jne 0x2dc8
movq %r13, %rcx
movl %r15d, (%rcx)
movw $0x154, (%r12)          # imm = 0x154
movl %ebx, %ebp
movl (%rsp), %r13d
jmp 0x2f6b
movl $0x1000, %edi           # imm = 0x1000
callq 0x10b0
movq %rax, %r12
movl 0x550(%r14), %ebp
movl 0x554(%r14), %edi
leaq 0x554(%r14), %rsi
movw $0x154, %ax             # imm = 0x154
movw %ax, (%r14)
movw %ax, (%r12)
movw $0x155, %ax             # imm = 0x155
movw %ax, 0x2(%r14)
movw %ax, 0x2(%r12)
shrq $0x20, %r13
xorl %ecx, %ecx
cmpl %r13d, %edi
setl %cl
cmpl %r13d, %ebp
movl $0xff, %eax
cmovlel %ecx, %eax
cmpb $-0x1, %al
je 0x2fdd
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x3024
movl $0xffffffff, %ebx       # imm = 0xFFFFFFFF
leaq 0x4(%r12), %rbp
movl %r15d, %eax
leaq 0xaa4(%r12), %r15
leaq 0x558(%r14), %rsi
movl $0x54c, %edx            # imm = 0x54C
movw %ax, 0x6(%rsp)
movl %edi, 0x1c(%rsp)
movq %rbp, %rdi
callq 0x1090
leaq 0xd4e(%r14), %rsi
movl $0x2a6, %edx            # imm = 0x2A6
movq %r15, 0x8(%rsp)
movq %r15, %rdi
callq 0x1090
movl 0x1c(%rsp), %edi
movzwl 0x6(%rsp), %r8d
movzwl 0xff4(%r14), %eax
movw %ax, 0xd4a(%r12)
movw $0x153, %cx             # imm = 0x153
movzwl %cx, %ecx
movl %ecx, %eax
addl %ebx, %eax
movl (%rbp,%rax,4), %eax
cmpl %r13d, %eax
jl 0x3105
movl %eax, (%rbp,%rcx,4)
decw %cx
jne 0x2ed5
movl %r13d, 0x4(%r12)
movw $0x154, (%r12)          # imm = 0x154
movw $0x1, %ax
movl (%rsp), %ebx
movq 0x8(%rsp), %r9
jmp 0x3127
movq %r12, %rdi
addq $0x4, %rdi
movl $0x550, %edx            # imm = 0x550
callq 0x1090
movl %r15d, %ebp
jmp 0x2f6b
leaq 0x4(%r14), %rax
movw $0x153, %cx             # imm = 0x153
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
movzwl %cx, %edi
leal (%rdi,%rdx), %r8d
movl (%rax,%r8,4), %r8d
leaq (%rax,%rdi,4), %rdi
cmpl %r15d, %r8d
jl 0x2f51
movl %r8d, (%rdi)
decw %cx
jne 0x2f32
movq %rax, %rdi
movl %r15d, (%rdi)
movw $0x154, (%r14)          # imm = 0x154
movq %r12, %rdi
addq $0x4, %rdi
movl $0x550, %edx            # imm = 0x550
callq 0x1090
movslq %r13d, %rsi
shlq $0xc, %rsi
xorl %r15d, %r15d
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1, %r13d
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %r14, %rdi
movq %rbx, %rcx
callq 0x1180
movq %rbx, %rdi
xorl %esi, %esi
movl $0x2, %edx
callq 0x10d0
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %r12, %rdi
movq %rbx, %rcx
callq 0x1180
movq %rbx, %rdi
callq 0x1030
movq %rax, %rbx
shrl $0xc, %ebx
decl %ebx
movl $0xff8, %esi            # imm = 0xFF8
jmp 0x3249
movl $0xffffffff, %ecx       # imm = 0xFFFFFFFF
movzwl 0xd4c(%r14), %r8d
movw $0x153, %dx             # imm = 0x153
movl $0x154, %eax            # imm = 0x154
movzwl %dx, %edx
movl %edx, %edi
addl %ecx, %edi
movl (%rbx,%rdi,4), %edi
cmpl %r13d, %edi
jl 0x3156
movl %edi, (%rbx,%rdx,4)
decq %rax
decw %dx
jne 0x2ff3
movl %r13d, 0x4(%r14)
movw $0x154, (%r14)          # imm = 0x154
movw $0x1, %ax
jmp 0x316c
leaq 0x4(%r12), %rdi
movl $0x550, %edx            # imm = 0x550
callq 0x1090
movq %r12, %rdi
addq $0xaa6, %rdi            # imm = 0xAA6
leaq 0xd4e(%r14), %rsi
movl $0x2a8, %edx            # imm = 0x2A8
callq 0x1090
movw %r15w, 0xaa4(%r12)
movl %r13d, %ebp
movl (%rsp), %ebx
jmp 0x31dc
incl %esi
movl %esi, %ecx
movl %r13d, (%rdi)
incl %edx
movw %dx, (%r14)
movzwl 0x2(%r14), %edx
cmpw %cx, %dx
jbe 0x30aa
movzwl %dx, %esi
addq %rsi, %rax
leaq (%r14,%rsi,2), %rsi
addq $0xaa4, %rsi            # imm = 0xAA4
movl %edx, %edi
movl %eax, %r8d
movzwl 0xaa4(%r14,%r8,2), %r8d
movw %r8w, (%rsi)
decq %rax
addq $-0x2, %rsi
decl %edi
cmpw %di, %cx
jb 0x308c
movzwl %cx, %eax
movw %r15w, 0xaa4(%r14,%rax,2)
incl %edx
movw %dx, 0x2(%r14)
movslq (%rsp), %rsi
shlq $0xc, %rsi
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %r14, %rdi
movq %rbx, %rcx
callq 0x1180
movq 0x8(%rsp), %r14
movq (%r14), %r15
movq %r15, %rbp
shrq $0x20, %rbp
movzwl 0x8(%r14), %ebx
movq %r15, %r13
jmp 0x3251
leal 0x1(%rcx), %eax
movl %r13d, 0x4(%r12,%rcx,4)
movw $0x154, (%r12)          # imm = 0x154
movzwl %ax, %ecx
cmpl $0x153, %ecx            # imm = 0x153
movl (%rsp), %ebx
movq 0x8(%rsp), %r9
ja 0x3147
movq %r12, %rdx
addq $0xd4c, %rdx            # imm = 0xD4C
movw $0x154, %cx             # imm = 0x154
movzwl -0x2(%rdx), %esi
movw %si, (%rdx)
addq $-0x2, %rdx
decl %ecx
cmpw %cx, %ax
jb 0x3135
movzwl %ax, %eax
movw %r8w, (%r9,%rax,2)
movl %edi, %ebp
jmp 0x31dc
movl %r13d, 0x4(%r14,%rdx,4)
movw $0x154, (%r14)          # imm = 0x154
movzwl %ax, %ecx
cmpl $0x153, %ecx            # imm = 0x153
ja 0x319a
movl (%rsp), %ebx
movzwl %ax, %eax
movl $0x6a6, %ecx            # imm = 0x6A6
movzwl -0x2(%r14,%rcx,2), %edx
movw %dx, (%r14,%rcx,2)
leaq -0x1(%rcx), %rdx
addq $-0x553, %rcx           # imm = 0xFAAD
cmpq %rax, %rcx
movq %rdx, %rcx
ja 0x3177
movl %r8d, %r13d
jmp 0x31a0
movl %r8d, %r13d
movl (%rsp), %ebx
movw %r15w, 0xaa4(%r14,%rax,2)
leaq 0x4(%r12), %rdi
movl $0x550, %edx            # imm = 0x550
callq 0x1090
movq %r12, %rdi
addq $0xaa6, %rdi            # imm = 0xAA6
leaq 0xd4e(%r14), %rsi
movl $0x2a8, %edx            # imm = 0x2A8
callq 0x1090
movw %r13w, 0xaa4(%r12)
movslq %ebx, %rsi
shlq $0xc, %rsi
xorl %r15d, %r15d
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1, %r13d
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %r14, %rdi
movq %rbx, %rcx
callq 0x1180
movq %rbx, %rdi
xorl %esi, %esi
movl $0x2, %edx
callq 0x10d0
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %r12, %rdi
movq %rbx, %rcx
callq 0x1180
movq %rbx, %rdi
callq 0x1030
movq %rax, %rbx
shrl $0xc, %ebx
decl %ebx
movl $0x1000, %esi           # imm = 0x1000
movq %r12, %rdi
callq 0x10c0
shlq $0x20, %rbp
andl $0xffffff00, %r15d      # imm = 0xFFFFFF00
orq %rbp, %r15
movzbl %r13b, %eax
orq %r15, %rax
movl %ebx, %edx
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop