/*

   The purpose of this module is to:

   fill in the partial fields of structs.

  When building the AST, we construct the AST items very superficially,
plugging in only the identifying TAGS etc, rather than the types and offsets
etc.

As soon as the tree is built, we need to run a pass over it to insert
'scope' objects so that when we later look for identifiers, we can restrict
the search to relevant scopes.

Once the full tree is built, we then run a pass over the tree and build out
the details of the declarations, so that when we get to the code generation
stage, we have all the offsets, sizes etc in the declarations.

*/

//simplify this more in the morning, just do struct declarations.

int field_size(int p)
{
  int size;
  //fprintf(stdout, "> field_size(%d)\n", p);

  switch (astop(p)) {
    case AST_TYPE_Struct: // nested struct
      //fprintf(stdout, "nested struct \n");
      size = field_size(nthchild(p,2));
      break;

    case AST_TYPE_ArrayOf: // child index lower upper
      { int count, elsize;
      count = (rightchild(nthchild(p,3))-rightchild(nthchild(p,2)));
      elsize = field_size(leftchild(p));
      //fprintf(stdout, "%d-element array of %d bytes\n", count, elsize); 
      size = (count * elsize); 
      }
      break;

    case AST_TYPE_PointerTo: // child
      //fprintf(stdout, "pointer to \n");
      size = MACHINE_DEPENDENT_POINTER_SIZE;
      break;

    case AST_TYPE_Atom:          // signed bytecount
      //fprintf(stdout, "%ssigned scalar element of %d bytes\n", (leftchild(p)==0 ? "un":""), rightchild(p));
      size = rightchild(p);
      break;

    default:
      fprintf(stdout, "! Internal error in %s at line %d: astop(%d) = %d\n", __FILE__, __LINE__, p, astop(p)); exit(1);

  }
  //fprintf(stdout, "< field_size(%d) = %d\n", p, size);
  return size;
}

int add_up_field_sizes(int p) // struct should now be complete except for top-level struct size
{
  int size = 0;
  
  //fprintf(stdout, "adding field sizes within %s:\n", c[leftchild(leftchild(p))].s);
  assert(astop(p) == AST_TYPE_Struct);
  p = rightchild(p); // structtypename childlinks structsize
  while (p != -1) {  // next_member tag this_element_type offset
    int child_size;
    //fprintf(stdout, "   looping:  p = %d,  op = %d\n", p, astop(p));
    if (astop(p) != AST_TYPE_StructMember) {
      //fprintf(stdout, "! Internal error in %s at line %d - p = %d, type = %d\n", __FILE__, __LINE__, p, astop(p)); exit(1);
    }
    child_size = field_size(nthchild(p,2));
    //fprintf(stdout, "   adding child %s size %d\n", c[leftchild(rightchild(p))].s, child_size);
    size = size + child_size;
    p = leftchild(p);
  }
  //fprintf(stdout, "done adding field sizes\n");
  return size;
}

void fix_struct_pointers(int program, int p)
{
  //show_one_ast(p,-1,__LINE__);
  //fprintf(stdout, "structure of type %s \n", c[leftchild(leftchild(p))].s);
  if (rightchild(p) == -1) { // no child links???
    int structdec;
    // this is the declaration of a variable.  fill in this field from the
    // declaration of the type.
    structdec = lookup_scalar_type(leftchild(p));
    //fprintf(stdout, "which is declared at %d\n", structdec);
    replace_struct_pointer(program, p, structdec);
  }
}

