/** @file
  This file is cloned from DMTF libredfish library tag v1.0.0 and maintained
  by EDKII.

//----------------------------------------------------------------------------
// Copyright Notice:
// Copyright 2017 Distributed Management Task Force, Inc. All rights reserved.
// License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libredfish/LICENSE.md
//----------------------------------------------------------------------------

  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
  (C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <redpath.h>

static char* getVersion(const char* path, char** end);
static void parseNode(const char* path, redPathNode* node, redPathNode** end);

static char* getStringTill(const char* string, const char* terminator, char** retEnd);

redPathNode* parseRedPath(const char* path)
{
    redPathNode* node;
    redPathNode* endNode;
    char* curPath;
    char* end;

    if(!path || strlen(path) == 0)
    {
        return NULL;
    }

    node = (redPathNode*)calloc(1, sizeof(redPathNode));
    if(!node)
    {
        return NULL;
    }
    if(path[0] == '/')
    {
        node->isRoot = true;
        if(path[1] == 'v')
        {
            node->version = getVersion(path+1, &curPath);
            if(curPath == NULL)
            {
                return node;
            }
            if(curPath[0] == '/')
            {
                curPath++;
            }
            node->next = parseRedPath(curPath);
        }
        else
        {
           node->next = parseRedPath(path+1);
        }
        return node;
    }
    node->isRoot = false;
    curPath = getStringTill(path, "/", &end);
    endNode = node;
    parseNode(curPath, node, &endNode);
    free(curPath);
    if(end != NULL)
    {
        endNode->next = parseRedPath(end+1);
    }
    return node;
}

void cleanupRedPath(redPathNode* node)
{
    if(!node)
    {
        return;
    }
    cleanupRedPath(node->next);
    node->next = NULL;
    if(node->version)
    {
        free(node->version);
    }
    if(node->nodeName)
    {
        free(node->nodeName);
    }
    if(node->op)
    {
        free(node->op);
    }
    if(node->propName)
    {
        free(node->propName);
    }
    if(node->value)
    {
        free(node->value);
    }
    free(node);
}

static char* getVersion(const char* path, char** end)
{
    return getStringTill(path, "/", end);
}

static void parseNode(const char* path, redPathNode* node, redPathNode** end)
{
    char* indexStart;
    char* index;
    char* indexEnd;
    char* nodeName = getStringTill(path, "[", &indexStart);
    size_t tmpIndex;
    char* opChars;

    node->nodeName = nodeName;
    if(indexStart == NULL)
    {
        *end = node;
        return;
    }
    node->next = (redPathNode*)calloc(1, sizeof(redPathNode));
    if(!node->next)
    {
        return;
    }
    //Skip past [
    indexStart++;
    *end = node->next;
    index = getStringTill(indexStart, "]", NULL);
    tmpIndex = (size_t)strtoull(index, &indexEnd, 0);
    if(indexEnd != index)
    {
        free(index);
        node->next->index = tmpIndex;
        node->next->isIndex = true;
        return;
    }
    opChars = strpbrk(index, "<>=~");
    if(opChars == NULL)
    {
        //TODO handle last() and position()
        node->next->op = strdup("exists");
        node->next->propName = index;
        return;
    }
    node->next->propName = (char*)malloc((opChars - index)+1);
    memcpy(node->next->propName, index, (opChars - index));
    node->next->propName[(opChars - index)] = 0;

    tmpIndex = 1;
    while(1)
    {
        if(opChars[tmpIndex] == '=' || opChars[tmpIndex] == '<' || opChars[tmpIndex] == '>' || opChars[tmpIndex] == '~')
        {
            tmpIndex++;
            continue;
        }
        break;
    }

    node->next->op = (char*)malloc(tmpIndex+1);
    memcpy(node->next->op, opChars, tmpIndex);
    node->next->op[tmpIndex] = 0;

    node->next->value = strdup(opChars+tmpIndex);
    free(index);
}

static char* getStringTill(const char* string, const char* terminator, char** retEnd)
{
    char* ret;
    char* end;
    end = strstr((char*)string, terminator);
    if(retEnd)
    {
        *retEnd = end;
    }
    if(end == NULL)
    {
        //No terminator
        return strdup(string);
    }
    ret = (char*)malloc((end-string)+1);
    memcpy(ret, string, (end-string));
    ret[(end-string)] = 0;
    return ret;
}
