/*
 * Binární vyhledávací strom — rekurzivní varianta
 *
 * S využitím datových typů ze souboru btree.h a připravených koster funkcí
 * implementujte binární vyhledávací strom pomocí rekurze.
 */

#include "../btree.h"
#include <stdio.h>
#include <stdlib.h>

/*
 * Inicializace stromu.
 *
 * Uživatel musí zajistit, že inicializace se nebude opakovaně volat nad
 * inicializovaným stromem. V opačném případě může dojít k úniku paměti (memory
 * leak). Protože neinicializovaný ukazatel má nedefinovanou hodnotu, není
 * možné toto detekovat ve funkci.
 */
void bst_init(bst_node_t **tree)
{
  *tree = NULL;  // Inicializace prázdného stromu
}

/*
 * Vyhledání uzlu v stromu.
 *
 * V případě úspěchu vrátí funkce hodnotu true a do proměnné value zapíše
 * ukazatel na obsah daného uzlu. V opačném případě funkce vrátí hodnotu false a proměnná
 * value zůstává nezměněná.
 *
 * Funkci implementujte rekurzivně bez použité vlastních pomocných funkcí.
 */
bool bst_search(bst_node_t *tree, char key, bst_node_content_t **value)
{
    // Prázdný strom nebo došli jsme na konec větve
    if (tree == NULL) {
        return false;
    }
    
    // Porovnáme klíče
    if (key < tree->key) {
        // Hledáme v levém podstromu
        return bst_search(tree->left, key, value);
    } else if (key > tree->key) {
        // Hledáme v pravém podstromu
        return bst_search(tree->right, key, value);
    } else {
        // Našli jsme hledaný uzel
        *value = &(tree->content);
        return true;
    }
}

/*
 * Vložení uzlu do stromu.
 *
 * Pokud uzel se zadaným klíče už ve stromu existuje, nahraďte jeho hodnotu.
 * Jinak vložte nový listový uzel.
 *
 * Výsledný strom musí splňovat podmínku vyhledávacího stromu — levý podstrom
 * uzlu obsahuje jenom menší klíče, pravý větší.
 *
 * Funkci implementujte rekurzivně bez použití vlastních pomocných funkcí.
 */
void bst_insert(bst_node_t **tree, char key, bst_node_content_t value)
{
    // Pokud je strom prázdný nebo jsme došli na místo pro vložení
    if (*tree == NULL) {
        // Vytvoříme nový uzel
        bst_node_t *new_node = (bst_node_t *)malloc(sizeof(bst_node_t));
        if (new_node == NULL) {
            return;  // Chyba alokace
        }
        
        new_node->key = key;
        new_node->content = value;
        new_node->left = NULL;
        new_node->right = NULL;
        
        *tree = new_node;
        return;
    }
    
    // Porovnáme klíče
    if (key < (*tree)->key) {
        // Vkládáme do levého podstromu
        bst_insert(&(*tree)->left, key, value);
    } else if (key > (*tree)->key) {
        // Vkládáme do pravého podstromu
        bst_insert(&(*tree)->right, key, value);
    } else {
        // Klíč už existuje - nahradíme hodnotu
        (*tree)->content = value;
    }
}

/*
 * Pomocná funkce která nahradí uzel nejpravějším potomkem.
 *
 * Klíč a hodnota uzlu target budou nahrazeny klíčem a hodnotou nejpravějšího
 * uzlu podstromu tree. Nejpravější potomek bude odstraněný. Funkce korektně
 * uvolní všechny alokované zdroje odstraněného uzlu.
 *
 * Funkce předpokládá, že hodnota tree není NULL.
 *
 * Tato pomocná funkce bude využitá při implementaci funkce bst_delete.
 *
 * Funkci implementujte rekurzivně bez použití vlastních pomocných funkcí.
 */
void bst_replace_by_rightmost(bst_node_t *target, bst_node_t **tree)
{
    if ((*tree)->right == NULL) {
        // Našli jsme nejpravější uzel
        target->key = (*tree)->key;
        target->content = (*tree)->content;
        
        // Uložíme si levý podstrom
        bst_node_t *left_subtree = (*tree)->left;
        // Uvolníme nejpravější uzel
        free(*tree);
        // Napojíme levý podstrom
        *tree = left_subtree;
    } else {
        // Pokračujeme v hledání nejpravějšího uzlu
        bst_replace_by_rightmost(target, &(*tree)->right);
    }
}

