/****************************************************/
/* File: util.c                                     */
/* Utility function implementation                  */
/* for the TINY compiler                            */
/* Compiler Construction: Principles and Practice   */
/* Kenneth C. Louden                                */
/****************************************************/

#include "util.h"
#include "globals.h"

/* Procedure printToken prints a token 
 * and its lexeme to the listing file
 */
void printToken(TokenType token, const char *tokenString) {
    switch (token) {
        case IF:
        case ELSE:
        case WHILE:
        case RETURN:
        case INT:
        case VOID:
            fprintf(listing,
                    "reserved word: %s\n", tokenString);
            break;
        case ASSIGN:
            fprintf(listing, "=\n");
            break;
        case EQ:
            fprintf(listing, "==\n");
            break;
        case NE:
            fprintf(listing, "!=\n");
            break;
        case LT:
            fprintf(listing, "<\n");
            break;
        case LE:
            fprintf(listing, "<=\n");
            break;
        case GT:
            fprintf(listing, ">\n");
            break;
        case GE:
            fprintf(listing, ">=\n");
            break;
        case PLUS:
            fprintf(listing, "+\n");
            break;
        case MINUS:
            fprintf(listing, "-\n");
            break;
        case TIMES:
            fprintf(listing, "*\n");
            break;
        case OVER:
            fprintf(listing, "/\n");
            break;
        case LPAREN:
            fprintf(listing, "(\n");
            break;
        case RPAREN:
            fprintf(listing, ")\n");
            break;
        case LBRACE:
            fprintf(listing, "[\n");
            break;
        case RBRACE:
            fprintf(listing, "]\n");
            break;
        case LCURLY:
            fprintf(listing, "{\n");
            break;
        case RCURLY:
            fprintf(listing, "}\n");
            break;
        case SEMI:
            fprintf(listing, ";\n");
            break;
        case COMMA:
            fprintf(listing, ",\n");
            break;
        case ENDFILE:
            fprintf(listing, "EOF\n");
            break;
        case NUM:
            fprintf(listing,
                    "NUM, val= %s\n", tokenString);
            break;
        case ID:
            fprintf(listing,
                    "ID, name= %s\n", tokenString);
            break;
        case ERROR:
            fprintf(listing,
                    "ERROR: %s\n", tokenString);
            break;
        default: /* should never happen */
            fprintf(listing, "Unknown token: %d\n", token);
    }
}

/* Function newStmtNode creates a new statement
 * node for syntax tree construction
 */
TreeNode *newStmtNode(StmtKind kind) {
    TreeNode *t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL)
        fprintf(listing, "Out of memory error at line %d\n", lineno);
    else {
        for (i = 0; i < MAXCHILDREN; i++) t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = StmtK;
        t->kind.stmt = kind;
        t->lineno = lineno;
    }
    return t;
}

/* Function newExpNode creates a new expression 
 * node for syntax tree construction
 */
TreeNode *newExpNode(ExpKind kind) {
    TreeNode *t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL)
        fprintf(listing, "Out of memory error at line %d\n", lineno);
    else {
        for (i = 0; i < MAXCHILDREN; i++) t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = ExpK;
        t->kind.exp = kind;
        t->lineno = lineno;
        t->type = Void;
    }
    return t;
}

TreeNode *newDeclNode(DeclKind kind) {
    TreeNode *t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL)
        fprintf(listing, "Out of memory error at line %d\n", lineno);
    else {
        for (i = 0; i < MAXCHILDREN; i++) t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = DeclK;
        t->kind.decl = kind;
        t->lineno = lineno;
    }
    return t;
}

TreeNode *newTypeNode(TypeKind kind) {
    TreeNode *t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL)
        fprintf(listing, "Out of memory error at line %d\n", lineno);
    else {
        for (i = 0; i < MAXCHILDREN; i++) t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = TypeK;
        t->kind.type = kind;
        t->lineno = lineno;
    }
    return t;
}

/* Function copyString allocates and makes a new
 * copy of an existing string
 */
