/*****************************************************************************/
/* Copyright (c) 2009 NXP B.V.                                               */
/*                                                                           */
/* This library is free software; you can redistribute it and/or             */
/* modify it under the terms of the GNU Lesser General Public                */
/* License as published by the Free Software Foundation, using               */
/* version 2.1 of the License.                                               */
/*                                                                           */
/* This library is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
/* Lesser General Public License for more details.                           */
/*                                                                           */
/* You should have received a copy of the GNU Lesser General Public          */
/* License along with this library; if not, write to the Free Software       */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
/*                                                                           */
/*****************************************************************************/
/**
    @file   phStbDbg_Tree.c
    @brief  Generic tree datastructure

@b  Component:  phStbDbg

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20081107    batelaan    Initial
  2 20090120    batelaan    Update file copyright/license text

--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/***********************************************
* INCLUDE FILES                                *
************************************************/

#include "tmNxCompId.h"
#include "phStbDbg.h"
#include "phStbDbg_Tree.h"
#include <string.h>
#if defined(UNITTEST)
#include <module.h>
#endif

/***********************************************
* LOCAL MACROS                                 *
* recommendation only: <MODULE><_WORD>+        *
************************************************/

#if defined(UNITTEST)
  #define TRACE(...) do {printk(KERN_DEBUG __VA_ARGS__);} while (0)
#else
  #define TRACE(...) do {} while (0)
#endif

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                [p]phStbTree_<Word>+_t  *
*******************************************************************/

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/

/**************************************************************************
* EXPORTED DATA                     g[k|p|kp|pk|kpk]phStbTree_<Word>+ *
***************************************************************************/

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          ph[<layer>]<Module>[_<Word>+] for exported functions*
********************************************************************************/

#if defined(UNITTEST)
static void StbDbg_NodeDump(const phStbDbg_TreeNode_t *node, unsigned int level)
{
    unsigned int l;
    const phStbDbg_TreeNode_t *child;
    char msg[DBG_KEY_MAX_LEN]; /* Hopefully enough for debugging!!! */
    for (l = 0; l < level; l++)
    {
        msg[l] = ' ';
    }
    msg[level] = 0;
    strcat(msg, node->base.name);
    if (node->value != NULL)
    {
        strcat(msg, "=");
        strcat(msg, node->value);
    }
    TRACE("%s\n", msg);
    for (child = node->base.children; child != NULL; child = child->base.next)
    {
        StbDbg_NodeDump(child, level + 4);
    }
}
#else /* #if defined(UNITTEST) */
#define StbDbg_NodeDump(_node, _level) do {} while (0)
#endif /* #else #if defined(UNITTEST) */

tmErrorCode_t phStbDbg_TreeCreate(
    const phStbDbg_TreeSetup_t *setup,
    size_t payloadSize,
    const char *value,
    const phStbDbg_TreeNode_t **pRoot)
{
    phStbDbg_TreeNode_t *root;
    root = setup->allocator(sizeof(phStbDbg_TreeNodeBase_t) + payloadSize);
    if (root == NULL)
    {
        TRACE("phStbDbg_TreeCreate allocator=>NULL\n");
        return PH_STBDBG_TREE_ERR_ALLOCATOR;
    }
    root->base.setup = (phStbDbg_TreeSetup_t*)setup;
    root->base.parent = NULL;
    root->base.next = NULL;
    root->base.children = NULL;
    root->base.name = "/";
    root->value = (char*)value;
    *pRoot = root;
    StbDbg_NodeDump(root, 0);
    return TM_OK;
}