/*
 * Odstranění uzlu ze stromu.
 *
 * Pokud uzel se zadaným klíčem neexistuje, funkce nic nedělá.
 * Pokud má odstraněný uzel jeden podstrom, zdědí ho rodič odstraněného uzlu.
 * Pokud má odstraněný uzel oba podstromy, je nahrazený nejpravějším uzlem
 * levého podstromu. Nejpravější uzel nemusí být listem.
 *
 * Funkce korektně uvolní všechny alokované zdroje odstraněného uzlu.
 *
 * Funkci implementujte rekurzivně pomocí bst_replace_by_rightmost a bez
 * použití vlastních pomocných funkcí.
 */
void bst_delete(bst_node_t **tree, char key)
{
    if (*tree == NULL) {
        return;  // Strom je prázdný nebo uzel nebyl nalezen
    }
    
    if (key < (*tree)->key) {
        // Mažeme v levém podstromu
        bst_delete(&(*tree)->left, key);
    } else if (key > (*tree)->key) {
        // Mažeme v pravém podstromu
        bst_delete(&(*tree)->right, key);
    } else {
        // Našli jsme mazaný uzel
        bst_node_t *node_to_delete = *tree;
        
        if (node_to_delete->left == NULL) {
            // Uzel má jen pravý podstrom
            *tree = node_to_delete->right;
            free(node_to_delete);
        } else if (node_to_delete->right == NULL) {
            // Uzel má jen levý podstrom
            *tree = node_to_delete->left;
            free(node_to_delete);
        } else {
            // Uzel má oba podstromy
            // Nahradíme hodnoty nejpravějším uzlem levého podstromu
            bst_replace_by_rightmost(node_to_delete, &node_to_delete->left);
        }
    }
}

/*
 * Zrušení celého stromu.
 *
 * Po zrušení se celý strom bude nacházet ve stejném stavu jako po
 * inicializaci. Funkce korektně uvolní všechny alokované zdroje rušených
 * uzlů.
 *
 * Funkci implementujte rekurzivně bez použití vlastních pomocných funkcí.
 */
void bst_dispose(bst_node_t **tree)
{
    if (*tree != NULL) {
        // Rekurzivně zrušíme podstromy
        bst_dispose(&(*tree)->left);
        bst_dispose(&(*tree)->right);
        // Uvolníme aktuální uzel
        free(*tree);
        *tree = NULL;
    }
}

/*
 * Preorder průchod stromem.
 *
 * Pro aktuálně zpracovávaný uzel zavolejte funkci bst_add_node_to_items.
 *
 * Funkci implementujte rekurzivně bez použití vlastních pomocných funkcí.
 */
void bst_preorder(bst_node_t *tree, bst_items_t *items)
{
     if (tree != NULL) {
        bst_add_node_to_items(tree, items);  // Zpracujeme aktuální uzel
        bst_preorder(tree->left, items);     // Levý podstrom
        bst_preorder(tree->right, items);    // Pravý podstrom
    }
}

/*
 * Inorder průchod stromem.
 *
 * Pro aktuálně zpracovávaný uzel zavolejte funkci bst_add_node_to_items.
 *
 * Funkci implementujte rekurzivně bez použití vlastních pomocných funkcí.
 */
void bst_inorder(bst_node_t *tree, bst_items_t *items)
{
    if (tree != NULL) {
        bst_inorder(tree->left, items);      // Levý podstrom
        bst_add_node_to_items(tree, items);  // Zpracujeme aktuální uzel
        bst_inorder(tree->right, items);     // Pravý podstrom
    }
}

/*
 * Postorder průchod stromem.
 *
 * Pro aktuálně zpracovávaný uzel zavolejte funkci bst_add_node_to_items.
 *
 * Funkci implementujte rekurzivně bez použití vlastních pomocných funkcí.
 */
void bst_postorder(bst_node_t *tree, bst_items_t *items)
{
     if (tree != NULL) {
        bst_postorder(tree->left, items);    // Levý podstrom
        bst_postorder(tree->right, items);   // Pravý podstrom
        bst_add_node_to_items(tree, items);  // Zpracujeme aktuální uzel
    }
}
