/**
 * Dump/print a slang_operation tree
 */


#include "main/imports.h"
#include "slang_compile.h"
#include "slang_print.h"


static void
spaces(int n)
{
   while (n--)
      printf(" ");
}


static void
print_type(const slang_fully_specified_type *t)
{
   switch (t->qualifier) {
   case SLANG_QUAL_NONE:
      /*printf("");*/
      break;
   case SLANG_QUAL_CONST:
      printf("const ");
      break;
   case SLANG_QUAL_ATTRIBUTE:
      printf("attrib ");
      break;
   case SLANG_QUAL_VARYING:
      printf("varying ");
      break;
   case SLANG_QUAL_UNIFORM:
      printf("uniform ");
      break;
   case SLANG_QUAL_OUT:
      printf("output ");
      break;
   case SLANG_QUAL_INOUT:
      printf("inout ");
      break;
   case SLANG_QUAL_FIXEDOUTPUT:
      printf("fixedoutput");
      break;
   case SLANG_QUAL_FIXEDINPUT:
      printf("fixedinput");
      break;
   default:
      printf("unknown qualifer!");
   }

   switch (t->specifier.type) {
   case SLANG_SPEC_VOID:
      printf("void");
      break;
   case SLANG_SPEC_BOOL:
      printf("bool");
      break;
   case SLANG_SPEC_BVEC2:
      printf("bvec2");
      break;
   case SLANG_SPEC_BVEC3:
      printf("bvec3");
      break;
   case SLANG_SPEC_BVEC4:
      printf("bvec4");
      break;
   case SLANG_SPEC_INT:
      printf("int");
      break;
   case SLANG_SPEC_IVEC2:
      printf("ivec2");
      break;
   case SLANG_SPEC_IVEC3:
      printf("ivec3");
      break;
   case SLANG_SPEC_IVEC4:
      printf("ivec4");
      break;
   case SLANG_SPEC_FLOAT:
      printf("float");
      break;
   case SLANG_SPEC_VEC2:
      printf("vec2");
      break;
   case SLANG_SPEC_VEC3:
      printf("vec3");
      break;
   case SLANG_SPEC_VEC4:
      printf("vec4");
      break;
   case SLANG_SPEC_MAT2:
      printf("mat2");
      break;
   case SLANG_SPEC_MAT3:
      printf("mat3");
      break;
   case SLANG_SPEC_MAT4:
      printf("mat4");
      break;
   case SLANG_SPEC_MAT23:
      printf("mat2x3");
      break;
   case SLANG_SPEC_MAT32:
      printf("mat3x2");
      break;
   case SLANG_SPEC_MAT24:
      printf("mat2x4");
      break;
   case SLANG_SPEC_MAT42:
      printf("mat4x2");
      break;
   case SLANG_SPEC_MAT34:
      printf("mat3x4");
      break;
   case SLANG_SPEC_MAT43:
      printf("mat4x3");
      break;
   case SLANG_SPEC_SAMPLER_1D:
      printf("sampler1D");
      break;
   case SLANG_SPEC_SAMPLER_2D:
      printf("sampler2D");
      break;
   case SLANG_SPEC_SAMPLER_3D:
      printf("sampler3D");
      break;
   case SLANG_SPEC_SAMPLER_CUBE:
      printf("samplerCube");
      break;
   case SLANG_SPEC_SAMPLER_1D_SHADOW:
      printf("sampler1DShadow");
      break;
   case SLANG_SPEC_SAMPLER_2D_SHADOW:
      printf("sampler2DShadow");
      break;
   case SLANG_SPEC_STRUCT:
      printf("struct");
      break;
   case SLANG_SPEC_ARRAY:
      printf("array");
      break;
   default:
      printf("unknown type");
   }
   /*printf("\n");*/
}


static void
print_variable(const slang_variable *v, int indent)
{
   spaces(indent);
   printf("VAR ");
   print_type(&v->type);
   printf(" %s (at %p)", (char *) v->a_name, (void *) v);
   if (v->initializer) {
      printf(" :=\n");
      slang_print_tree(v->initializer, indent + 3);
   }
   else {
      printf(";\n");
   }
}


