/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program 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 General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/**
    @file   phStbDbg_ModuleCommon.c
    @brief  Kernel-level common OS-independent code for phStbDbg component.

@b  Component:  phStbDbg.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20081110    batelaan    Initial
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

#include <tmNxCompId.h>
#include <phStbCommon.h>
#include <phStbDbg.h>
#include <phStbDbg_Internal.h>
#include <phStbDbg_Tree.h>

/***********************************************
* 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

/******************************************************************
* FUNCTION PROTOTYPES                    [p]ph<Module>_<Word>+    *
*******************************************************************/

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                   [p]ph<Module>_<Word>+_t  *
*******************************************************************/

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

/*******************************************************************
* LOCAL DATA                                                       *
********************************************************************/

static phStbDbg_TreeSetup_t gTreeSetup = {
    PH_STBDBG_TREE_VALUE_ON_LEAF_ONLY,
    &phStbDbg_Allocator,
    &phStbDbg_Deallocator
};

static const phStbDbg_TreeNode_t *gKeyTreeRoot;

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

/******************************************************************************/
void phStbDbg_KeyInit(void)
{
    tmErrorCode_t err = phStbDbg_TreeCreate(
        &gTreeSetup,
        sizeof(char*),
        NULL,
        &gKeyTreeRoot);
    DBG_ASSERT2(err == TM_OK, ("err=0x%x", err));
    phStbDbg_KeyTreeLockInit();
}

/******************************************************************************/
tmErrorCode_t tmDbg_KeySet(
    const char *key,
    const char *value)
{
    const phStbDbg_TreeNode_t *node;
    tmErrorCode_t err;

    phStbDbg_KeyTreeLockEnter();
    if (value == NULL)
    {
        err = PH_STBDBG_TREE_ERR_NULL_VALUE;
        goto DEPARTURES;
    }
    /* TODO should check that key is < DBG_KEY_MAX_LEN. */
    TRACE("phStbDbg_KeySet \"%s\" to \"%s\"\n", key, value);
    err = phStbDbg_TreeGetOrCreateNode(
        gKeyTreeRoot,
        key,
        sizeof(char*),
        &node,
        value);
    if ((err == TM_OK) || (err == PH_STBDBG_TREE_ERR_NODE_CREATED))
    {
        if (node->value == NULL)
        {
            TRACE("StbDbg_KeySet \"%s\" is parent node\n", key);
            err = PH_STBDBG_TREE_ERR_NODE_NOT_LEAF;
            goto DEPARTURES;
        }
        /* Node returned has a different value? */
        if ((err == TM_OK) &&
            (strcmp(node->value, value) != 0))
        {
            /* Yes, update the value. */
            TRACE("StbDbg_KeySet \"%s\" update value from \"%s\" to \"%s\"\n", key,
                  node->value, value);
            phStbDbg_Deallocator(node->value);
            ((phStbDbg_TreeNode_t*)node)->value = NULL; /* Ensure we set the value below. */
        }
        /* Do we need to set the value? */
        if ((node->value == NULL) || (node->value == value))
        {
            size_t valueLen = strlen(value);
            /* Silently truncate string length if needed. */
            if (valueLen >= DBG_KEY_MAX_LEN)
            {
                valueLen = DBG_KEY_MAX_LEN - 1;
            }
            /* Must copy the value string since it may live on a stack. */
            ((phStbDbg_TreeNode_t*)node)->value = phStbDbg_Allocator(valueLen + 1);
            strncpy(node->value, value, valueLen);
            node->value[valueLen] = 0;
        }
        err = TM_OK;
    }
  DEPARTURES:
    phStbDbg_KeyTreeLockExit();
    return err;
}

/******************************************************************************/
tmErrorCode_t tmDbg_KeyGet(
    const char *key,
    char *valueStorage)
{
    const phStbDbg_TreeNode_t *node;
    tmErrorCode_t err;
    phStbDbg_KeyTreeLockEnter();
    err = phStbDbg_TreeGetNode(
        gKeyTreeRoot,
        key,
        &node);
    if (err == TM_OK)
    {
        if (node->value == NULL)
        {
            err = PH_STBDBG_TREE_ERR_NODE_NOT_LEAF;
        }
        else
        {
            strcpy(valueStorage, node->value);
        }
    }
    else
    {
        TRACE("StbDbg_KeyGet \"%s\" => 0x%x\n", key, err);
    }
    phStbDbg_KeyTreeLockExit();
    return err;
}

/******************************************************************************/
tmErrorCode_t tmDbg_KeyNext(
    char *keyStorage,
    char *valueStorage)
{
    const phStbDbg_TreeNode_t *node;
    tmErrorCode_t err;

    phStbDbg_KeyTreeLockEnter();

    /* Find start node. Start of iteration? */
    if (keyStorage[0] == 0)
    {
        /* Yes. */
        node = gKeyTreeRoot;
        err = TM_OK;
    }
    else
    {
        err = phStbDbg_TreeGetNode(
            gKeyTreeRoot,
            keyStorage,
            &node);
    }

    if (err == TM_OK)
    {
        /* Find next node: */
        node = phStbDbg_TreeNextNode(
            node,
            phStbDbg_TreeOrder_LeafOnly);
        if (node != NULL)
        {
            (void)phStbDbg_TreeGetNodePath(node, keyStorage);
            strcpy(valueStorage, node->value);
        }
        else
        {
            err = PH_STBDBG_TREE_ERR_FINISHED;
            TRACE("phStbDbg_KeyNext => NULL\n");
        }
    }

    phStbDbg_KeyTreeLockExit();
    return err;
}

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