// get new temp
temp_var * new_temp(ast_node root) {
    if (!root)
        return NULL;

    // get root's scope table temp list
    temp_list * t_list = ((symhashtable_t *)root->scope_table)->t_list;

    // get a new temp from the list
    temp_var * new_var = (temp_var *)calloc(1,sizeof(temp_var));
    assert(new_var);

    // give unique id
    new_var->id = t_list->count;

    // make new name -- not yet
    char * name = make_temp_name(new_var->id);

    // put new temp in list
    t_list->list[t_list->count] = new_var;

    // if list is full, expand!
    t_list->count++;
    if (t_list->count == t_list->size) {
        t_list->size *= 2;
        t_list->list = realloc(t_list->list, t_list->size * sizeof(temp_var *));
        assert(t_list->list);
    }

    // add that new temp to local table under the name
    symnode_t * new_node = insert_into_symhashtable(root->scope_table,name, NULL);

    // attach temp to created symnode
    new_var->temp_symnode = new_node;

    // set node type
    set_node_type(new_node, VAR_SYM);

    // set node characteristics
    var_symbol node_model;
    node_model.name = name;
    node_model.type = INT_TS;
    node_model.modifier = SINGLE_DT;
    node_model.specie = TEMP_VAR;
    node_model.byte_size = TYPE_SIZE(INT_TS);

    // save to symbol table
    set_node_var(new_node, &node_model);

    // return newly created temp
    return new_var;
}
/* 
 * instert_into_symboltable
 *
 * Insert an entry into the innermost scope of symbol table.  First 
 * make sure it's not already in that scope.  Return a pointer to the
 * entry. 
 */
symnode_t *insert_into_symboltable(symboltable_t *symtab, var_lookup_type type, char *name, int lineno) {

  assert(symtab);
  assert(symtab->leaf);
  
  symnode_t *node = lookup_symhashtable(symtab->leaf, name, NOHASHSLOT);

  /* error check!! */
  
  if (node == NULL) {
    node = insert_into_symhashtable(symtab->leaf, type, name, lineno, LOCAL_VT);
    return node;
  } else {
    return NULL;
  }
   
}
symnode_t *insert_temp(symboltable_t *symtab, var_lookup_type type, char *name, int lineno) {

  assert(symtab);
  assert(symtab->leaf);

  int name_len = strlen(name);
  char *typed_name = calloc(1, sizeof(char) * (name_len + 3));
  sprintf(typed_name, "%s%s", temp_prefix, name);

  symnode_t *node = lookup_symhashtable(symtab->leaf, typed_name, NOHASHSLOT);

  if (node == NULL) {
    node = insert_into_symhashtable(symtab->root, type, typed_name, lineno, TEMP_VT);
    return node;
  } else {
    return NULL;
  }
}
/* Insert an entry into the innermost scope of symbol table.  First
   make sure it's not already in that scope.  Return a pointer to the
   entry. */
  symnode_t *insert_into_symboltable(symboltable_t *symtab, ast_node astnode) {

    char *name = astnode->value_string;
    assert(symtab);
    assert(symtab->leaf);

    symnode_t *node = lookup_symhashtable(symtab->leaf, name, NOHASHSLOT);

    /* error check!! */

    if (node == NULL) {
      node = insert_into_symhashtable(symtab->leaf, astnode);
      return node;
    } else {
      return NULL;
    }

  }
void collect_literals(ast_node root, symboltable_t *symtab){

  ast_node child;
  symnode_t *node;
  symhashtable_t *collection = symtab->literal_collection;
  assert(collection != NULL);
  char* name;
  int slot = 0;

  /* Recursively call the function to traverse the tree in a preorder way */
  for(child = root->left_child; child != NULL; child = child->right_sibling){
    collect_literals(child, symtab);
  }

  switch (root->node_type) {
    case STRING_LITERAL_N:
      name = root->value_string;

      // Calculate slot for this node, try to find the current node in the symbol table
      slot = hashPJW(name, collection->size);
      node = lookup_symhashtable(collection, name, slot);

      if(node == NULL){
        insert_into_symhashtable(collection, root);
      }

      break;

    /* Need to hash the value differently so as to not to mistake strings and
    ints, (e.g. 1 vs "1") */
    case INT_LITERAL_N:

      name = calloc(DEFAULTSTRSIZE, sizeof(char));
      sprintf(name, "__%d", root->value_int);
      root->value_string = strdup(name);

      slot = hashPJW(name, collection->size);
      node = lookup_symhashtable(collection, name, slot);

      if(node == NULL){
        insert_into_symhashtable(collection, root);
      }
      break;

    case RETURN_N:
      // Empty return. Create a new symnode to add to
      // the collection because otherwise it will give us
      // trouble later when trying to create quads, since
      // the function to create quads takes symnodes and there
      // is currently no symnode for this empty return.

      if(root->left_child != NULL){
        if(root->left_child->node_type == VOID_TYPE_N){
          name = calloc(DEFAULTSTRSIZE, sizeof(char));
          sprintf(name, "__%s", "void");

          root->left_child->value_string = calloc(DEFAULTSTRSIZE, sizeof(char));
          strcpy(root->left_child->value_string, name);

          slot = hashPJW(name, collection->size);
          node = lookup_symhashtable(collection, name, slot);

          if(node == NULL){
            insert_into_symhashtable(collection, root->left_child);
          }
        }
      }
    default:
      break;
  }
}
/*
 * Builds the symbol table, creating scopes as needed, and linking them together.
 * Adds variable and function identifiers to scopes appropriately.
 */
