#include "scanner.h"
#include "otalk.h"
#include "primitives.h"
#include <string.h>

extern char* bytes_code_names[];
extern int bytes_code_args[];

void print_tag(t_tag tag,t_token);

void print_token(t_token token)
{
  print_tag(TOKEN_TAG(token),token);
}

char* tag_as_string(t_tag tag)
{
  switch(tag) {
  case tag_plus:              return "tag_plus";
  case tag_minus:             return "tag_minus";
  case tag_mul:               return "tag_mul";
  case tag_div:               return "tag_div";
    
  case tag_less:              return "tag_less";
  case tag_less_eq:           return "tag_less_eq";
  case tag_greater:           return "tag_greater";
  case tag_greater_eq:        return "tag_greater_eq";
  case tag_equal:             return "tag_equal";
    

  case tag_and:               return "tag_and";
  case tag_or:                return "tag_or";
  case tag_open_par:          return "tag_open_par";
  case tag_close_par:         return "tag_close_par";
  case tag_blockopen_par:     return "tag_blockopen_par";
  case tag_blockclose_par:    return "tag_blockclose_par";
  case tag_dot:               return "tag_dot";
  case tag_comma:             return "tag_comma";
  case tag_method_sep:        return "tag_method_sep";
  case tag_semicolon:         return "tag_semicolon";
  case tag_pipe:              return "tag_pipe";
  case tag_return:            return "tag_return";
  case tag_assign:            return "tag_assign";
  case tag_name:              return "tag_name";
  case tag_string:            return "tag_string";
  case tag_symbol:            return "tag_symbol";
  case tag_if:                return "tag_if";
  case tag_then:              return "tag_then";
  case tag_else:              return "tag_else";
  case tag_endif:             return "tag_endif";
  case tag_number:            return "tag_number";
  case tag_while:             return "tag_while";
  case tag_end:               return "tag_end";
  case tag_errorhandler:      return "tag_errorhandler";
  case tag_eof:               return "tag_eof";
    
  default:                    return "unknown";
  }
}

void print_tag(t_tag tag,t_token token)
{
  switch(tag) {
  case tag_plus:           putchar('+'); break;
  case tag_minus:          putchar('-'); break;
  case tag_mul:            putchar('*'); break;
  case tag_div:            putchar('/'); break;

  case tag_less:           putchar('<'); break;
  case tag_less_eq:        putchar('<'); putchar('='); break;
  case tag_greater:        putchar('>'); break;
  case tag_greater_eq:     putchar('>'); putchar('='); break;
  case tag_equal:          putchar('='); putchar('='); break;


  case tag_and:            putchar('&'); break;
  case tag_or:             fputs("||",stdout);   break;
  case tag_open_par:       putchar('('); break;
  case tag_close_par:      putchar(')'); break;
  case tag_blockopen_par:  putchar('['); break;
  case tag_blockclose_par: putchar(']'); break;
  case tag_dot:            putchar('.'); break;
  case tag_comma:          putchar(','); break;
  case tag_method_sep:     putchar('!'); break;
  case tag_semicolon:      putchar(';'); break;
  case tag_pipe:           putchar('|'); break;
  case tag_return:         putchar('^'); break;
  case tag_assign:         putchar('='); break;
  case tag_name:           fputs(TOKEN_NAME(token),stdout);    break;
  case tag_string:         printf("\"%s\"",TOKEN_NAME(token)); break;
  case tag_symbol:         printf(":%s",TOKEN_NAME(token));    break;
  case tag_if:             fputs("if",stdout);    break;
  case tag_then:           fputs("then",stdout);  break;
  case tag_else:           fputs("else",stdout);  break;
  case tag_endif:          fputs("endif",stdout); break;
  case tag_end:            fputs("end",stdout); break;
  case tag_while:          fputs("while",stdout);  break;
  case tag_errorhandler:   fputs("errorhandler",stdout);  break;
  case tag_number:         printf("%d",TOKEN_NUMBER(token)); break;
  default:                 printf("TAG(%d)",(int)TOKEN_TAG(token));
  }
};

static int print_offset=0;

void print_newline()
{
  int i;
  putchar('\n');
  for (i=0; i<print_offset; ++i)
    putchar(' ');
}

void print_liste(t_node node, char* sep, int newline_p)
{
  int i;

  for (i=0; i<NODE_FILL(node); ++i) {
    print_node(NODE_ENTRY(node,i));
    if ((i+1) < NODE_FILL(node)) {
      fputs(sep,stdout);
      if (newline_p) {print_newline();}
    }
  }
}