static tmErrorCode_t phStbDbg_TreeGetOrCreateNodeEx(
    bool createNewNodeIfNotFound,
    const phStbDbg_TreeNode_t *root,
    const char *path,
    size_t payloadSize,
    const phStbDbg_TreeNode_t **pNode,
    const char *value)
{
    phStbDbg_TreeNode_t *node = (phStbDbg_TreeNode_t*)root;

    /* if path is: "aaaaaaaaaa/bbbbbbbbb/ccccccccc"
       nodeName =>  ^          ^         ^
       nextNodeName            ^        ^         ^
       nodeNameLen is length of nodeName to nextNodeName or next "/".
       So if *nextNodeName is NUL, then we are dealing with the last node in the path.
    */
    const char *nodeName = path;
    size_t nodeNameLen;
    const char *nextNodeName;

    tmErrorCode_t ret = TM_OK;

    /* Skip leading slashes. */
    while (*nodeName == '/')
    {
        nodeName++;
    }

    /* Handle each part of the path... */
    for (/*nop*/; *nodeName; nodeName = nextNodeName)
    {
        phStbDbg_TreeNode_t *child;

        /* Find child node name: */
        nextNodeName = strchr(nodeName, '/');
        if (nextNodeName == NULL)
        {
            nodeNameLen = strlen(nodeName);
            nextNodeName = nodeName + nodeNameLen;
        }
        else
        {
            nodeNameLen = (size_t)(nextNodeName - nodeName);
        }

        /* Handle trailing or multiple slashes */
        while (*nextNodeName == '/')
        {
            nextNodeName++;
        }
        /* If *nextNodeName == 0 then we are dealing with the last
           node on the path. We'll use that test later on... */

        TRACE("phStbDbg_TreeGetOrCreateNodeEx nodeName=%s\n", nodeName);

        /* Find child node: */
        for (child = node->base.children;
             child != NULL;
             child = child->base.next)
        {
            if ((strncmp(nodeName, child->base.name, nodeNameLen) == 0)
                && (child->base.name[nodeNameLen] == 0))
            {
                /* found child node */
                TRACE("phStbDbg_TreeGetOrCreateNodeEx childnode name (%s) found\n", child->base.name);
                node = child;
                break;
            }
        }

        if (child == NULL)
        {
            /* Not found. */
            TRACE("phStbDbg_TreeGetOrCreateNodeEx child not found\n");
            if (createNewNodeIfNotFound)
            {
                phStbDbg_TreeNode_t *newNode;
                /* Check that we are not setting a value on a non-leaf
                 * node if this is not allowed. */
                if (((root->base.setup->flags & PH_STBDBG_TREE_VALUE_ON_LEAF_ONLY) != 0U)
                    && (node->value != NULL))
                {
                    TRACE("phStbDbg_TreeGetOrCreateNodeEx leaf node can't have child!\n");
                    return PH_STBDBG_TREE_ERR_NODE_LEAF;
                }
                TRACE("phStbDbg_TreeGetOrCreateNodeEx creating child node\n");
                if (payloadSize < sizeof(char*))
                {
                    return PH_STBDBG_TREE_ERR_PAYLOADSIZE_TOO_SMALL;
                }
                newNode = root->base.setup->allocator(sizeof(root->base) + payloadSize);
                if (newNode == NULL)
                {
                    TRACE("phStbDbg_TreeGetOrCreateNodeEx allocator=>NULL\n");
                    return PH_STBDBG_TREE_ERR_ALLOCATOR;
                }
                ret = PH_STBDBG_TREE_ERR_NODE_CREATED;
                newNode->base.setup = root->base.setup;
                newNode->base.parent = node;
                newNode->base.children = NULL;
                newNode->base.name = root->base.setup->allocator(nodeNameLen + 1);
                strncpy(newNode->base.name, nodeName, nodeNameLen);
                newNode->base.name[nodeNameLen] = 0;
                if (node->base.children == NULL)
                {
                    TRACE("phStbDbg_TreeGetOrCreateNodeEx inserting as first child\n");
                    node->base.children = newNode;
                    newNode->base.next = NULL;
                }
                else
                {
                    phStbDbg_TreeNode_t *lastChild = NULL;
                    /* Insert in alphabetical sorted order: */
                    for (child = node->base.children;
                         child != NULL;
                         child = child->base.next)
                    {
                        if (strcmp(child->base.name, newNode->base.name) > 0) {
                            TRACE("phStbDbg_TreeGetOrCreateNodeEx inserting before (%s)\n", child->base.name);
                            newNode->base.next = child;
                            if (lastChild == NULL)
                            {
                                node->base.children = newNode;
                            }
                            else
                            {
                                lastChild->base.next = newNode;
                            }
                            break;
                        }
                        lastChild = child;
                    }
                    if (child == NULL)
                    {
                        /* not yet inserted. */
                        TRACE("phStbDbg_TreeGetOrCreateNodeEx inserting as last child, after (%s)\n", lastChild->base.name);
                        newNode->base.next = NULL;
                        lastChild->base.next = newNode;
                    }
                }
                /* Leaf node? */
                if ((*nextNodeName) == 0)
                {
                    newNode->value = (char*)value;
                    StbDbg_NodeDump(root, 0);
                }
                else
                {
                    newNode->value = NULL;
                }
                node = newNode;
            }
            else
            {
                TRACE("phStbDbg_TreeGetOrCreateNodeEx => PH_STBDBG_TREE_ERR_NODE_NOT_FOUND\n");
                *pNode = NULL;
                return PH_STBDBG_TREE_ERR_NODE_NOT_FOUND;
            }
        } /* if child not found. */
    }
    *pNode = node;
    return TM_OK;
}