void fix_struct_field_offsets(int program, int p)
{
  int lhs, rhs;

  if ((p == -1) || (astop(p) == -1)) return;

  
  //fprintf(stdout, "> fix_struct_field_offsets(%d (%s))\n", p, op[AST[p]-AST_BASE].Diag_Name);

#ifdef NEVER
  switch (astop(p)) {

  case AST_TYPE_Struct: // structtypename childlinks structsize
    {int structdec;
    show_one_ast(p,-1,__LINE__);
    fprintf(stdout, "structure of type %s \n", c[leftchild(leftchild(p))].s);
    if (rightchild(p) == -1) { // no child links???
      // this is the declaration of a variable.  fill in this field from the
      // declaration of the type.
      structdec = lookup_scalar_type(leftchild(p));
      // either replace references to p with references to structdec,
      // or fill in all the missing fields of p.
      fprintf(stdout, "which is declared at %d\n", structdec);
      replace_struct_pointer(program, p, structdec);
      fix_one_struct(rightchild(structdec)); 
    } else {
      fix_one_struct(rightchild(p)); 
    }
    if (nthchild(p, 2) == -1) { // no computed size yet?
      // walk subfields, assign sizes and offsets
      int offset = 0, fieldsize = 0, child = rightchild(p);
      fprintf(stdout, "setting object size for %s:\n", c[leftchild(leftchild(p))].s);
      while (child != -1) {  // next_member tag this_element_type offset
        nthchild(child, 3) = offset; // child's offset
        fieldsize = fix_one_struct(nthchild(child, 2));
	fprintf(stdout, "child %s - offset %d, field size %d\n", 
                c[leftchild(rightchild(child))].s, offset, fieldsize);
        offset += fieldsize;
        child = leftchild(child);
      }
    }
    }
    break;

  case AST_TYPE_ArrayOf: // child index lower upper
    fprintf(stdout, "%d-element array of \n", rightchild(nthchild(p,3))-rightchild(nthchild(p,2)));
    size = size + ((rightchild(nthchild(p,3))-rightchild(nthchild(p,2))) * fix_one_struct(leftchild(p))); 
    break;

  case AST_TYPE_PointerTo: // child
    fprintf(stdout, "pointer to \n");
    size = size + 4; fix_one_struct(leftchild(p)); 
    break;

  case AST_TYPE_StructMember: // next_member tag this_element_type offset
    fprintf(stdout, "child %s which is\n", c[leftchild(rightchild(p))].s);
    //fix_one_struct(nthchild(p,2)); should show type, check type is complete
    fprintf(stdout, "at offset %d\n", nthchild(p,3));
    size = size + fix_one_struct(leftchild(p)); 
    break;

  case AST_TYPE_Atom:          // signed bytecount
    fprintf(stdout, "%ssigned scalar element of %d bytes\n", (leftchild(p)==0 ? "un":""), rightchild(p));
    size = size + rightchild(p);
    break;


//  case AST_TAG:    // atom/scalar  - not sure if this needs to be here
//    if (TYPEINFO(p) == -1) TYPEINFO(p) = lookup_scalar_type(p);
//    break;


  default
    fprintf(stdout, "! Internal error in %s at line %d\n", __FILE__, __LINE__); exit(1);

  }

#endif
  //fprintf(stdout, "< fix_struct_field_offsets(%d)\n", p);
}

void fix_struct_size(int program, int p)
{
  int size = add_up_field_sizes(p);
  //fprintf(stdout, "setting struct size of %d to %d\n", p, size);
  nthchild(p, 2) = size;
}

void fix_one_struct(int program, int p)
{
  
  fix_struct_pointers(program, p); 
  
  fix_struct_field_offsets(program, p);
  
  fix_struct_size(program, p);
}

void fix_all_structs(int program, int p)
{
  int i;
  int disp;
  int this;

  if (astop(p) == AST_Scope) {
    push_scope(p);
  }
  if (astop(p) == AST_TYPE_Struct) {
    // make sure any child structs already handled. (nested, not pointed to)
    int child = rightchild(p);
    //fprintf(stdout, "looking for nested structs in %s:\n", c[leftchild(leftchild(p))].s);
    while (child != -1) {  // next_member tag this_element_type offset
      if (astop(nthchild(child,2)) == AST_TYPE_Struct) fix_all_structs(program, nthchild(child,2));
      child = leftchild(child);
    }
    //fprintf(stdout, "done looking for nested structs in %s:\n", c[leftchild(leftchild(p))].s);
    //fprintf(stdout, "now we do %s itself:\n", c[leftchild(leftchild(p))].s);
    fix_one_struct(program, p); 
  } else {
    disp = op[astop(p)-AST_BASE].Display_Children;
    this = 1<<(children(p)-1);
    for (i = 0; i < children(p); i++) {
      if ((nthchild(p, i) != -1) && (disp&this)) {
        fix_all_structs(program, nthchild(p, i)); 
      }
      this = this>>1;
    }
  }
  if (astop(p) == AST_Scope) {
    pop_scope();
  }
}

void fix_structs(int p)
{
  fix_all_structs(p, p);
}