static void
print_binary(const slang_operation *op, const char *oper, int indent)
{
   assert(op->num_children == 2);
#if 0
   printf("binary at %p locals=%p outer=%p\n",
          (void *) op,
          (void *) op->locals,
          (void *) op->locals->outer_scope);
#endif
   slang_print_tree(&op->children[0], indent + 3);
   spaces(indent);
   printf("%s at %p locals=%p outer=%p\n",
          oper, (void *) op, (void *) op->locals,
          (void *) op->locals->outer_scope);
   slang_print_tree(&op->children[1], indent + 3);
}


static void
print_generic2(const slang_operation *op, const char *oper,
               const char *s, int indent)
{
   GLuint i;
   if (oper) {
      spaces(indent);
      printf("%s %s at %p locals=%p outer=%p\n",
             oper, s, (void *) op, (void *) op->locals, 
             (void *) op->locals->outer_scope);
   }
   for (i = 0; i < op->num_children; i++) {
      spaces(indent);
      printf("//child %u of %u:\n", i, op->num_children);
      slang_print_tree(&op->children[i], indent);
   }
}

static void
print_generic(const slang_operation *op, const char *oper, int indent)
{
   print_generic2(op, oper, "", indent);
}


static const slang_variable_scope *
find_scope(const slang_variable_scope *s, slang_atom name)
{
   GLuint i;
   for (i = 0; i < s->num_variables; i++) {
      if (s->variables[i]->a_name == name)
         return s;
   }
   if (s->outer_scope)
      return find_scope(s->outer_scope, name);
   else
      return NULL;
}

static const slang_variable *
find_var(const slang_variable_scope *s, slang_atom name)
{
   GLuint i;
   for (i = 0; i < s->num_variables; i++) {
      if (s->variables[i]->a_name == name)
         return s->variables[i];
   }
   if (s->outer_scope)
      return find_var(s->outer_scope, name);
   else
      return NULL;
}


