// File: /workspace/github_cpp_makefile_star2_O2/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 $0x18, %rsp
movq %r8, %r15
movl %ecx, %ebx
movq %rdx, %r14
movl %esi, %r12d
movq %rdi, %r13
cmpw $0x0, 0x2(%rdx)
je 0x2872
movl %ebx, (%rsp)
leaq 0x4(%r14), %rbx
movzwl (%r14), %eax
leaq 0x14(%rsp), %rdx
movl %r12d, (%rdx)
leaq (%r14,%rax,4), %rsi
addq $0x4, %rsi
movq %rbx, %rdi
callq 0x2ea2
subq %rbx, %rax
shrq $0x2, %rax
movzwl (%r14), %ecx
leal -0x1(%rcx), %edx
cmpl %ecx, %eax
cmovll %eax, %edx
movslq %edx, %rax
leal 0x1(%rax), %ecx
cmpl %r12d, 0x4(%r14,%rax,4)
movslq %ecx, %rcx
cmovgq %rax, %rcx
movzwl 0xaa4(%r14,%rcx,2), %ebp
movl $0x1000, %edi           # imm = 0x1000
callq 0x10a0
movq %rax, %rbx
andl $0x0, (%rax)
movl %ebp, %esi
shll $0xc, %esi
movq %r15, %rdi
xorl %edx, %edx
callq 0x10c0
pushq $0x1
popq %rdx
movl $0x1000, %esi           # imm = 0x1000
movq %rbx, %rdi
movq %r15, %rcx
callq 0x10f0
movq %r13, %rdi
movl %r12d, %esi
movq %rbx, %rdx
movl %ebp, %ecx
movq %r15, 0x8(%rsp)
movq %r15, %r8
callq 0x273e
movq %rax, %r12
movl %edx, %r15d
movl $0x1000, %esi           # imm = 0x1000
movq %rbx, %rdi
callq 0x10b0
testb $0x1, %r12b
je 0x292e
cmpw $0x2a7, (%r14)          # imm = 0x2A7
ja 0x2946
movl $0xffffffff, %ebx       # imm = 0xFFFFFFFF
shrq $0x20, %r12
movq %r14, %rdi
movl %r12d, %esi
callq 0x2624
incl %eax
movzwl 0x2(%r14), %ecx
movzwl %ax, %eax
movq %rcx, %rdi
movq 0x8(%rsp), %rdx
cmpq %rax, %rdi
jbe 0x2913
leal (%rdi,%rbx), %esi
movzwl 0xaa4(%r14,%rsi,2), %esi
movw %si, 0xaa4(%r14,%rdi,2)
decq %rdi
jmp 0x284f
cmpw $0x2a7, (%r14)          # imm = 0x2A7
ja 0x2892
movq %r14, %rdi
movl %r12d, %esi
callq 0x2624
movq %r14, %rdi
movl %ebx, %esi
movq %r15, %rdx
jmp 0x2929
movl %ebx, %r13d
movq %r15, 0x8(%rsp)
movl $0xff8, %edi            # imm = 0xFF8
callq 0x10a0
movq %rax, %r15
andw $0x0, 0x2(%rax)
movl 0x550(%r14), %ebx
movl 0x554(%r14), %ebp
movw $0x154, %ax             # imm = 0x154
movw %ax, (%r14)
movw %ax, (%r15)
xorl %ecx, %ecx
cmpl %r12d, %ebp
setl %cl
cmpl %r12d, %ebx
movl $0xff, %eax
cmovlel %ecx, %eax
cmpb $-0x1, %al
je 0x29ee
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x2a1f
movw $0x153, (%r15)          # imm = 0x153
pushq $0x1
popq %rax
cmpq $0x154, %rax            # imm = 0x154
je 0x29e1
movl 0x554(%r14,%rax,4), %ecx
movl %ecx, (%r15,%rax,4)
incq %rax
jmp 0x28f6
movw %r15w, 0xaa4(%r14,%rax,2)
incl %ecx
movw %cx, 0x2(%r14)
movq %r14, %rdi
movl (%rsp), %esi
callq 0x2e2d
movq (%r13), %rcx
movq %rcx, %rbp
shrq $0x20, %rbp
movzwl 0x8(%r13), %ebx
movq %rcx, %rax
jmp 0x2bdd
movl $0x1000, %edi           # imm = 0x1000
callq 0x10a0
movq %rax, %r13
movl 0x550(%r14), %ebp
movl 0x554(%r14), %ebx
movw $0x154, %ax             # imm = 0x154
movw %ax, (%r14)
movw %ax, (%r13)
movw $0x155, %ax             # imm = 0x155
movw %ax, 0x2(%r14)
movw %ax, 0x2(%r13)
shrq $0x20, %r12
xorl %ecx, %ecx
cmpl %r12d, %ebx
setl %cl
cmpl %r12d, %ebp
movl $0xff, %eax
cmovlel %ecx, %eax
cmpb $-0x1, %al
je 0x2ad6
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x2b71
movw $0x153, (%r13)          # imm = 0x153
xorl %eax, %eax
cmpq $0x2a6, %rax            # imm = 0x2A6
je 0x2a75
movl 0x558(%r14,%rax,2), %ecx
movl %ecx, 0x4(%r13,%rax,2)
movzwl 0xd4e(%r14,%rax), %ecx
movw %cx, 0xaa4(%r13,%rax)
addq $0x2, %rax
jmp 0x29b0
movq %r15, %rdi
movl %r12d, %esi
callq 0x2624
jmp 0x2a3e
movw $0x153, (%r14)          # imm = 0x153
movq %r14, %rdi
movl %r12d, %esi
callq 0x2624
xorl %eax, %eax
cmpq $0x154, %rax            # imm = 0x154
je 0x2a1b
movl 0x554(%r14,%rax,4), %ecx
movl %ecx, 0x4(%r15,%rax,4)
incq %rax
jmp 0x2a01
movl %ebx, %ebp
jmp 0x2a3e
xorl %eax, %eax
cmpq $0x154, %rax            # imm = 0x154
je 0x2a3b
movl 0x554(%r14,%rax,4), %ecx
movl %ecx, 0x4(%r15,%rax,4)
incq %rax
jmp 0x2a21
movl %r12d, %ebp
movq %r14, %rdi
movl %r13d, %esi
movq 0x8(%rsp), %rbx
movq %rbx, %rdx
callq 0x2e2d
movq %r15, %rdi
movq %rbx, %rsi
callq 0x2e62
movl %eax, %ebx
movl $0xff8, %esi            # imm = 0xFF8
movq %r15, %rdi
callq 0x10b0
pushq $0x1
popq %rax
xorl %ecx, %ecx
jmp 0x2bdd
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
movzwl 0xff4(%r14), %eax
movw %ax, 0xd4a(%r13)
movq %r13, %rdi
movl %r12d, %esi
callq 0x2624
incl %eax
movzwl 0x2(%r13), %ecx
decl %ecx
movzwl %cx, %ecx
cmpw %ax, %cx
jbe 0x2ac0
leal (%rcx,%rbp), %edx
movzwl 0xaa4(%r13,%rdx,2), %edx
movw %dx, 0xaa4(%r13,%rcx,2)
decq %rcx
jmp 0x2aa1
movzwl %ax, %eax
movw %r15w, 0xaa4(%r13,%rax,2)
movl %ebx, %ebp
movl (%rsp), %esi
jmp 0x2bae
movl $0xffffffff, %ebx       # imm = 0xFFFFFFFF
movw $0x153, (%r14)          # imm = 0x153
movzwl 0xd4c(%r14), %eax
movw %ax, 0x6(%rsp)
movq %r14, %rdi
movl %r12d, %esi
callq 0x2624
incl %eax
movzwl 0x2(%r14), %ecx
decl %ecx
movzwl %cx, %ecx
cmpw %ax, %cx
jbe 0x2b24
leal (%rcx,%rbx), %edx
movzwl 0xaa4(%r14,%rdx,2), %edx
movw %dx, 0xaa4(%r14,%rcx,2)
decq %rcx
jmp 0x2b05
movzwl %ax, %eax
movw %r15w, 0xaa4(%r14,%rax,2)
xorl %eax, %eax
movl (%rsp), %esi
cmpq $0x2a8, %rax            # imm = 0x2A8
je 0x2b62
movl 0x554(%r14,%rax,2), %ecx
movl %ecx, 0x4(%r13,%rax,2)
movzwl 0xd4e(%r14,%rax), %ecx
movw %cx, 0xaa6(%r13,%rax)
addq $0x2, %rax
jmp 0x2b35
movzwl 0x6(%rsp), %eax
movw %ax, 0xaa4(%r13)
jmp 0x2bae
xorl %eax, %eax
movl (%rsp), %esi
cmpq $0x2a8, %rax            # imm = 0x2A8
je 0x2ba3
movl 0x554(%r14,%rax,2), %ecx
movl %ecx, 0x4(%r13,%rax,2)
movzwl 0xd4e(%r14,%rax), %ecx
movw %cx, 0xaa6(%r13,%rax)
addq $0x2, %rax
jmp 0x2b76
movw %r15w, 0xaa4(%r13)
movl %r12d, %ebp
movq %r14, %rdi
movq 0x8(%rsp), %rbx
movq %rbx, %rdx
callq 0x2e2d
movq %r13, %rdi
movq %rbx, %rsi
callq 0x2e62
movl %eax, %ebx
movl $0x1000, %esi           # imm = 0x1000
movq %r13, %rdi
callq 0x10b0
xorl %ecx, %ecx
pushq $0x1
popq %rax
shlq $0x20, %rbp
andl $0xffffff00, %ecx       # imm = 0xFFFFFF00
orq %rbp, %rcx
movzbl %al, %eax
orq %rcx, %rax
movl %ebx, %edx
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop