#define TREE_LIST_C
#include "my_assert.h"
#include "app/tlst/tree_list.h"

#if TREE_LIST_DEEP_ENABLE
// @tree_list_foreach
// Tree-List iteration function
// Walks the specified node seaching for derived nodes. Each node found is passed to the specified
// by user function @fcall with the specified user dependent context @ctx
// Function walks the current node while @fcall returns 'true', and returns current node immediately as
// far as @fcall returns 'false'. The function returns NULL if @fcall never returns 'false'.
// @ref_node - the tree node to walk in;
// @fcall - user defined function callback;
// @ctx - user defined function context;
// Returns: tree-list node in case @fcall have returned 'false', or NULL otherwise.
const sTreeList_t * tree_list_foreach( const sTreeList_t * ref_node, fTreeListForEach_t fcall, void * ctx )
{
    for( const sTreeList_t * iter = ref_node->c_list; NULL != iter; ++iter )
    {
         if( ! fcall( iter, ctx ) )
         {
             return iter;
         }
    }

    return NULL;
}
#endif

// @tree_list_search
// Search in the tree list @in for the node specified by @key using
// ... the compare function @fcmp.
// @in - the tree list to search in, shall not be NULL;
// @search_key - the node key to search for, shall not be NULL;
// @fcmp - nodes comparator, shall not be NULL; 
// @def_node - the default node to be returned if no node found by @search_key, can be NULL;
// Returns: the node pointer if found, @def_node otherwise.
// Note: The list @in shall be terminated by DECLARE_TREE_LIST_LAST() element.
const sTreeList_t * tree_list_search( const sTreeList_t * in,
                                      const void * search_key,
                                      fTreeListComparer_t fcmp,
                                      const sTreeList_t * def_node )
{
    my_assert( in );
    my_assert( search_key );
    my_assert( fcmp );

    for( const sTreeList_t * iter = in->list; NULL != iter; ++iter )
    {
         if( NULL == iter->key )
         {
             break;
         }
         else
         if( fcmp( iter->key, search_key ) )
         {
             return iter;
         }
    }

    return def_node;
}

#if TREE_LIST_DEEP_ENABLE
// @sTreeListSearchDeepCtx_t
// Internal context structure for @tree_list_search_deep
typedef struct
{
   const void * v_nextkey;                // next key to search
   const void * v_ret_node;               // found node
   #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
   const void * v_ret_pnode;              // parent of found node
   #endif
   const sTreeList_t * def_node;          // this node is considered as default
   const sTreeListSearchDeep_FSet_t * fs; // @fs parameter in 'tree_list_search_deep' function
   void * user_ctx;                       // user context
}
sTreeListSearchDeepCtx_t;


// @tl_search_deep_iterator
// Internal function.
// A @tree_list_foreach routine callback in @tree_list_search_deep_int implementation
// Modifies on return:
// [true]:
// - none
// [false]:
// - ctx->v_ret_node
static bool tl_search_deep_iterator( const sTreeList_t * node, void * _ctx )
{
    sTreeListSearchDeepCtx_t * ctx = (sTreeListSearchDeepCtx_t*)_ctx;

    my_assert( ctx->fs );
    my_assert( ctx->fs->fcmp );    
    my_assert( ctx->v_nextkey );

    (void)ctx->v_ret_node;         // keep value, do not touch
    #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
    (void)ctx->v_ret_pnode;        // keep value, do not touch
    #endif

    if( NULL == node->key )        // end-of-list [DECLARE_TREE_LIST_LAST] 
    {
        (void)ctx->v_ret_node;
        #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
        (void)ctx->v_ret_pnode;
        #endif
        return false;              // terminate walking
    }
    else
    if( ctx->fs->fcmp( node->key, ctx->v_nextkey ) )
    {
        #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
        // check if user agree the result
        if( ctx->fs->fagree( node, ctx->v_ret_pnode, ctx->user_ctx ) )
        {
        #endif
        ctx->v_ret_node = node;    // found
        #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
        (void)ctx->v_ret_pnode;    // keep value, do not touch
        #endif
        return false;              // terminate walking
        #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
        } // if not then continue searching
        #endif
    }

    // Check if no default node is assigned yet;
    // Check if the default node detector function is specified;
    // Check if the node is consideted as default;
    if( (NULL == ctx->def_node) && (NULL != ctx->fs->fisdef) && ctx->fs->fisdef( node, ctx->user_ctx ) )
    {
        ctx->def_node = node; // current node is the default node
    }

    (void)ctx->v_ret_node;
    #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
    (void)ctx->v_ret_pnode;
    #endif
    return true; // continue walking
}

// @tree_list_search_deep_int
// Internal function.
// Extended search for a key chain in the tree-list.
// @ref_node - base node, root, the node to start search from;
// @fs - functional set, see @sTreeListSearchDeep_FSet_t:
//  - fnext: user-defined abstract routine to retrieve next key in the chain;
//  - fcmp: user-defined abstract predicate to compare nodes;
//  - fisdef: user-defined predicate to determine if the node is the "default" node;
//  - freclim: user-defined abstract routine to limit recursive calls, see @fTreeListSearchDeepRecursiveLimiter_t;
// @v_nextkey_override - override key to search, shall be NULL to use @fs->fnext, or this value will be used;
// @user_ctx - user-dependent context, will be passed to routines in functional set @fs;
// Returns:
//  NON NULL: found node;
//  NULL: node not found;
static const sTreeList_t * tree_list_search_deep_int( const sTreeList_t * ref_node, 
                                                      const sTreeListSearchDeep_FSet_t * fs,
                                                      const void * v_nextkey_override,
                                                      void * user_ctx )
{
   const sTreeList_t * found = NULL;

   my_assert( ref_node );
   my_assert( fs );   
   my_assert( fs->fnext );
   my_assert( fs->fcmp );

   sTreeListSearchDeepCtx_t ctx = { 
    .v_nextkey = v_nextkey_override,
    .v_ret_node = NULL,
    .def_node = NULL,
    .fs = fs,
    .user_ctx = user_ctx,
   };

   do
   {
       if( NULL == v_nextkey_override )
       {
           // get next key to search in the tree list
           ctx.v_nextkey = fs->fnext( user_ctx );
       }
       else
       {
           // use predefined key to search
           (void)ctx.v_nextkey;
       }

       // check if there no next key
       if( NULL == ctx.v_nextkey ) break; // invalid key

       ctx.v_ret_node = NULL;
       #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
       ctx.v_ret_pnode = ref_node;
       #endif
       ctx.def_node = NULL;

       // search for the key in tree list
       if( NULL != tree_list_foreach( ref_node, tl_search_deep_iterator, &ctx ) )
       {
           // not found?
           if( NULL == ctx.v_ret_node )
           {
               // try the default node if possible
               if( NULL != ctx.def_node )
               {
                   // search recursevely (support optional/default nodes):
                   // - specify the default node found as root node;
                   // - pass the same function set (@fs);
                   // - override the key to search (do not use @fs->fnext);
                   // - limit recursive calls by user defined way;
                   #if TREE_LIST_RECURSIVE_LIMIT_ENABLE
                   bool possbile_call = true;
                   // check if recursive call is possible:
                   if( NULL != fs->freclim )
                   {
                       possbile_call = fs->freclim( true, user_ctx );  // enter recursive call
                   }
                   if( possbile_call )
                   {
                       found = tree_list_search_deep_int( ctx.def_node, fs, ctx.v_nextkey, user_ctx ); 
                   }
                   if( NULL != fs->freclim )
                   {
                       fs->freclim( false, user_ctx ); // leave recursive call
                   }
                   #else
                   found = tree_list_search_deep_int( ctx.def_node, fs, ctx.v_nextkey, user_ctx ); 
                   #endif 
               }
               else
               {
                   // assign NULL due to the element not found
                   found = NULL;
                   #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
                   (void)ctx.v_ret_pnode;
                   #endif
               }
           }
           else
           {
               // assign the element found
               found = ctx.v_ret_node;
               #if TREE_LIST_DEEP_PARENT_SEARCH_ENABLE
               (void)ctx.v_ret_pnode;
               #endif
           }

           if( NULL == found )
           {
               break; // not found
           }
           else
           {
               if( NULL != v_nextkey_override )
               {
                   v_nextkey_override = NULL;
               }
           }
       }
       else break; // end-of-list, element not found

       ref_node = found; // found, go next key in the chain
   }
   while( NULL != found );

   return found;
}

// @tree_list_search_deep_int
// Extended search for a key chain in the tree-list.
// @ref_node - base node, root, the node to start search from;
// @fs - functional set, see @sTreeListSearchDeep_FSet_t:
//  - fnext: user-defined abstract routine to retrieve next key in the chain;
//  - fcmp: user-defined abstract predicate to compare nodes;
//  - fisdef: user-defined predicate to determine if the node is the "default" node;
//  - freclim: user-defined abstract routine to limit recursive calls, see @fTreeListSearchDeepRecursiveLimiter_t;
// @user_ctx - user-dependent context, will be passed to routines in functional set @fs;
// Returns:
//  NON NULL: found node;
//  NULL: node not found;
const sTreeList_t * tree_list_search_deep( const sTreeList_t * ref_node, 
                                           const sTreeListSearchDeep_FSet_t * fs,
                                           void * user_ctx )
{
   return tree_list_search_deep_int( ref_node, fs, NULL, user_ctx );
}
#endif