void
slang_print_tree(const slang_operation *op, int indent)
{
   GLuint i;

   switch (op->type) {

   case SLANG_OPER_NONE:
      spaces(indent);
      printf("SLANG_OPER_NONE\n");
      break;

   case SLANG_OPER_BLOCK_NO_NEW_SCOPE:
      spaces(indent);
      printf("{ locals=%p  outer=%p\n", (void*)op->locals, (void*)op->locals->outer_scope);
      print_generic(op, NULL, indent+3);
      spaces(indent);
      printf("}\n");
      break;

   case SLANG_OPER_BLOCK_NEW_SCOPE:
   case SLANG_OPER_NON_INLINED_CALL:
      spaces(indent);
      printf("{{ // new scope  locals=%p outer=%p: ",
             (void *) op->locals,
             (void *) op->locals->outer_scope);
      for (i = 0; i < op->locals->num_variables; i++) {
         printf("%s ", (char *) op->locals->variables[i]->a_name);
      }
      printf("\n");
      print_generic(op, NULL, indent+3);
      spaces(indent);
      printf("}}\n");
      break;

   case SLANG_OPER_VARIABLE_DECL:
      assert(op->num_children == 0 || op->num_children == 1);
      {
         slang_variable *v;
         v = _slang_variable_locate(op->locals, op->a_id, GL_TRUE);
         if (v) {
            const slang_variable_scope *scope;
            spaces(indent);
            printf("DECL (locals=%p outer=%p) ", (void*)op->locals, (void*) op->locals->outer_scope);
            print_type(&v->type);
            printf(" %s (%p)", (char *) op->a_id,
                   (void *) find_var(op->locals, op->a_id));

            scope = find_scope(op->locals, op->a_id);
            printf(" (in scope %p) ", (void *) scope);
            assert(scope);
            if (op->num_children == 1) {
               printf(" :=\n");
               slang_print_tree(&op->children[0], indent + 3);
            }
            else if (v->initializer) {
               printf(" := INITIALIZER\n");
               slang_print_tree(v->initializer, indent + 3);
            }
            else {
               printf(";\n");
            }
            /*
            spaces(indent);
            printf("TYPE: ");
            print_type(&v->type);
            spaces(indent);
            printf("ADDR: %d  size: %d\n", v->address, v->size);
            */
         }
         else {
            spaces(indent);
            printf("DECL %s (anonymous variable!!!!)\n", (char *) op->a_id);
         }
      }
      break;

   case SLANG_OPER_ASM:
      spaces(indent);
      printf("ASM: %s at %p locals=%p outer=%p\n",
             (char *) op->a_id,
             (void *) op,
             (void *) op->locals,
             (void *) op->locals->outer_scope);
      print_generic(op, "ASM", indent+3);
      break;

   case SLANG_OPER_BREAK:
      spaces(indent);
      printf("BREAK\n");
      break;

   case SLANG_OPER_CONTINUE:
      spaces(indent);
      printf("CONTINUE\n");
      break;

   case SLANG_OPER_DISCARD:
      spaces(indent);
      printf("DISCARD\n");
      break;

   case SLANG_OPER_RETURN:
      spaces(indent);
      printf("RETURN\n");
      if (op->num_children > 0)
         slang_print_tree(&op->children[0], indent + 3);
      break;

   case SLANG_OPER_RETURN_INLINED:
      spaces(indent);
      printf("RETURN_INLINED\n");
      if (op->num_children > 0)
         slang_print_tree(&op->children[0], indent + 3);
      break;

   case SLANG_OPER_LABEL:
      spaces(indent);
      printf("LABEL %s\n", (char *) op->a_id);
      break;

   case SLANG_OPER_EXPRESSION:
      spaces(indent);
      printf("EXPR:  locals=%p outer=%p\n",
             (void *) op->locals,
             (void *) op->locals->outer_scope);
      /*print_generic(op, "SLANG_OPER_EXPRESSION", indent);*/
      slang_print_tree(&op->children[0], indent + 3);
      break;

   case SLANG_OPER_IF:
      spaces(indent);
      printf("IF\n");
      slang_print_tree(&op->children[0], indent + 3);
      spaces(indent);
      printf("THEN\n");
      slang_print_tree(&op->children[1], indent + 3);
      spaces(indent);
      printf("ELSE\n");
      slang_print_tree(&op->children[2], indent + 3);
      spaces(indent);
      printf("ENDIF\n");
      break;

   case SLANG_OPER_WHILE:
      assert(op->num_children == 2);
      spaces(indent);
      printf("WHILE LOOP: locals = %p\n", (void *) op->locals);
      indent += 3;
      spaces(indent);
      printf("WHILE cond:\n");
      slang_print_tree(&op->children[0], indent + 3);
      spaces(indent);
      printf("WHILE body:\n");
      slang_print_tree(&op->children[1], indent + 3);
      indent -= 3;
      spaces(indent);
      printf("END WHILE LOOP\n");
      break;

   case SLANG_OPER_DO:
      spaces(indent);
      printf("DO LOOP: locals = %p\n", (void *) op->locals);
      indent += 3;
      spaces(indent);
      printf("DO body:\n");
      slang_print_tree(&op->children[0], indent + 3);
      spaces(indent);
      printf("DO cond:\n");
      slang_print_tree(&op->children[1], indent + 3);
      indent -= 3;
      spaces(indent);
      printf("END DO LOOP\n");
      break;

   case SLANG_OPER_FOR:
      spaces(indent);
      printf("FOR LOOP: locals = %p\n", (void *) op->locals);
      indent += 3;
      spaces(indent);
      printf("FOR init:\n");
      slang_print_tree(&op->children[0], indent + 3);
      spaces(indent);
      printf("FOR condition:\n");
      slang_print_tree(&op->children[1], indent + 3);
      spaces(indent);
      printf("FOR step:\n");
      slang_print_tree(&op->children[2], indent + 3);
      spaces(indent);
      printf("FOR body:\n");
      slang_print_tree(&op->children[3], indent + 3);
      indent -= 3;
      spaces(indent);
      printf("ENDFOR\n");
      /*
      print_generic(op, "FOR", indent + 3);
      */
      break;

   case SLANG_OPER_VOID:
      spaces(indent);
      printf("(oper-void)\n");
      break;

   case SLANG_OPER_LITERAL_BOOL:
      spaces(indent);
      printf("LITERAL (");
      for (i = 0; i < op->literal_size; i++)
         printf("%s ", op->literal[0] ? "TRUE" : "FALSE");
      printf(")\n");

      break;

   case SLANG_OPER_LITERAL_INT:
      spaces(indent);
      printf("LITERAL (");
      for (i = 0; i < op->literal_size; i++)
         printf("%d ", (int) op->literal[i]);
      printf(")\n");
      break;

   case SLANG_OPER_LITERAL_FLOAT:
      spaces(indent);
      printf("LITERAL (");
      for (i = 0; i < op->literal_size; i++)
         printf("%f ", op->literal[i]);
      printf(")\n");
      break;

   case SLANG_OPER_IDENTIFIER:
      {
         const slang_variable_scope *scope;
         spaces(indent);
         if (op->var && op->var->a_name) {
            scope = find_scope(op->locals, op->var->a_name);
            printf("VAR %s  (in scope %p)\n", (char *) op->var->a_name,
                   (void *) scope);
            assert(scope);
         }
         else {
            scope = find_scope(op->locals, op->a_id);
            printf("VAR' %s  (in scope %p) locals=%p outer=%p\n",
                   (char *) op->a_id,
                   (void *) scope,
                   (void *) op->locals,
                   (void *) op->locals->outer_scope);
            /*assert(scope);*/
         }
      }
      break;

   case SLANG_OPER_SEQUENCE:
      print_generic(op, "COMMA-SEQ", indent+3);
      break;

   case SLANG_OPER_ASSIGN:
      spaces(indent);
      printf("ASSIGNMENT  locals=%p outer=%p\n",
             (void *) op->locals,
             (void *) op->locals->outer_scope);
      print_binary(op, ":=", indent);
      break;

   case SLANG_OPER_ADDASSIGN:
      spaces(indent);
      printf("ASSIGN\n");
      print_binary(op, "+=", indent);
      break;

   case SLANG_OPER_SUBASSIGN:
      spaces(indent);
      printf("ASSIGN\n");
      print_binary(op, "-=", indent);
      break;

   case SLANG_OPER_MULASSIGN:
      spaces(indent);
      printf("ASSIGN\n");
      print_binary(op, "*=", indent);
      break;

   case SLANG_OPER_DIVASSIGN:
      spaces(indent);
      printf("ASSIGN\n");
      print_binary(op, "/=", indent);
      break;

	/*SLANG_OPER_MODASSIGN,*/
	/*SLANG_OPER_LSHASSIGN,*/
	/*SLANG_OPER_RSHASSIGN,*/
	/*SLANG_OPER_ORASSIGN,*/
	/*SLANG_OPER_XORASSIGN,*/
	/*SLANG_OPER_ANDASSIGN,*/
   case SLANG_OPER_SELECT:
      spaces(indent);
      printf("SLANG_OPER_SELECT n=%d\n", op->num_children);
      assert(op->num_children == 3);
      slang_print_tree(&op->children[0], indent+3);
      spaces(indent);
      printf("?\n");
      slang_print_tree(&op->children[1], indent+3);
      spaces(indent);
      printf(":\n");
      slang_print_tree(&op->children[2], indent+3);
      break;

   case SLANG_OPER_LOGICALOR:
      print_binary(op, "||", indent);
      break;

   case SLANG_OPER_LOGICALXOR:
      print_binary(op, "^^", indent);
      break;

   case SLANG_OPER_LOGICALAND:
      print_binary(op, "&&", indent);
      break;

   /*SLANG_OPER_BITOR*/
   /*SLANG_OPER_BITXOR*/
   /*SLANG_OPER_BITAND*/
   case SLANG_OPER_EQUAL:
      print_binary(op, "==", indent);
      break;

   case SLANG_OPER_NOTEQUAL:
      print_binary(op, "!=", indent);
      break;

   case SLANG_OPER_LESS:
      print_binary(op, "<", indent);
      break;

   case SLANG_OPER_GREATER:
      print_binary(op, ">", indent);
      break;

   case SLANG_OPER_LESSEQUAL:
      print_binary(op, "<=", indent);
      break;

   case SLANG_OPER_GREATEREQUAL:
      print_binary(op, ">=", indent);
      break;

   /*SLANG_OPER_LSHIFT*/
   /*SLANG_OPER_RSHIFT*/
   case SLANG_OPER_ADD:
      print_binary(op, "+", indent);
      break;

   case SLANG_OPER_SUBTRACT:
      print_binary(op, "-", indent);
      break;

   case SLANG_OPER_MULTIPLY:
      print_binary(op, "*", indent);
      break;

   case SLANG_OPER_DIVIDE:
      print_binary(op, "/", indent);
      break;

   /*SLANG_OPER_MODULUS*/
   case SLANG_OPER_PREINCREMENT:
      spaces(indent);
      printf("PRE++\n");
      slang_print_tree(&op->children[0], indent+3);
      break;

   case SLANG_OPER_PREDECREMENT:
      spaces(indent);
      printf("PRE--\n");
      slang_print_tree(&op->children[0], indent+3);
      break;

   case SLANG_OPER_PLUS:
      spaces(indent);
      printf("SLANG_OPER_PLUS\n");
      break;

   case SLANG_OPER_MINUS:
      spaces(indent);
      printf("SLANG_OPER_MINUS\n");
      break;

   /*SLANG_OPER_COMPLEMENT*/
   case SLANG_OPER_NOT:
      spaces(indent);
      printf("NOT\n");
      slang_print_tree(&op->children[0], indent+3);
      break;

   case SLANG_OPER_SUBSCRIPT:
      spaces(indent);
      printf("SLANG_OPER_SUBSCRIPT locals=%p outer=%p\n",
             (void *) op->locals,
             (void *) op->locals->outer_scope);
      print_generic(op, NULL, indent+3);
      break;

   case SLANG_OPER_CALL:
#if 0
         slang_function *fun
            = _slang_function_locate(A->space.funcs, oper->a_id,
                                     oper->children,
                                     oper->num_children, &A->space, A->atoms);
#endif
      spaces(indent);
      printf("CALL %s(\n", (char *) op->a_id);
      for (i = 0; i < op->num_children; i++) {
         slang_print_tree(&op->children[i], indent+3);
         if (i + 1 < op->num_children) {
            spaces(indent + 3);
            printf(",\n");
         }
      }
      spaces(indent);
      printf(")\n");
      break;

   case SLANG_OPER_METHOD:
      spaces(indent);
      printf("METHOD CALL %s.%s\n", (char *) op->a_obj, (char *) op->a_id);
      break;

   case SLANG_OPER_FIELD:
      spaces(indent);
      printf("FIELD %s of\n", (char*) op->a_id);
      slang_print_tree(&op->children[0], indent+3);
      break;

   case SLANG_OPER_POSTINCREMENT:
      spaces(indent);
      printf("POST++\n");
      slang_print_tree(&op->children[0], indent+3);
      break;

   case SLANG_OPER_POSTDECREMENT:
      spaces(indent);
      printf("POST--\n");
      slang_print_tree(&op->children[0], indent+3);
      break;

   default:
      printf("unknown op->type %d\n", (int) op->type);
   }

}



