#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
#include "yarp/node.h"

// Clear the node but preserves the location.
void yp_node_clear(yp_node_t *node) {
    yp_location_t location = node->location;
    memset(node, 0, sizeof(yp_node_t));
    node->location = location;
}

static void
yp_node_memsize_node(yp_node_t *node, yp_memsize_t *memsize);

// Calculate the size of the node list in bytes.
static size_t
yp_node_list_memsize(yp_node_list_t *node_list, yp_memsize_t *memsize) {
    size_t size = sizeof(yp_node_list_t) + (node_list->capacity * sizeof(yp_node_t *));
    for (size_t index = 0; index < node_list->size; index++) {
        yp_node_memsize_node(node_list->nodes[index], memsize);
    }
    return size;
}

// Append a new node onto the end of the node list.
void
yp_node_list_append(yp_node_list_t *list, yp_node_t *node) {
    if (list->size == list->capacity) {
        list->capacity = list->capacity == 0 ? 4 : list->capacity * 2;
        list->nodes = (yp_node_t **) realloc(list->nodes, sizeof(yp_node_t *) * list->capacity);
    }
    list->nodes[list->size++] = node;
}

YP_EXPORTED_FUNCTION void
yp_node_destroy(yp_parser_t *parser, yp_node_t *node);

// Deallocate the inner memory of a list of nodes. The parser argument is not
// used, but is here for the future possibility of pre-allocating memory pools.
static void
yp_node_list_free(yp_parser_t *parser, yp_node_list_t *list) {
    if (list->capacity > 0) {
        for (size_t index = 0; index < list->size; index++) {
            yp_node_destroy(parser, list->nodes[index]);
        }
        free(list->nodes);
    }
}

// Deallocate the space for a yp_node_t. Similarly to yp_node_alloc, we're not
// using the parser argument, but it's there to allow for the future possibility
// of pre-allocating larger memory pools.
YP_EXPORTED_FUNCTION void
yp_node_destroy(yp_parser_t *parser, yp_node_t *node) {
    switch (YP_NODE_TYPE(node)) {
        <%- nodes.each do |node| -%>
#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
        case <%= node.type %>: {
            <%- if node.fields.any? { |field| ![YARP::LocationField, YARP::OptionalLocationField, YARP::UInt32Field, YARP::FlagsField, YARP::ConstantField, YARP::OptionalConstantField].include?(field.class) } -%>
            yp_<%= node.human %>_t *cast = (yp_<%= node.human %>_t *) node;
            <%- end -%>
            <%- node.fields.each do |field| -%>
            <%- case field -%>
            <%- when YARP::LocationField, YARP::OptionalLocationField, YARP::UInt32Field, YARP::FlagsField, YARP::ConstantField, YARP::OptionalConstantField -%>
            <%- when YARP::NodeField -%>
            yp_node_destroy(parser, (yp_node_t *)cast-><%= field.name %>);
            <%- when YARP::OptionalNodeField -%>
            if (cast-><%= field.name %> != NULL) {
                yp_node_destroy(parser, (yp_node_t *)cast-><%= field.name %>);
            }
            <%- when YARP::StringField -%>
            yp_string_free(&cast-><%= field.name %>);
            <%- when YARP::NodeListField -%>
            yp_node_list_free(parser, &cast-><%= field.name %>);
            <%- when YARP::ConstantListField -%>
            yp_constant_id_list_free(&cast-><%= field.name %>);
            <%- else -%>
            <%- raise -%>
            <%- end -%>
            <%- end -%>
            break;
        }
        <%- end -%>
#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
        default:
            assert(false && "unreachable");
            break;
    }
    free(node);
}

static void
yp_node_memsize_node(yp_node_t *node, yp_memsize_t *memsize) {
    memsize->node_count++;

    switch (YP_NODE_TYPE(node)) {
        // We do not calculate memsize of a ScopeNode
        // as it should never be generated
        case YP_SCOPE_NODE:
            return;
        <%- nodes.each do |node| -%>
#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
        case <%= node.type %>: {
            yp_<%= node.human %>_t *cast = (yp_<%= node.human %>_t *) node;
            memsize->memsize += sizeof(*cast);
            <%- node.fields.each do |field| -%>
            <%- case field -%>
            <%- when YARP::ConstantField, YARP::OptionalConstantField, YARP::UInt32Field, YARP::FlagsField, YARP::LocationField, YARP::OptionalLocationField -%>
            <%- when YARP::NodeField -%>
            yp_node_memsize_node((yp_node_t *)cast-><%= field.name %>, memsize);
            <%- when YARP::OptionalNodeField -%>
            if (cast-><%= field.name %> != NULL) {
                yp_node_memsize_node((yp_node_t *)cast-><%= field.name %>, memsize);
            }
            <%- when YARP::StringField -%>
            memsize->memsize += yp_string_memsize(&cast-><%= field.name %>);
            <%- when YARP::NodeListField -%>
            yp_node_list_memsize(&cast-><%= field.name %>, memsize);
            <%- when YARP::ConstantListField -%>
            memsize->memsize += yp_constant_id_list_memsize(&cast-><%= field.name %>);
            <%- else -%>
            <%- raise -%>
            <%- end -%>
            <%- end -%>
            break;
        }
        <%- end -%>
#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
    }
}

// Calculates the memory footprint of a given node.
YP_EXPORTED_FUNCTION void
yp_node_memsize(yp_node_t *node, yp_memsize_t *memsize) {
    *memsize = (yp_memsize_t) { .memsize = 0, .node_count = 0 };
    yp_node_memsize_node(node, memsize);
}

// Returns a string representation of the given node type.
YP_EXPORTED_FUNCTION const char *
yp_node_type_to_str(yp_node_type_t node_type)
{
    switch (node_type) {
<%- nodes.each do |node| -%>
        case <%= node.type %>:
            return "<%= node.type %>";
<%- end -%>
    }
    return "";
}