void print_node(t_node node)
{
  int i;

  if (node == NULL) {
    printf("NULL");
    return;
  }
  
  switch(NODE_TAG(node)) {
  case tag_plus:  case tag_minus: 
  case tag_div:   case tag_mul:
  case tag_or:    case tag_and:
  case tag_equal:
  case tag_less:    case tag_less_eq:
  case tag_greater: case tag_greater_eq:
    putchar('(');
    print_node(NODE_LEFT(node));
    print_token(NODE_TOKEN(node));
    print_node(NODE_RIGHT(node));
    putchar(')');
    break;
  case tag_assign:
    print_node(NODE_LEFT(node));
    putchar(' ');
    print_token(NODE_TOKEN(node));
    putchar(' ');
    print_node(NODE_RIGHT(node));
    break;
  case tag_if:
    printf("if ");
    print_node(IF_TEST(node));
    print_newline();
    printf("then");
    print_offset += 2;
    print_newline();
    print_liste(IF_THEN(node),";",1);
    print_offset -= 2;
    print_newline();
    printf("else");
    print_offset += 2;
    print_newline();
    print_liste(IF_ELSE(node),";",1);
    print_offset -= 2;
    print_newline();
    printf("endif");
    break;
  case tag_dot:
    print_node(NODE_LEFT(node));
    print_token(NODE_TOKEN(node));
    print_node(NODE_RIGHT(node));
    break;
  case tag_method_vector:
    printf("\n!--------------------------------------------------!\n");
    print_node(METHOD_CLASS(node));
    putchar('.');
    print_node(METHOD_NAME(node));
    putchar(' ');
    print_node(METHOD_PARAMETERS(node));
    putchar('\n');
    putchar('|');
    print_liste(METHOD_VARS(node)," ",0);
    putchar('|');
    putchar('\n');
    print_liste(METHOD_STATEMENTS(node),";",1);
    printf("\n");
    break;
  case tag_closure_vector:
    putchar('[');
    putchar('|');
    print_node(NODE_ENTRY(node,0));
    putchar('|');
    putchar('\n');
    print_liste(NODE_ENTRY(node,1),";",1);
    putchar(']');
    break;
  case tag_call:
    print_node(NODE_ENTRY(node,0));
    putchar('(');
    print_node(NODE_ENTRY(node,1));
    putchar(')');
    break;
  case tag_vector:
    for (i=0; i<NODE_FILL(node); ++i) {
      print_node(NODE_ENTRY(node,i));
      if ((i+1) < NODE_FILL(node))
	putchar(',');
    }
    break;
  case tag_return:
    putchar('^');
    print_node(NODE_LEFT(node));
    break;
  default: 
    print_token(NODE_TOKEN(node));
  }
}

void print_class(T_OBJECT class)
{
  T_OBJECT liste = CLASS_METHODS(class);
  int i;
  int max = NUMBER(VECTOR_FILL(liste));
  
  printf("\nKlasse %s Methods: %d\n",STRING_CHARS(CLASS_NAME(class)),max);
  printf("Own Slots: %d\n",VECTOR_LENGTH(CLASS_SLOTS(class)));

  for (i=1; i <= max; ++i) {
    printf("  Methode %-20s %d %p\n",
	   STRING_CHARS(O_METHOD_NAME(VECTOR_ENTRY(liste,i))),
	   NUMBER(O_METHOD_N_ARGS(VECTOR_ENTRY(liste,i))),
	   VECTOR_ENTRY(liste,i));
  }
}

void write_byte_code(int length, t_codevector vector, T_OBJECT constants, T_OBJECT* constants_vector)
{
  int i=0;
  int index;

  while (i < length) {
    index = vector[i];
    printf("%3d: %-14s ",i,bytes_code_names[index]);
    switch(bytes_code_args[index]) {
    case 0:
      i += 1;
      break;
    case 1:
      printf("%d",(int)vector[i+1]);
      i += 2;
      break;
    case 2:
      printf("%04d",(int)vector[i+1]+128*(int)vector[i+2]);
      i += 3;
      break;
    case -2:
      printf("%d %d",(int)vector[i+1],(int)vector[i+2]);
      i += 3;
      break;
    case -20:
      if (constants != NULL) {
	printf("%s",STRING_CHARS(SLOT_N(constants,vector[i+1])));
      }
      else if (constants_vector != NULL) {
	printf("%s",STRING_CHARS(constants_vector[vector[i+1]]));
      }
      else {
	printf("%d",(int)vector[i+1]);
      }
      printf(" Args: %d",(int)vector[i+2]);
      i += 3;
      break;
    case -10:
      if (constants != NULL) {
	printf("%s",STRING_CHARS(SLOT_N(constants,vector[i+1])));
      }
      else if (constants_vector != NULL) {
	printf("%s",STRING_CHARS(constants_vector[vector[i+1]]));
      }
      else {
	printf("%d",(int)vector[i+1]);
      }
      i += 2;
      break;
    case -11: {
      extern struct primitive_def primitives[];

      printf("%s Args: %d",primitives[(int)vector[i+1]].name,
	                   primitives[(int)vector[i+1]].args);
      i += 2;
      break;
    }
    default:
      i+=1;
    }
    if (i>1000) {
      puts("Output stopped");
      return;
    }
    puts("");
  }
}

void print_method(T_OBJECT method)
{
  printf("Methode %s\n",STRING_CHARS(O_METHOD_NAME(method)));
  write_byte_code(BV_LENGTH(O_METHOD_VECTOR(method)),
		  BV_BYTES(O_METHOD_VECTOR(method)),
		  O_METHOD_CONSTANTS(method),NULL);
}


/*eof*/