void
slang_print_function(const slang_function *f, GLboolean body)
{
   GLuint i;

#if 0
   if (strcmp((char *) f->header.a_name, "main") != 0)
     return;
#endif

   printf("FUNCTION %s ( scope=%p\n",
          (char *) f->header.a_name, (void *) f->parameters);

   for (i = 0; i < f->param_count; i++) {
      print_variable(f->parameters->variables[i], 3);
   }

   printf(") param scope = %p\n", (void *) f->parameters);

   if (body && f->body)
      slang_print_tree(f->body, 0);
}





const char *
slang_type_qual_string(slang_type_qualifier q)
{
   switch (q) {
   case SLANG_QUAL_NONE:
      return "none";
   case SLANG_QUAL_CONST:
      return "const";
   case SLANG_QUAL_ATTRIBUTE:
      return "attribute";
   case SLANG_QUAL_VARYING:
      return "varying";
   case SLANG_QUAL_UNIFORM:
      return "uniform";
   case SLANG_QUAL_OUT:
      return "out";
   case SLANG_QUAL_INOUT:
      return "inout";
   case SLANG_QUAL_FIXEDOUTPUT:
      return "fixedoutput";
   case SLANG_QUAL_FIXEDINPUT:
      return "fixedinputk";
   default:
      return "qual?";
   }
}