tmErrorCode_t phStbDbg_TreeGetNode(
    const phStbDbg_TreeNode_t *root,
    const char *path,
    const phStbDbg_TreeNode_t **pNode)
{
    tmErrorCode_t err = phStbDbg_TreeGetOrCreateNodeEx(
        false, root, path, 0, pNode, NULL);
    return err;

}

tmErrorCode_t phStbDbg_TreeGetOrCreateNode(
    const phStbDbg_TreeNode_t *root,
    const char *path,
    size_t payloadSize,
    const phStbDbg_TreeNode_t **pNode,
    const char *value)
{
    tmErrorCode_t err = phStbDbg_TreeGetOrCreateNodeEx(
        true, root, path, payloadSize, pNode, value);
    return err;
}

phStbDbg_TreeNode_t *phStbDbg_TreeGetRoot(
    const phStbDbg_TreeNode_t *node)
{
    while (node->base.parent != NULL)
    {
        node = node->base.parent;
    }
    return (phStbDbg_TreeNode_t*)node;
}

phStbDbg_TreeNode_t *phStbDbg_TreeNextNode(
    const phStbDbg_TreeNode_t *node,
    phStbDbg_TreeOrder_t order)
{
    const phStbDbg_TreeNode_t *dest = node;
    DBG_ASSERT(order == phStbDbg_TreeOrder_LeafOnly);

    TRACE("phStbDbg_TreeNextNode \"%s\"\n", node->base.name);
    while (dest != NULL)
    {
        /* go down first child chain. */
        while (dest->base.children != NULL)
        {
            dest = dest->base.children;
            TRACE("phStbDbg_TreeNextNode goto child \"%s\"\n", dest->base.name);
        }

        /* New node found? if so return it. */
        if (dest != node)
        {
            TRACE("phStbDbg_TreeNextNode => \"%s\"\n", dest->base.name);
            return (phStbDbg_TreeNode_t*)dest;
        }

        /* Any sibling nodes? */
        if (dest->base.next != NULL)
        {
            dest = dest->base.next;
            TRACE("phStbDbg_TreeNextNode goto next \"%s\"\n", dest->base.name);
        }
        else
        {
            /* ascend up the tree to find a non-NULL next branch. */
            for (;;)
            {
                dest = dest->base.parent;
                if (dest == NULL)
                {
                    break;
                }
                if (dest->base.next != NULL)
                {
                    TRACE("phStbDbg_TreeNextNode ascending to \"%s\"\n", dest->base.name);
                    dest = dest->base.next;
                    break;
                }
            }
        }
    }
    TRACE("phStbDbg_TreeNextNode => NULL\n");
    return NULL;
}

tmErrorCode_t phStbDbg_TreeGetNodePath(
    const phStbDbg_TreeNode_t *node,
    char *path)
{
    const phStbDbg_TreeNode_t *parentNode;
    unsigned int len = 0;
    /* First work out total length of parent path, excluding root node. */
    for (parentNode = node->base.parent;
         parentNode != NULL && parentNode->base.parent != NULL;
         parentNode = parentNode->base.parent)
    {
        len++; /* For a slash */
        len += strlen(parentNode->base.name);
    }
    TRACE("phStbDbg_TreeGetNodePath \"%s\": len of parent path = %u\n", node->base.name, len);
    /* Now fill in the path from the back. */
    strcpy(path + len, node->base.name);
    for (parentNode = node->base.parent;
         parentNode != NULL && parentNode->base.parent != NULL;
         parentNode = parentNode->base.parent)
    {
        size_t parentNameLen = strlen(parentNode->base.name);
        len--;
        path[len] = '/';
        len -= parentNameLen;
        strncpy(path + len, parentNode->base.name, parentNameLen);
    }
    TRACE("phStbDbg_TreeGetNodePath \"%s\" => \"%s\"\n", node->base.name, path);
    return TM_OK;
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
