// File: /workspace/github_cpp_makefile_star2_O1/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
pushq %rax
movq %r8, %rbx
movl %ecx, %ebp
movq %rdx, %r14
movl %esi, %r15d
movq %rdi, %r12
cmpw $0x0, 0x2(%rdx)
je 0x2b33
movl %ebp, (%rsp)
leaq 0x4(%r14), %rdx
movzwl (%r14), %eax
movq %rdx, %rcx
testl %eax, %eax
je 0x2a2a
movl %eax, %esi
movq %rdx, %rcx
movq %rsi, %rdi
shrq %rdi
movq %rdi, %r8
notq %r8
addq %rsi, %r8
leaq (%rcx,%rdi,4), %rsi
addq $0x4, %rsi
cmpl %r15d, -0x4(%rsi)
cmovgq %rdi, %r8
cmovleq %rsi, %rcx
movq %r8, %rsi
testq %r8, %r8
jg 0x29ff
subq %rdx, %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 %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1000, %esi           # imm = 0x1000
movl $0x1, %edx
movq %rbp, %rdi
movq %rbx, %rcx
callq 0x1110
movq %r12, %rdi
movl %r15d, %esi
movq %rbp, %rdx
movl %r13d, %ecx
movq %rbx, %r8
callq 0x29c4
movq %rax, %r13
movl %edx, %r15d
movl $0x1000, %esi           # imm = 0x1000
movq %rbp, %rdi
callq 0x10c0
testb $0x1, %r13b
je 0x2b6c
cmpw $0x2a7, (%r14)          # imm = 0x2A7
ja 0x2c0f
shrq $0x20, %r13
movq %r14, %rdi
movl %r13d, %esi
callq 0x28a8
incl %eax
movzwl 0x2(%r14), %ecx
cmpw %ax, %cx
jbe 0x2b1a
movzwl %cx, %esi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
addq %rsi, %rdx
leaq (%r14,%rsi,2), %rsi
addq $0xaa4, %rsi            # imm = 0xAA4
movl %ecx, %edi
movl %edx, %r8d
movzwl 0xaa4(%r14,%r8,2), %r8d
movw %r8w, (%rsi)
decq %rdx
addq $-0x2, %rsi
decl %edi
cmpw %di, %ax
jb 0x2afc
movzwl %ax, %eax
movw %r15w, 0xaa4(%r14,%rax,2)
incl %ecx
movw %cx, 0x2(%r14)
movslq (%rsp), %rsi
jmp 0x2b49
cmpw $0x2a7, (%r14)          # imm = 0x2A7
ja 0x2b85
movq %r14, %rdi
movl %r15d, %esi
callq 0x28a8
movslq %ebp, %rsi
shlq $0xc, %rsi
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), %r13
movq %r13, %r15
shrq $0x20, %r15
movzwl 0x8(%r12), %ebx
movq %r13, %rbp
jmp 0x2f3b
movl %ebp, (%rsp)
movl $0xff8, %edi            # imm = 0xFF8
callq 0x10b0
movq %rax, %r12
movl $0x0, (%rax)
movl 0x550(%r14), %r13d
movl 0x554(%r14), %ebp
movw $0x154, %ax             # imm = 0x154
movw %ax, (%r14)
movw %ax, (%r12)
xorl %ecx, %ecx
cmpl %r15d, %ebp
setl %cl
cmpl %r15d, %r13d
movl $0xff, %eax
cmovlel %ecx, %eax
cmpb $-0x1, %al
je 0x2d2c
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x2d5a
movw $0x153, (%r12)          # imm = 0x153
movq %r12, %rdi
addq $0x4, %rdi
leaq 0x558(%r14), %rsi
movl $0x54c, %edx            # imm = 0x54C
callq 0x1090
movq %r12, %rdi
movl %r15d, %esi
callq 0x28a8
movl %ebp, %r15d
jmp 0x2d72
movw %r15w, 0x6(%rsp)
movl $0x1000, %edi           # imm = 0x1000
callq 0x10b0
movq %rax, %r12
movl $0x0, (%rax)
movl 0x550(%r14), %r15d
movl 0x554(%r14), %ebp
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, %ebp
setl %cl
cmpl %r13d, %r15d
movl $0xff, %eax
cmovlel %ecx, %eax
cmpb $-0x1, %al
je 0x2de1
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x2e87
movw $0x153, (%r12)          # imm = 0x153
leaq 0x4(%r12), %rdi
leaq 0xaa4(%r12), %r15
leaq 0x558(%r14), %rsi
movl $0x54c, %edx            # imm = 0x54C
callq 0x1090
leaq 0xd4e(%r14), %rsi
movl $0x2a6, %edx            # imm = 0x2A6
movq %r15, %rdi
callq 0x1090
movzwl 0xff4(%r14), %eax
movw %ax, 0xd4a(%r12)
movq %r12, %rdi
movl %r13d, %esi
callq 0x28a8
incl %eax
movzwl 0x2(%r12), %ecx
decl %ecx
cmpw %ax, %cx
jbe 0x2d13
movzwl %cx, %esi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
addq %rsi, %rdx
leaq (%r12,%rsi,2), %rsi
addq $0xaa4, %rsi            # imm = 0xAA4
movl %edx, %edi
movzwl 0xaa4(%r12,%rdi,2), %edi
movw %di, (%rsi)
decq %rdx
addq $-0x2, %rsi
decl %ecx
cmpw %cx, %ax
jb 0x2cf7
movzwl %ax, %eax
movzwl 0x6(%rsp), %ecx
movw %cx, 0xaa4(%r12,%rax,2)
movl %ebp, %r15d
jmp 0x2ec9
movw $0x153, (%r14)          # imm = 0x153
movq %r14, %rdi
movl %r15d, %esi
callq 0x28a8
movq %r12, %rdi
addq $0x4, %rdi
leaq 0x554(%r14), %rsi
movl $0x550, %edx            # imm = 0x550
callq 0x1090
movl %r13d, %r15d
jmp 0x2d72
movq %r12, %rdi
addq $0x4, %rdi
leaq 0x554(%r14), %rsi
movl $0x550, %edx            # imm = 0x550
callq 0x1090
movl (%rsp), %eax
movslq %eax, %rsi
shlq $0xc, %rsi
xorl %r13d, %r13d
movq %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1, %ebp
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 0x2f33
movw $0x153, (%r14)          # imm = 0x153
movzwl 0xd4c(%r14), %ebp
movq %r14, %rdi
movl %r13d, %esi
callq 0x28a8
incl %eax
movzwl 0x2(%r14), %ecx
decl %ecx
cmpw %ax, %cx
jbe 0x2e3a
movzwl %cx, %esi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
addq %rsi, %rdx
leaq (%r14,%rsi,2), %rsi
addq $0xaa4, %rsi            # imm = 0xAA4
movl %edx, %edi
movzwl 0xaa4(%r14,%rdi,2), %edi
movw %di, (%rsi)
decq %rdx
addq $-0x2, %rsi
decl %ecx
cmpw %cx, %ax
jb 0x2e1e
movzwl %ax, %eax
movzwl 0x6(%rsp), %ecx
movw %cx, 0xaa4(%r14,%rax,2)
leaq 0x4(%r12), %rdi
leaq 0x554(%r14), %rsi
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 %bp, 0xaa4(%r12)
jmp 0x2ec9
movzwl 0x6(%rsp), %ebp
leaq 0x4(%r12), %rdi
leaq 0x554(%r14), %rsi
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 %bp, 0xaa4(%r12)
movl %r13d, %r15d
movl (%rsp), %eax
movslq %eax, %rsi
shlq $0xc, %rsi
xorl %r13d, %r13d
movq %rbx, %rdi
xorl %edx, %edx
callq 0x10d0
movl $0x1, %ebp
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, %r15
andl $0xffffff00, %r13d      # imm = 0xFFFFFF00
orq %r15, %r13
movzbl %bpl, %eax
orq %r13, %rax
movl %ebx, %edx
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop