#include "constant_pool_gen.h"
#include "assert.h"
#include "dbug.h"
#include "memory.h"
#include "print_enum.h"
#include "queue.h"
#include "symbol_table.h"
#include "traverse.h"
#include "tree_basic.h"

typedef struct cp_link q_item;

struct INFO
{
    struct queue* constant_queue;
};

static info* info_init(void)
{
    info* result;
    DBUG_ENTER("info_init");

    result = (info*)MEMmalloc(sizeof(struct INFO));

    result->constant_queue = q_init();
    DBUG_RETURN(result);
}

static void info_free(info* arg_info) { MEMfree(arg_info); }

int find_constant(struct queue* q, type_t ty, union cp_val value)
{
    if (!q->head) return -1;
    q_item* node = q->head;
    int            idx  = 0;

    if (ty == TY_int)
    {
        do
        {
            if (node->type == TY_int && node->ival == value.ival) return idx;
        } while (idx++, (node = node->next));
    }
    else if (ty == TY_float)
    {
        do
        {
            if (node->type == TY_float && node->fval == value.fval) return idx;
        } while (idx++, (node = node->next));
    }

    return -1;
}

node* CPGint(node* arg_node, info* arg_info)
{
    int v   = INT_VALUE(arg_node);

    /* These special-value constants have instructions of their own */
    if (v == 0 || v == -1 || v == 1) return arg_node;

    int idx = find_constant(arg_info->constant_queue, TY_int, (union cp_val){ .ival = v });

    if (idx >= 0)
    {
        INT_CPINDEX(arg_node) = idx;
    }
    else
    {
        q_nq(arg_info->constant_queue, TY_int, (union cp_val){ .ival = v });
        INT_CPINDEX(arg_node) = arg_info->constant_queue->length - 1;
    }

    return arg_node;
}

node* CPGfloat(node* arg_node, info* arg_info)
{
    float          v = FLOAT_VALUE(arg_node);

    /* These special-value constants have instructions of their own */
    if (v == 0 || v == 1) return arg_node;

    int idx = find_constant(arg_info->constant_queue, TY_float, (union cp_val){ .fval = v });

    if (idx >= 0)
    {
        FLOAT_CPINDEX(arg_node) = idx;
    }
    else
    {
        q_nq(arg_info->constant_queue, TY_float, (union cp_val){ .fval = v });
        FLOAT_CPINDEX(arg_node) = arg_info->constant_queue->length - 1;
    }

    return arg_node;
}

void print_constant_queue(struct queue* q)
{
    q_item* n = q->head;
    if (!q->head) return;

    do
    {
        if (n->type == TY_int)
        {
            printf("Constant of type int:   %d\n", n->ival);
        }
        else
        {
            printf("Constant of type float: %f\n", n->fval);
        }
    } while ((n = n->next));
}

node* CPGdoGenerate(node* syntaxtree)
{
    DBUG_ENTER("CPGdoGenerate");
    info* arg_info = info_init();

    TRAVpush(TR_cpg);
    syntaxtree = TRAVdo(syntaxtree, arg_info);
    TRAVpop();

    print_constant_queue(arg_info->constant_queue);
    PROGRAM_CONSTANTPOOL(syntaxtree) = arg_info->constant_queue;

    info_free(arg_info);

    DBUG_RETURN(syntaxtree);
}