static const char *
slang_type_string(slang_type_specifier_type t)
{
   switch (t) {
   case SLANG_SPEC_VOID:
      return "void";
   case SLANG_SPEC_BOOL:
      return "bool";
   case SLANG_SPEC_BVEC2:
      return "bvec2";
   case SLANG_SPEC_BVEC3:
      return "bvec3";
   case SLANG_SPEC_BVEC4:
      return "bvec4";
   case SLANG_SPEC_INT:
      return "int";
   case SLANG_SPEC_IVEC2:
      return "ivec2";
   case SLANG_SPEC_IVEC3:
      return "ivec3";
   case SLANG_SPEC_IVEC4:
      return "ivec4";
   case SLANG_SPEC_FLOAT:
      return "float";
   case SLANG_SPEC_VEC2:
      return "vec2";
   case SLANG_SPEC_VEC3:
      return "vec3";
   case SLANG_SPEC_VEC4:
      return "vec4";
   case SLANG_SPEC_MAT2:
      return "mat2";
   case SLANG_SPEC_MAT3:
      return "mat3";
   case SLANG_SPEC_MAT4:
      return "mat4";
   case SLANG_SPEC_SAMPLER_1D:
      return "sampler1D";
   case SLANG_SPEC_SAMPLER_2D:
      return "sampler2D";
   case SLANG_SPEC_SAMPLER_3D:
      return "sampler3D";
   case SLANG_SPEC_SAMPLER_CUBE:
      return "samplerCube";
   case SLANG_SPEC_SAMPLER_1D_SHADOW:
      return "sampler1DShadow";
   case SLANG_SPEC_SAMPLER_2D_SHADOW:
      return "sampler2DShadow";
   case SLANG_SPEC_SAMPLER_RECT:
      return "sampler2DRect";
   case SLANG_SPEC_SAMPLER_RECT_SHADOW:
      return "sampler2DRectShadow";
   case SLANG_SPEC_STRUCT:
      return "struct";
   case SLANG_SPEC_ARRAY:
      return "array";
   default:
      return "type?";
   }
}