char *copyString(char *s) {
    int n;
    char *t;
    if (s == NULL) return NULL;
    n = strlen(s) + 1;
    t = malloc(n);
    if (t == NULL)
        fprintf(listing, "Out of memory error at line %d\n", lineno);
    else
        strcpy(t, s);
    return t;
}

/* Variable indentno is used by printTree to
 * store current number of spaces to indent
 */
static int indentno = 0;

/* macros to increase/decrease indentation */
#define INDENT indentno += 2
#define UNINDENT indentno -= 2

/* printSpaces indents by printing spaces */
static void printSpaces(void) {
    int i;
    for (i = 0; i < indentno; i++)
        fprintf(listing, " ");
}

void printType(TreeNode *tree) {
    switch (tree->type) {
        case Void:
            fprintf(listing, "void");
            break;
        case Integer:
            fprintf(listing, "int");
            break;
        case IntegerArray:
            fprintf(listing, "int[]");
            break;
        default:
            return;
    }
}

/* procedure printTree prints a syntax tree to the 
 * listing file using indentation to indicate subtrees
 */
void printTree(TreeNode *tree) {
    int i;
    INDENT;
    while (tree != NULL) {
        printSpaces();
        if (tree->nodekind == StmtK) {
            switch (tree->kind.stmt) {
                case CompK:
                    fprintf(listing, "Compound Statement:\n");
                    break;
                case IfK:
                    fprintf(listing, "%s:\n", 
                        (tree->child[2] != NULL) ? "If-Else Statement" : "If Statement");
                    break;
                case IterK:
                    fprintf(listing, "While Statement:\n");
                    break;
                case ReturnK:
                    fprintf(listing, "Return Statement:\n");
                    break;
                default:
                    fprintf(listing, "Unknown StmtNode kind\n");
                    break;
            }
        } else if (tree->nodekind == ExpK) {
            switch (tree->kind.exp) {
                case AssignK:
                    fprintf(listing, "Assign:\n");
                    break;
                case OpK:
                    fprintf(listing, "Op: ");
                    printToken(tree->attr.op, "\0");
                    break;
                case ConstK:
                    fprintf(listing, "Const: %d\n", tree->attr.val);
                    break;
                case IdK:
                    fprintf(listing, "Variable: name = %s\n", tree->attr.name);
                    break;
                case ArrIdK:
                    fprintf(listing, "Variable: name = %s\n", tree->attr.name);
                    break;
                case CallK:
                    fprintf(listing, "Call: function name = %s\n", tree->attr.name);
                    break;
                default:
                    fprintf(listing, "Unknown ExpNode kind\n");
                    break;
            }
        } else if (tree->nodekind == DeclK) {
            switch (tree->kind.decl) {
                case FuncK:
                    fprintf(listing, "Function Declaration: name = %s, return type = ", tree->attr.name);
                    printType(tree);
                    fprintf(listing, "\n");
                    break;
                case VarK:
                    fprintf(listing, "Variable Declaration: name = %s, type = ", tree->attr.name);
                    printType(tree);
                    fprintf(listing, "\n");
                    break;
                case ArrVarK:
                    fprintf(listing, "Variable Declaration: name = %s, type = ", tree->attr.name);
                    printType(tree);
                    fprintf(listing, "\n");
                    break;
                case NonArrParamK:
                    if (tree->type == Void)
                        fprintf(listing, "Void Parameter\n");
                    else {
                        fprintf(listing, "Parameter: name = %s, type = ", tree->attr.name);
                        printType(tree);
                        fprintf(listing, "\n");
                    }
                    break;
                case ArrParamK:
                    fprintf(listing, "Parameter: name = %s, type = ", tree->attr.name);
                    printType(tree);
                    fprintf(listing, "\n");
                    break;
                default:
                    fprintf(listing, "Unknown DeclNode kind\n");
                    break;
            }
        } else if (tree->nodekind == TypeK) {

        } else
            fprintf(listing, "Unknown node kind\n");
        for (i = 0; i < MAXCHILDREN; i++)
            printTree(tree->child[i]);
        tree = tree->sibling;
    }
    UNINDENT;
}
