#include "assert.h"
#include "bool_cast_rewrite.h"
#include "dbug.h"
#include "memory.h"
#include "free.h"
#include "symbol_table.h"
#include "traverse.h"
#include "tree_basic.h"
#include "ctinfo.h"
#include "mymacros.h"


type_t deduce_expr_type(node* expr)
{
    type_t expr_type;
    switch (NODE_TYPE(expr))
    {
        case N_var:
            expr_type = VAR_STLINK(expr)->type;
            break;
        case N_call:
            expr_type = CALL_STLINK(expr)->type;
            break;
        case N_cast:
            expr_type = CAST_TYPE(expr);
            break;
        case N_binop:
            expr_type = BINOP_TYPE(expr);
            break;
        case N_monop:
            expr_type = MONOP_TYPE(expr);
            break;
        case N_int:
            expr_type = TY_int;
            break;
        case N_bool:
            expr_type = TY_bool;
            break;
        case N_float:
            expr_type = TY_float;
            break;
        case N_condexpr:
            expr_type = CONDEXPR_TYPE(expr);
            break;
        default:
            CTIerrorLine(NODE_LINE(expr), "Non-expression-expression?");
            expr_type = TY_unknown;
            break;
    }
    return expr_type;
}

node* BCRcast(node* arg_node, info* arg_info)
{
    DBUG_ENTER("BCRcast");
    node*  expr      = CAST_EXPR(arg_node);
    type_t expr_type = deduce_expr_type(CAST_EXPR(arg_node));
    type_t cast_type = CAST_TYPE(arg_node);

    if ((cast_type != TY_bool && expr_type != TY_bool) || (cast_type == expr_type))
    {
        /* This isn't a bool->T or T->bool cast. */
        TO(CAST_EXPR);
        DBUG_RETURN(arg_node);
    }

    node* result_expr = NULL;

    node *one, *zero;
    if (expr_type == TY_bool)
    {
        // bool->T
        if (cast_type == TY_int)
        {
            one  = TBmakeInt(1);
            zero = TBmakeInt(0);
        }
        else // TY_float
        {
            one         = TBmakeFloat(1);
            zero        = TBmakeFloat(0);
        }
        result_expr = TBmakeCondexpr(TRAVopt(expr, arg_info), one, zero);
        CONDEXPR_TYPE(result_expr) = cast_type;
    }
    else
    {
        // T->bool
        if (expr_type == TY_int)
            zero = TBmakeInt(0);
        else
            zero = TBmakeFloat(0);
        result_expr = TBmakeBinop(BO_ne, TRAVopt(expr, arg_info), zero);
        BINOP_TYPE(result_expr) = TY_bool;
        BINOP_EXPRTYPE(result_expr) = expr_type;
    }

    CAST_EXPR(arg_node) = NULL;
    FREEdoFreeTree(arg_node);

    DBUG_RETURN(result_expr);
}

node* BCRdoRewrite(node* syntaxtree)
{
    DBUG_ENTER("BCRdoRewrite");

    TRAVpush(TR_bcr);
    syntaxtree = TRAVdo(syntaxtree, NULL);
    TRAVpop();

    DBUG_RETURN(syntaxtree);
}