static const char *
slang_fq_type_string(const slang_fully_specified_type *t)
{
   static char str[1000];
   sprintf(str, "%s %s", slang_type_qual_string(t->qualifier),
      slang_type_string(t->specifier.type));
   return str;
}


void
slang_print_type(const slang_fully_specified_type *t)
{
   printf("%s %s", slang_type_qual_string(t->qualifier),
      slang_type_string(t->specifier.type));
}


#if 0
static char *
slang_var_string(const slang_variable *v)
{
   static char str[1000];
   sprintf(str, "%s : %s",
           (char *) v->a_name,
           slang_fq_type_string(&v->type));
   return str;
}
#endif


void
slang_print_variable(const slang_variable *v)
{
   printf("Name: %s\n", (char *) v->a_name);
   printf("Type: %s\n", slang_fq_type_string(&v->type));
}


void
_slang_print_var_scope(const slang_variable_scope *vars, int indent)
{
   GLuint i;

   spaces(indent);
   printf("Var scope %p  %d vars:\n", (void *) vars, vars->num_variables);
   for (i = 0; i < vars->num_variables; i++) {
      spaces(indent + 3);
      printf("%s (at %p)\n", (char *) vars->variables[i]->a_name, (void*) (vars->variables + i));
   }
   spaces(indent + 3);
   printf("outer_scope = %p\n", (void*) vars->outer_scope);

   if (vars->outer_scope) {
      /*spaces(indent + 3);*/
      _slang_print_var_scope(vars->outer_scope, indent + 3);
   }
}



int
slang_checksum_tree(const slang_operation *op)
{
   int s = op->num_children;
   GLuint i;

   for (i = 0; i < op->num_children; i++) {
      s += slang_checksum_tree(&op->children[i]);
   }
   return s;
}