void build_symbol_table(ast_node root, int level, int sibno, symboltable_t *symtab) {
  //calculate the scope for the variable/func declaration
  //calculate the parent for that variable/func declaration
  //need function to take as input
  //printf("here \n");
  symhashtable_t *hash;

  /* Depending on node types, go deeper, create sibling scopes, add to hashtable,
   * or take other appropriate action.
   */
  switch (root->node_type) {
    ast_node param;
    int param_offset = 4;
    int param_count = 0;

    case SEQ_N:     // change main level when see a new sequence
      level++;
      break;

    case FORMAL_PARAMS_N:
      level++;
      param_count = 0;
      insert_scope_info(root, level, sibno, MAX(level - 1, 0), getSibling(level) );
    //   for(param = root->left_child; param != NULL; param = param->right_sibling){
    //     if(param->node_type == ARRAY_TYPE_N || param->return_type == ARRAY_TYPE_N){
    //      param_count += DEFAULT_ARRAY_PARAM_SIZE;
    //    }
    //    else{
    //      param_count++;
    //    }
    //  }
    //  printf("PARAM COUNT!!!!! = %d\n", param_count);
    //
    //  for(param = root->left_child; param != NULL; param = param->right_sibling){
    //    if(param->node_type == ARRAY_TYPE_N || param->return_type == ARRAY_TYPE_N){
    //      param->snode->offset = param_count * 4;
    //      param->snode->offset -= DEFAULT_ARRAY_PARAM_SIZE;
    //    }
    //    else{
    //     param->snode->offset = param_count-- * 4;
    //    }
    // }


      break;

    case FUNC_DECLARATION_N: // function declaraions
      //does hashtable exist with given lvl, siblvl (use find_hashtable)
      check_if_redef(root, symtab ,level, sibno);
      hash = find_hashtable(symtab->root, level, sibno);
      if(hash == NULL) {
        hash = make_insert_hashtable(symtab->root, level, sibno, MAX(level - 1, 0), getSibling(level) );

      }
      insert_into_symhashtable(hash, root); // will only insert if it is empty.
      insert_scope_info(root, level, sibno, MAX(level - 1, 0), getSibling(level) );
      break;

    case FUNCTION_N:
      check_if_declared(root, symtab ,level, sibno);
      insert_scope_info(root, level, sibno, MAX(level - 1, 0), getSibling(level) );
      break;

    case ID_N:      /* print the id */

        check_if_redef(root, symtab ,level, sibno);

      //if(root->return_type != 0) {  // a non-zero value means that it is a declaration, since only declarations
                                    // are assigned a return type when building the abstract syntax tree.
      if(root->isDecl){

        hash = find_hashtable(symtab->root, level, sibno);
        if(hash == NULL) {
          hash = make_insert_hashtable(symtab->root, level, sibno, MAX(level - 1, 0), getSibling(level) );
        }
        insert_into_symhashtable(hash, root);
      }
      else {  // don't know if previously declared
        check_if_declared(root, symtab , level, sibno);
      }
      insert_scope_info(root, level, sibno, MAX(level - 1, 0), getSibling(level) );
      break;

    case ARRAY_TYPE_N:             // check for return types!

        check_if_redef(root, symtab ,level, sibno);

      //cif(root->return_type != 0) {
      if(root->isDecl){
        hash = find_hashtable(symtab->root, level, sibno);
        if(hash == NULL) {
          hash = make_insert_hashtable(symtab->root, level, sibno, MAX(level - 1, 0), getSibling(level) );
          }
        insert_into_symhashtable(hash, root);
      }
      else {
        check_if_declared(root, symtab , level, sibno);
      }
      insert_scope_info(root, level, sibno, MAX(level - 1, 0), getSibling(level) );
      break;

    case RETURN_N:
      insert_scope_info(root, level, sibno, MAX(level - 1, 0), getSibling(level) );
      break;

    default:
      // printf("at default of switch\n");
      assert(symtab->root != NULL);
      hash = find_hashtable(symtab->root, level, sibno);
      if(hash == NULL) {
        hash = make_insert_hashtable(symtab->root, level, sibno, MAX(level - 1, 0), getSibling(level) );
      }
      //note: cannot use insert_scope_info here because siblings[level - 1] causes invalid read as level-1 can go negative
      break;
  }

  if(arraylen == level) {
    arraylen = arraylen + DELTA;
    siblings = realloc(siblings, sizeof(int) * arraylen);

    assert(siblings != NULL);

    for(int k=0; k < DELTA; k++) {
      siblings[arraylen - (DELTA-k)] = 0;
    }
  }

  /* Recurse on each child of the subtree root, with a depth one
     greater than the root's depth. */
  ast_node child;
  for (child = root->left_child; child != NULL; child = child->right_sibling)
    build_symbol_table(child, level, siblings[level], symtab);

  if(root->node_type == SEQ_N){//} || root->node_type == FORMAL_PARAMS_N){
    siblings[level]++;  // change sibling level after you're done printing all
                      // subtrees, i.e., after done recursing.
  }
}