/***********************************************************************
 * $Id$
 * Copyright 2009 Aplix Corporation. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Node-specific functions
 ***********************************************************************/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "comment.h"
#include "lex.h"
#include "misc.h"
#include "node.h"
#include "process.h"

struct node *
newelement(const char *name)
{
    struct element *element = memalloc(sizeof(struct element));
    element->n.type = NODE_ELEMENT;
    element->name = name;
    return &element->n;
}

struct node *
newattr(const char *name, const char *val)
{
    struct attr *attr = memalloc(sizeof(struct attr));
    attr->n.type = NODE_ATTR;
    attr->name = name;
    attr->value = val;
    return &attr->n;
}

struct node *
newattrlist(void)
{
    struct attrlist *attrlist = memalloc(sizeof(struct attrlist));
    attrlist->n.type = NODE_ATTRLIST;
    return &attrlist->n;
}

/***********************************************************************
 * addnode : add node as child of another node
 *
 * Enter:   parent node
 *          child node
 *
 * The children list is constructed backwards. This is fixed later with
 * a call to reversechildren.
 *
 * If child is an attrlist, its children are added to parent and the
 * attrlist is freed.
 */
void
addnode(struct node *parent, struct node *child)
{
    if (!child)
        return;
    if (child->type == NODE_ATTRLIST) {
        /* Add the attrs in the attrlist to parent. */
        struct node *child2;
        reversechildren(child);
        child2 = child->children;
        memfree(child);
        while (child2) {
            struct node *next = child2->next;
            addnode(parent, child2);
            child2 = next;
        }
    } else {
        child->next = parent->children;
        parent->children = child;
        child->parent = parent;
    }
}

/***********************************************************************
 * reversechildren : recursively reverse child lists
 *
 * Also sets parent field on each node.
 */
void
reversechildren(struct node *node)
{
    struct node *newlist = 0;
    struct node *child = node->children;
    while (child) {
        struct node *next = child->next;
        child->parent = node;
        child->next = newlist;
        newlist = child;
        reversechildren(child);
        child = next;
    }
    node->children = newlist;
}

/***********************************************************************
 * nodeisempty : test if node is empty (has no children)
 */
int
nodeisempty(struct node *node)
{
    return !node->children;
}

/***********************************************************************
 * nodewalk : single step of depth last traversal of node tree
 *
 * Return:  next node in walk, 0 if finished
 */
struct node *
nodewalk(struct node *node)
{
    if (node->children)
        return node->children;
    if (node->next)
        return node->next;
    do {
        node = node->parent;
        if (!node)
            return 0;
    } while (!node->next);
    return node->next;
}

/***********************************************************************
 * findchildelement : find child element of a particular name
 *
 * Enter:   node = element
 *          name = name to find
 *
 * Return:  0 else child element of that name
 */
static struct node *
findchildelement(struct node *node, const char *name)
{
    node = node->children;
    while (node) {
        if (node->type == NODE_ELEMENT) {
            struct element *element = (void *)node;
            if (!strcmp(element->name, name))
                break;
        }
        node = node->next;
    }
    return node;
}

/***********************************************************************
 * getattr : get value of attribute
 *
 * Enter:   node = element to find attribute in
 *          name = name of attribute
 *
 * Return:  0 if not found, else 0-terminated string value
 */
const char *
getattr(struct node *node, const char *name)
{
    node = node->children;
    while (node) {
        if (node->type == NODE_ATTR) {
            struct attr *attr = (void *)node;
            if (!strcmp(attr->name, name))
                return attr->value;
        }
        node = node->next;
    }
    return 0;
}

/***********************************************************************
 * findchildelementwithnameattr : find child element with a name attribute
 *                                of a particular value
 *
 * Enter:   node = element
 *          name = name to find
 *
 * Return:  0 else child element with name attr of that value
 */
static struct node *
findchildelementwithnameattr(struct node *node, const char *name)
{
    node = node->children;
    while (node) {
        if (node->type == NODE_ELEMENT) {
            const char *s = getattr(node, "name");
            if (s && !strcmp(s, name))
                break;
        }
        node = node->next;
    }
    return node;
}

/***********************************************************************
 * findreturntype : find Type parse node for return type
 *
 * Enter:   node = Operation element
 *
 * Return:  0 if not found, else Type parse node for return type
 */
struct node *
findreturntype(struct node *node)
{
    return findchildelement(node, "Type");
}

/***********************************************************************
 * findparamidentifier : find identifier parse node for parameter
 *
 * Enter:   node = Operation element
 *          name = parameter name to find
 *
 * Return:  0 if not found, else node struct for parameter identifier
 */
struct node *
findparamidentifier(struct node *node, const char *name)
{
    node = findchildelement(node, "ArgumentList");
    if (node)
        node = findchildelementwithnameattr(node, name);
    return node;
}

/***********************************************************************
 * findthrowidentifier : find identifier parse node for exception name
 *
 * Enter:   node = Operation element
 *          name = exception name to find
 *
 * Return:  0 if not found, else node for Name element, child of Raises
 *              or SetRaises
 */
struct node *
findthrowidentifier(struct node *node, const char *name)
{
    struct node *node2 = findchildelement(node, "Raises");
    if (node2)
        node2 = findchildelementwithnameattr(node2, name);
    if (!node2) {
        node2 = findchildelement(node, "SetRaises");
        if (node2)
            node2 = findchildelementwithnameattr(node2, name);
    }
    return node2;
}

/***********************************************************************
 * outputid : output the id of a node
 */
static void
outputid(struct node *node)
{
    if (node->parent)
        outputid(node->parent);
    if (node->id) {
        fputs("::", stdout);
        printtext(node->id, strlen(node->id), 1);
    }
}

/***********************************************************************
 * outputnode : output node and its children
 *
 * Enter:   node = node to output, assumed to be an element
 *          indent
 */
void
outputnode(struct node *node, unsigned int indent)
{
    struct element *element = (void *)node;
    struct node *child;
    int empty = 1;
    printf("%*s<%s", indent, "", element->name);
    child = element->n.children;
    while (child) {
        switch(child->type) {
        case NODE_ELEMENT:
            empty = 0;
            break;
        case NODE_ATTR:
            {
                struct attr *attr = (void *)child;
                printf(" %s=\"", attr->name);
                printtext(attr->value, strlen(attr->value), 1);
                printf("\"");
            }
            break;
        }
        child = child->next;
    }
    if (node->id) {
        printf(" id=\"");
        outputid(node);
        printf("\"");
    }
    if (!empty || node->comments || node->wsstart) {
        printf(">\n");
        if (node->wsstart) {
            printf("%*s  <webidl>", indent, "");
            outputwidl(node);
            printf("</webidl>\n");
        }
        outputdescriptive(node, indent + 2);
        child = element->n.children;
        while (child) {
            switch(child->type) {
            case NODE_ELEMENT:
                outputnode(child, indent + 2);
                break;
            }
            child = child->next;
        }
        printf("%*s</%s>\n", indent, "", element->name);
    } else
        printf("/>\n");
}