import java.util.Vector;

aspect C_CodeGenEnv {

  // Environment wrapper for C-code generation
  // handles qualid nesting, indentation, file writing and
  // prefix propagation

  public class C_env {

    final private static class C_printer {
      
      private boolean newline = true;
      private PrintStream out;

      public C_printer(PrintStream out) {
	this.out = out;
      }

      public void print(C_env env, String s) {
	if (newline) {
	  newline = false;
	  for (int i = 0 ; i < env.indent ; i++) {
	    out.print("  ");
	  }
	}
	out.print(s);
      }
      public void println(C_env env, String s) {
	print(env, s);
	out.println();
	newline = true;
      }
    }

    public final int version; //labcomm version (2006 or 2014)
    public final String verStr; // version suffix to append (currently _2006 and empty string)

    public final String qualid;
    public final String lcName;
    public final String rawPrefix;
    public final String prefix;
    private int indent;
    public final int depth;
    private C_printer printer;
    public final int nestedLevel;
    private boolean rootIsPointer;
    private int rootLevel;

    public boolean versionHasMetaData() {
      return version != 2006;
    }

    private C_env(String qualid, String lcName, String rawPrefix, 
		  int indent, int depth, C_printer printer,
                  int nestedLevel, int version)
    {
      this.version = version;
      this.verStr = LabCommVersion.versionString(version);
      this.qualid = qualid;
      this.lcName = lcName;
      this.rawPrefix = rawPrefix;
      if (rawPrefix == null) {
        System.err.println("WARNING: prefix==null");
        this.prefix = ""; 
      } else if (rawPrefix.equals("")) {
        this.prefix = rawPrefix;
      } else {
        this.prefix = rawPrefix + "_";
      }
      this.indent = indent;
      this.depth = depth;
      this.printer = printer;
      this.nestedLevel = nestedLevel;
    }

    public C_env(String qualid, String lcName, String rawPrefix,
		 PrintStream out, int version)
    {
      this(qualid, lcName, rawPrefix, 0, 0, new C_printer(out), 0, version);
    }

    public C_env(String qualid, String lcName, String rawPrefix,
		 int indent, int depth, C_printer printer, int version) {
            this(qualid, lcName, rawPrefix, indent, depth, printer, 0, version);
    }

    public C_env nestArray(String suffix) {
      return new C_env(qualid + suffix, lcName, rawPrefix,
		       indent, depth + 1, printer, nestedLevel + 1, version);
    }

    public C_env nestStruct(String suffix) {
      return new C_env(qualid + suffix, lcName, rawPrefix, 
		       indent, depth, printer, nestedLevel + 1, version);
    }

    public void indent() {
      indent++;
    }

    public void unindent() {
      indent--;
    }

    public String prefix() {
      return rawPrefix;
    }

    public void print(String s) {
      printer.print(this, s);
    }

    public void println() {
      printer.println(this, "");
    }

    public void println(String s) {
      printer.println(this, s);
    }

    public C_env setPointer() {
      rootIsPointer = true;
      rootLevel = nestedLevel;
      return this;
    }

    public String memberAccessor() {
      return (rootIsPointer && (nestedLevel == rootLevel)) ? "->" : ".";
    }

    public String accessor() {
      return (rootIsPointer && (nestedLevel == rootLevel)) ? "*" : "";
    }
  }

  public C_env ArrayType.C_Nest(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_Nest(C_env env)" + 
		    " not declared");
  }

  public C_env FixedArrayType.C_Nest(C_env env) {
    String index = env.memberAccessor() + "a";
    for (int i = 0 ; i < getNumExp() ; i++) {
      index += "[i_" + env.depth + "_" + i + "]";
    }
    return env.nestArray(index);
  }

  public C_env VariableArrayType.C_Nest(C_env env) {
    return env.nestArray(env.memberAccessor() + "a[i_" + env.depth + "]");
  }


}

aspect C_IsDynamic {
  
  // Determine if a type has dynamically allocated data
  syn boolean Decl.C_isDynamic() = getType().C_isDynamic();
  syn boolean Type.C_isDynamic() = false;
  syn boolean PrimType.C_isDynamic() = getToken() == LABCOMM_STRING;
  syn boolean UserType.C_isDynamic() = 
    lookupType(getName()).getType().C_isDynamic();
  syn boolean StructType.C_isDynamic() {
    for (int i = 0 ; i < getNumField() ; i++) {
      if (getField(i).getType().C_isDynamic()) {
	return true;
      }
    }
    return false;
  }
  syn boolean FixedArrayType.C_isDynamic() = getType().C_isDynamic();
  syn boolean VariableArrayType.C_isDynamic() = true;
}

aspect C_CodeGen {

  public void Program.C_genH(PrintStream out, Vector includes, 
			     String lcName, String prefix, int version) {
    C_env env = new C_env("", lcName, prefix, out, version);

    // Hackish prettyprint preamble
    out.println("/* LabComm declarations:");
    pp(out);
    out.println("*/");
    env.println("");
    env.println("");
    env.println("#ifndef __LABCOMM_" + env.lcName + "_H__"); 
    env.println("#define __LABCOMM_" + env.lcName + "_H__");
    env.println("");

    // Include
    env.println("#include \"labcomm2014.h\"");
    for (int i = 0 ; i < includes.size() ; i++) {
      env.println("#include \"" + includes.get(i) + "\"");
    }
    env.println("");

    C_emitH(env);

    env.println("#endif");
  }

  public void Program.C_genC(PrintStream out, Vector includes, 
			     String lcName, String prefix, int version) {
    C_env env = new C_env("", lcName, prefix, out, version);

    // Include
    env.println("#include \"labcomm2014.h\"");
    env.println("#include \"labcomm2014_private.h\"");
    for (int i = 0 ; i < includes.size() ; i++) {
      env.println("#include \"" + includes.get(i) + "\"");
    }
    env.println("");
    
    // Method Implementations
    C_emitC(env);
  }

  public void Program.C_emitH(C_env env) {
    for (int i = 0; i < getNumDecl(); i++) {
      getDecl(i).C_emitType(env);
      getDecl(i).C_emitDecoderDeclaration(env);
      getDecl(i).C_emitEncoderDeclaration(env);
      getDecl(i).C_emitSizeofDeclaration(env);
      getDecl(i).C_emitCopyDeclaration(env);
      getDecl(i).C_emitCopyDeallocationDeclaration(env);
      env.println("");
    }
    C_emitConstructorDeclaration(env);
    C_emitForAll(env);
  }

  public void Program.C_emitC(C_env env) {
    for (int i = 0; i < getNumDecl(); i++) {
      getDecl(i).C_emitSignature(env);
      getDecl(i).C_emitDecoder(env);
      getDecl(i).C_emitDecoderRegisterHandler(env);
      getDecl(i).C_emitDecoderIoctl(env);
      getDecl(i).C_emitEncoder(env);
      getDecl(i).C_emitEncoderRegisterHandler(env);
      getDecl(i).C_emitEncoderIoctl(env);
      getDecl(i).C_emitSizeof(env);
      getDecl(i).C_emitCopy(env);
      getDecl(i).C_emitCopyDeallocation(env);
    }
    C_emitConstructor(env);
  }
 
}

aspect C_Common {

  public void ArrayType.C_emitLoopVariables(C_env env) {
    for (int i = 0 ; i < getNumExp() ; i++) {
      env.println("int i_" + env.depth + "_" + i + ";");
    }
  }

}

aspect C_Type {

  public void Decl.C_emitType(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitType(C_env env)" + 
		    " not declared");
  }

  public void TypeDecl.C_emitType(C_env env) {
    env.println("#ifndef LABCOMM_DEFINED_" + env.prefix + getName());
    env.print("typedef ");
    getType().C_emitType(env, env.prefix + getName());
    env.println(";");
    env.println("#define LABCOMM_DEFINED_" + env.prefix + getName());
    env.println("#endif");
  }

  public void SampleDecl.C_emitType(C_env env) {
    env.println("#ifndef LABCOMM_DEFINED_" + env.prefix + getName());
    env.print("typedef ");
    getType().C_emitType(env, env.prefix + getName());
    env.println(";");
    env.println("#define LABCOMM_DEFINED_" + env.prefix + getName());
    env.println("#endif");
    env.println("extern const struct labcomm2014_signature " +
                "*labcomm2014_signature_" + env.prefix + getName() + 
                ";");
  }

  public void Type.C_emitType(C_env env, String name) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitType(C_env env, String name)" + 
		    " not declared");
  }

  public void VoidType.C_emitType(C_env env, String name) {
    env.print("char " + name);
  }

  public void PrimType.C_emitType(C_env env, String name) {
    switch (getToken()) {
      case LABCOMM_BOOLEAN: { env.print("uint8_t"); } break;
      case LABCOMM_BYTE: { env.print("uint8_t"); } break;
      case LABCOMM_SHORT: { env.print("int16_t"); } break;
      case LABCOMM_INT: { env.print("int32_t"); } break;
      case LABCOMM_LONG: { env.print("int64_t"); } break;
      case LABCOMM_FLOAT: { env.print("float"); } break;
      case LABCOMM_DOUBLE: { env.print("double"); } break;
      case LABCOMM_STRING: { env.print("char*"); } break;
      case LABCOMM_SAMPLE: { 
        env.print("const struct labcomm2014_signature *"); 
      } break;
    }
    env.print(" " + name);
  }

  public void UserType.C_emitType(C_env env, String name) {
    env.print(env.prefix + getName() + " " + name);
  }

  public void StructType.C_emitType(C_env env, String name) {
    env.println("struct {");
    env.indent();
    for (int i = 0 ; i < getNumField() ; i++) {
      getField(i).C_emitType(env);
      env.println(";");
    }
    env.unindent();
    env.print("} " + name);
  }

  public void Field.C_emitType(C_env env) {
    getType().C_emitType(env, getName());
  }

  public void FixedArrayType.C_emitType(C_env env, String name) {
    env.println("struct {");
    env.indent();
    StringBuffer index = new StringBuffer("a");
    for (int i = 0 ; i < getNumExp() ; i++) {
      index.append("[" + getExp(i).C_getValue() + "]");
    }
    getType().C_emitType(env, index.toString());
    env.println(";");
    env.unindent();
    env.print("} " + name);
  }

  public void VariableArrayType.C_emitType(C_env env, String name) {
    env.println("struct {");
    env.indent();
    for (int i = 0 ; i < getNumExp() ; i++) {
      if (getExp(i) instanceof VariableSize) {
	env.println("int n_" + i + ";");
      } else {
	env.println("// n_" + i + "=" + getExp(i).C_getValue());
      }
    }
    getType().C_emitType(env, "*a");
    env.println(";");
    env.unindent();
    env.print("} " + name);
  }

  public String Exp.C_getValue() {
   throw new Error(this.getClass().getName() + 
		    ".C_getValue()" + 
		    " not declared");
  }

  public String IntegerLiteral.C_getValue() {
    return getValue();
  }

}

aspect C_Declarations {

  public void Decl.C_emitDecoderDeclaration(C_env env) {
  }

  public void SampleDecl.C_emitDecoderDeclaration(C_env env) {
    env.println("int labcomm2014_decoder_register_" + 
		env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_decoder *d,");
    env.println("void (*handler)(");
    env.indent();
    env.println(env.prefix + getName() + " *v,");
    env.println("void *context");
    env.unindent();
    env.println("),");
    env.println("void *context");
    env.unindent();
    env.println(");");

    env.println("int labcomm2014_decoder_ioctl_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_decoder *d,");
    env.println("int ioctl_action,");
    env.println("...");
    env.unindent();
    env.println(");");
  }
  
  public void Decl.C_emitEncoderDeclaration(C_env env) {
  }

  public void SampleDecl.C_emitEncoderDeclaration(C_env env) {
    env.println("int labcomm2014_encoder_register_" + 
		env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_encoder *e);");
    env.unindent();

    env.println("int labcomm2014_encode_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_encoder *e");
    if(!isVoid() ) {
        env.println(", "+env.prefix + getName() + " *v");
    }
    env.unindent();
    env.println(");");

    env.println("int labcomm2014_encoder_ioctl_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_encoder *e,");
    env.println("int ioctl_action,");
    env.println("...");
    env.unindent();
    env.println(");");
  }

}

aspect C_Limit {

  public String Exp.C_getLimit(C_env env, int i) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitDecoderLimit(C_env env, int i)" + 
		    " not declared");
  }

  public String IntegerLiteral.C_getLimit(C_env env, int i) {
    return getValue();
  }

  public String VariableSize.C_getLimit(C_env env, int i) {
    return env.qualid + env.memberAccessor() + "n_" + i;
  }
  
}

aspect C_Index {

  public void ArrayType.C_emitCalcIndex(C_env env) {
  }

  public void VariableArrayType.C_emitCalcIndex(C_env env) {
    env.print("int i_" + env.depth + " = ");

    String i_prefix = "i_" + env.depth + "_";
    String expr = i_prefix + "0";
    for (int i = 1 ; i < getNumExp() ; i++) {
      expr = "(" + expr + ") * " +
	getExp(i).C_getLimit(env, i) + " + " + 
	i_prefix + i;
    }
    env.println(expr + ";");
  }

}

aspect C_Decoder {

  public void Decl.C_emitDecoder(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitDecoder(C_env env)" + 
		    " not declared");
  }

  public void TypeDecl.C_emitDecoder(C_env env) {
  }

  public void SampleDecl.C_emitDecoder(C_env env) {
    env = env.nestStruct("v");
    env.println("static void decode_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_reader *r,");
    env.println("void (*handle)(");
    env.indent();
    env.println(env.prefix + getName() + " *v,");
    env.println("void *context");
    env.unindent();
    env.println("),");
    env.println("void *context");
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println(env.prefix + getName() + " v;");
    getType().C_emitDecoder(env);
    env.println("handle(&v, context);");
    if (C_isDynamic()) {
      env.println("{");
      env.indent();
      getType().C_emitDecoderDeallocation(env);
      env.unindent();
      env.println("}");
    }
    env.unindent();
    env.println("}");
  }

  public void Type.C_emitDecoder(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitDecoder(C_env env)" + 
		    " not declared");
  }

  public void VoidType.C_emitDecoder(C_env env) {
  }

  public void PrimType.C_emitDecoder(C_env env) {
    env.print(env.qualid + " = ");
    switch (getToken()) {
      case LABCOMM_SAMPLE: { 
        env.println("labcomm2014_internal_decoder_index_to_signature(" +
                    "r->decoder, labcomm2014_read_int(r));");
      } break;
      default: {
        env.println("labcomm2014_read_" + getName() + "(r);");
      }; break;
    }
  }

  public void UserType.C_emitDecoder(C_env env) {
    lookupType(getName()).getType().C_emitDecoder(env);
  }

  public void StructType.C_emitDecoder(C_env env) {
    for (int i = 0 ; i < getNumField() ; i++) {
      getField(i).C_emitDecoder(env);
    }
  }

  public void ArrayType.C_emitDecoder(C_env env) {
    C_emitDecoderDecodeLimit(env);
    C_emitDecoderArrayAllocate(env);
    env.println("{");
    env.indent();
    C_emitLoopVariables(env);
    for (int i = 0 ; i < getNumExp() ; i++) {
      String iterator = "i_" + env.depth + "_" + i;
      env.println("for (" + iterator + " = 0" +
		  " ; " +
		  iterator + " < " + getExp(i).C_getLimit(env, i) +
		  " ; " +
		  iterator + "++) {");
      env.indent();
    }
    C_emitCalcIndex(env);
    getType().C_emitDecoder(C_Nest(env));
    for (int i = getNumExp() - 1 ; i >= 0 ; i--) {
      env.unindent();
      env.println("}");
    }
    env.unindent();
    env.println("}");
  }

  public void Field.C_emitDecoder(C_env env) {
    getType().C_emitDecoder(env.nestStruct("." + getName()));
  }

  public void Exp.C_emitDecoderDecodeLimit(C_env env, int i) {
  }

  public void VariableSize.C_emitDecoderDecodeLimit(C_env env, int i) {
    env.println(env.qualid + ".n_" + i + " = labcomm2014_read_packed32(r);");
  }

  public void ArrayType.C_emitDecoderDecodeLimit(C_env env) {
    for (int i = 0 ; i < getNumExp() ; i++) {
      getExp(i).C_emitDecoderDecodeLimit(env, i);
    }
  }

  public void ArrayType.C_emitDecoderArrayAllocate(C_env env) {
  }

  public void VariableArrayType.C_emitDecoderArrayAllocate(C_env env) {
    env.print(env.qualid + 
              ".a = labcomm2014_memory_alloc(r->memory, 1, sizeof(" + 
	      env.qualid + ".a[0])");
    for (int i = 0 ; i < getNumExp() ; i++) {
      env.print(" * " + getExp(i).C_getLimit(env, i));
    }
    env.println(");");
  }

  // Code for deallocation of dynamically allocated data 

  public void Type.C_emitDecoderDeallocation(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitDecoderDeallocation(C_env env)" + 
		    " not declared");
  }

  public void PrimType.C_emitDecoderDeallocation(C_env env) {
    if (C_isDynamic()) {
      env.println("labcomm2014_memory_free(r->memory, 1, " + 
                  env.qualid + ");");
    }
  }

  public void UserType.C_emitDecoderDeallocation(C_env env) {
    if (C_isDynamic()) {
      lookupType(getName()).getType().C_emitDecoderDeallocation(env);
    }
  }

  public void StructType.C_emitDecoderDeallocation(C_env env) {
    if (C_isDynamic()) {
      for (int i = 0 ; i < getNumField() ; i++) {
	getField(i).C_emitDecoderDeallocation(env);
      }
    }
  }

  public void ArrayType.C_emitDecoderDeallocation(C_env env) {
    if (getType().C_isDynamic()) {
      env.println("{");
      env.indent();
      C_emitLoopVariables(env);
      for (int i = 0 ; i < getNumExp() ; i++) {
	String iterator = "i_" + env.depth + "_" + i;
	env.println("for (" + iterator + " = 0" +
		    " ; " +
		    iterator + " < " + getExp(i).C_getLimit(env, i) +
		    " ; " +
		    iterator + "++) {");
	env.indent();
      }
      C_emitCalcIndex(env);
      getType().C_emitDecoderDeallocation(C_Nest(env));
      for (int i = 0 ; i < getNumExp() ; i++) {
	env.unindent();
	env.println("}");
      }
      env.unindent();
      env.println("}");
    }
  }

  public void VariableArrayType.C_emitDecoderDeallocation(C_env env) {
    super.C_emitDecoderDeallocation(env);
    env.println("labcomm2014_memory_free(r->memory, 1, " + 
                env.qualid + ".a);");
  }

  public void Field.C_emitDecoderDeallocation(C_env env) {
    getType().C_emitDecoderDeallocation(env.nestStruct("." + getName()));
  }

  public void Decl.C_emitDecoderRegisterHandler(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitDecoderRegisterHandler(C_env env)" + 
		    " not declared");
  }

  public void TypeDecl.C_emitDecoderRegisterHandler(C_env env) {
  }

  public void SampleDecl.C_emitDecoderRegisterHandler(C_env env) {
    env.println("int labcomm2014_decoder_register_" + 
		env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_decoder *d,");
    env.println("void (*handler)(");
    env.indent();
    env.println(env.prefix + getName() + " *v,");
    env.println("void *context");
    env.unindent();
    env.println("),");
    env.println("void *context");
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println("return labcomm2014_internal_decoder_register(");
    env.indent();
    env.println("d,");
    env.println("&signature_" + env.prefix + getName() + ",");
    env.println("(labcomm2014_decoder_function)decode_" + env.prefix + getName() + ",");
    env.println("(labcomm2014_handler_function)handler,");
    env.println("context");
    env.unindent();
    env.println(");");
    env.unindent();
    env.println("}");
  }

}

aspect C_copy {

  private void SampleDecl.C_emitCopyFunctionParam(C_env env, String src,
						  String dst)
  {
    env.println("void labcomm2014_copy_" +
                env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_memory *mem,");
    env.println(env.prefix + getName() + " *" + dst + ",");
    env.println(env.prefix + getName() + " *" + src);
    env.unindent();
    env.print(")");
  }

  public void Decl.C_emitCopyDeclaration(C_env env) {
  }

  public void SampleDecl.C_emitCopyDeclaration(C_env env) {
    C_emitCopyFunctionParam(env, "src", "dst");
    env.println(";");
  }

  public void Decl.C_emitCopy(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitCopy(C_env env)" + 
		    " not declared");
  }

  public void TypeDecl.C_emitCopy(C_env env) {
  }

  public void SampleDecl.C_emitCopy(C_env env) {
    final String dst = "dst";
    final String src = "src";
    C_env env_src = env.nestStruct(src).setPointer();
    C_env env_dst = env.nestStruct(dst).setPointer();

    C_emitCopyFunctionParam(env_src, src, dst);
    env_src.println("");
    env_src.println("{");
    env_src.indent();
    getType().C_emitCopy(env_src, env_dst);
    env_src.unindent();
    env_src.println("}");
  }

  public void Type.C_emitCopy(C_env env_src, C_env env_dst) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitCopy(C_env env)" + 
		    " not declared");
  }

  public void VoidType.C_emitCopy(C_env env_src, C_env env_dst) {
  }

  public void PrimType.C_emitCopy(C_env env_src, C_env env_dst) {
    if (C_isDynamic()) {
      env_src.println(String.format(
	  "%s%s = labcomm2014_memory_alloc(mem, 1, strlen(%s%s)+1);",
	  env_dst.accessor(), env_dst.qualid,
	  env_src.accessor(), env_src.qualid));
      env_src.println(String.format(
	  "memcpy(%s%s, %s%s, strlen(%s%s)+1);",
	  env_dst.accessor(), env_dst.qualid,
	  env_src.accessor(), env_src.qualid,
	  env_src.accessor(), env_src.qualid));
    } else {
      env_src.println(env_dst.accessor() + env_dst.qualid + " = " +
		      env_src.accessor() + env_src.qualid + ";");
    }
  }

  public void UserType.C_emitCopy(C_env env_src, C_env env_dst) {
    lookupType(getName()).getType().C_emitCopy(env_src, env_dst);
  }

  public void StructType.C_emitCopy(C_env env_src, C_env env_dst) {
    for (int i = 0 ; i < getNumField() ; i++) {
      getField(i).C_emitCopy(env_src, env_dst);
    }
  }

  public void ArrayType.C_emitCopy(C_env env_src, C_env env_dst) {
    C_emitCopyDecodeLimit(env_src, env_dst);
    C_emitCopyArrayAllocate(env_src, env_dst);
    env_src.println("{");
    env_src.indent();
    C_emitLoopVariables(env_src);
    for (int i = 0 ; i < getNumExp() ; i++) {
      String iterator = "i_" + env_src.depth + "_" + i;
      env_src.println("for (" + iterator + " = 0" +
		  " ; " +
		  iterator + " < " + getExp(i).C_getLimit(env_src, i) +
		  " ; " +
		  iterator + "++) {");
      env_src.indent();
    }
    C_emitCalcIndex(env_src);
    getType().C_emitCopy(C_Nest(env_src), C_Nest(env_dst));
    for (int i = getNumExp() - 1 ; i >= 0 ; i--) {
      env_src.unindent();
      env_src.println("}");
    }
    env_src.unindent();
    env_src.println("}");
  }

  public void Field.C_emitCopy(C_env env_src, C_env env_dst) {
    String fnam = env_src.memberAccessor() + getName();
    getType().C_emitCopy(env_src.nestStruct(fnam), env_dst.nestStruct(fnam));
  }

  public void Exp.C_emitCopyDecodeLimit(C_env env_src, C_env env_dst, int i) {
    // Ordinary array has no length-member.
  }

  public void VariableSize.C_emitCopyDecodeLimit(C_env env_src, C_env env_dst, int i) {
    String src = env_src.qualid + env_src.memberAccessor() + "n_" + i;
    String dst = env_dst.qualid + env_dst.memberAccessor() + "n_" + i;
    env_src.println(dst + " = " + src + ";");
  }

  public void ArrayType.C_emitCopyDecodeLimit(C_env env_src, C_env env_dst) {
    for (int i = 0 ; i < getNumExp() ; i++) {
      getExp(i).C_emitCopyDecodeLimit(env_src, env_dst, i);
    }
  }

  public void ArrayType.C_emitCopyArrayAllocate(C_env env_src, C_env env_dst) {
  }

  public void VariableArrayType.C_emitCopyArrayAllocate(C_env env_src,
							C_env env_dst)
  {
    env_src.print(env_dst.qualid + env_dst.memberAccessor() +
                  "a = labcomm2014_memory_alloc(mem, 1, sizeof(" +
		  env_src.qualid + env_src.memberAccessor() + "a[0])");
    for (int i = 0 ; i < getNumExp() ; i++) {
      env_src.print(" * " + getExp(i).C_getLimit(env_src, i));
    }
    env_dst.println(");");
  }

  // Code for deallocation of dynamically allocated data in a copy.

  private void SampleDecl.C_emitCopyDeallocationFunctionParam(C_env env,
							      String par)
  {
    env.println("void labcomm2014_copy_free_" +
                env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_memory *mem,");
    env.println(env.prefix + getName() + " *" + par);
    env.unindent();
    env.print(")");
  }

  public void Decl.C_emitCopyDeallocationDeclaration(C_env env) {
  }

  public void SampleDecl.C_emitCopyDeallocationDeclaration(C_env env) {
    C_emitCopyDeallocationFunctionParam(env, "c");
    env.println(";");
  }

  public void Decl.C_emitCopyDeallocation(C_env env) {
    throw new Error(this.getClass().getName() +
		    ".C_emitCopy(C_env env)" +
		    " not declared");
  }

  public void TypeDecl.C_emitCopyDeallocation(C_env env) {
  }

  public void SampleDecl.C_emitCopyDeallocation(C_env env) {
    String par = "par";
    env = env.nestStruct(par).setPointer();

    C_emitCopyDeallocationFunctionParam(env, par);
    env.println("");
    env.println("{");
    env.indent();
    getType().C_emitCopyDeallocation(env);
    env.unindent();
    env.println("}");
  }

  public void Type.C_emitCopyDeallocation(C_env env) {
    throw new Error(this.getClass().getName() +
  		    ".C_emitCopyDeallocation(C_env env)" +
  		    " not declared");
  }

  public void VoidType.C_emitCopyDeallocation(C_env env) {
  }

  public void PrimType.C_emitCopyDeallocation(C_env env) {
    if (C_isDynamic()) {
      env.println("labcomm2014_memory_free(mem, 1, " +
                  env.accessor() + env.qualid + ");");
    }
  }

  public void UserType.C_emitCopyDeallocation(C_env env) {
    if (C_isDynamic()) {
      lookupType(getName()).getType().C_emitCopyDeallocation(env);
    }
  }

  public void StructType.C_emitCopyDeallocation(C_env env) {
    if (C_isDynamic()) {
      for (int i = 0 ; i < getNumField() ; i++) {
  	getField(i).C_emitCopyDeallocation(env);
      }
    }
  }

  public void ArrayType.C_emitCopyDeallocation(C_env env) {
    if (getType().C_isDynamic()) {
      env.println("{");
      env.indent();
      C_emitLoopVariables(env);
      for (int i = 0 ; i < getNumExp() ; i++) {
  	String iterator = "i_" + env.depth + "_" + i;
  	env.println("for (" + iterator + " = 0" +
  		    " ; " +
  		    iterator + " < " + getExp(i).C_getLimit(env, i) +
  		    " ; " +
  		    iterator + "++) {");
  	env.indent();
      }
      C_emitCalcIndex(env);
      getType().C_emitCopyDeallocation(C_Nest(env));
      for (int i = 0 ; i < getNumExp() ; i++) {
  	env.unindent();
  	env.println("}");
      }
      env.unindent();
      env.println("}");
    }
  }

  public void VariableArrayType.C_emitCopyDeallocation(C_env env) {
    super.C_emitCopyDeallocation(env);
    env.println("labcomm2014_memory_free(mem, 1, " +
                env.qualid + env.memberAccessor() + "a);");
  }

  public void Field.C_emitCopyDeallocation(C_env env) {
    getType().C_emitCopyDeallocation(env.nestStruct(env.memberAccessor()
						    + getName()));
  }
}

aspect C_DecoderIoctl {

  public void Decl.C_emitDecoderIoctl(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitDecoderIoctl(C_env env)" + 
		    " not declared");
  }

  public void TypeDecl.C_emitDecoderIoctl(C_env env) {
  }

  public void SampleDecl.C_emitDecoderIoctl(C_env env) {
    env.println("int labcomm2014_decoder_ioctl_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_decoder *d,");
    env.println("int ioctl_action,");
    env.println("...");
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println("int result;");
    env.println("va_list va;");
    env.println("va_start(va, ioctl_action);");
    env.println("result = labcomm2014_internal_decoder_ioctl(");
    env.indent();
    env.println("d, &signature_" + env.prefix + getName() + ", ");
    env.println("ioctl_action, va);");
    env.unindent();
    env.println("va_end(va);");
    env.println("return result;");
    env.unindent();
    env.println("}");
  }
}


aspect C_Encoder {

  public void Decl.C_emitEncoder(C_env env) {
    throw new Error(this.getClass().getName() + 
        ".C_emitEncoder()" + 
        " not declared");
  }

  public void TypeDecl.C_emitEncoder(C_env env) {
    // do nothing for type decls
  }

  public void SampleDecl.C_emitEncoder(C_env env) {
    env = env.nestStruct("(*v)");
    env.println("static int encode_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_writer *w");
    if(!isVoid() ) {
        env.println(", "+env.prefix + getName() + " *v");
    }
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println("int result = 0;");
    getType().C_emitEncoder(env);
    env.println("return result;");
    env.unindent();
    env.println("}");

    // Typesafe encode wrapper
    env.println("int labcomm2014_encode_" + env.prefix + getName() + "(");
    env.println("struct labcomm2014_encoder *e");
    if(!isVoid() ) {
        env.println(", "+env.prefix + getName() + " *v");
    }
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println("return labcomm2014_internal_encode(e, &signature_" + 
		env.prefix + getName() + 
		", (labcomm2014_encoder_function)encode_" + 
                env.prefix + getName() +
		(!isVoid()?", v":", NULL")+");");
    env.unindent();
    env.println("}");
  }

  public void Type.C_emitEncoder(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitEncoder(C_env env)" + 
		    " not declared");
  }

  public void VoidType.C_emitEncoder(C_env env) {
    env.println("result = 0;");
  }

  public void PrimType.C_emitEncoder(C_env env) {
    env.print("result = ");
    switch (getToken()) {
      case LABCOMM_SAMPLE: { 
        env.println("labcomm2014_write_int(w, " + 
                    "labcomm2014_internal_encoder_signature_to_index(w->encoder, " +
                    env.qualid + "));");
      } break;
      default: {
        env.println("labcomm2014_write_" + getName() + 
                    "(w, " + env.qualid + ");");
      } break;
    }
    env.println("if (result != 0) { return result; }");
  }

  public void UserType.C_emitEncoder(C_env env) {
    decl().getType().C_emitEncoder(env);
  }

  public void StructType.C_emitEncoder(C_env env) {
    for (int i = 0 ; i < getNumField() ; i++) {
      getField(i).C_emitEncoder(env);
    }
  }

  public void ArrayType.C_emitEncoder(C_env env) {
    C_emitEncoderEncodeLimit(env);
    env.println("{");
    env.indent();
    C_emitLoopVariables(env);
    for (int i = 0 ; i < getNumExp() ; i++) {
      String iterator = "i_" + env.depth + "_" + i;
      env.println("for (" + iterator + " = 0" +
		  " ; " +
		  iterator + " < " + getExp(i).C_getLimit(env, i) +
		  " ; " +
		  iterator + "++) {");
      env.indent();
    }
    C_emitCalcIndex(env);
    getType().C_emitEncoder(C_Nest(env));
    for (int i = getNumExp() - 1 ; i >= 0 ; i--) {
      env.unindent();
      env.println("}");
    }
    env.unindent();
    env.println("}");
  }

  public void Field.C_emitEncoder(C_env env) {
    getType().C_emitEncoder(env.nestStruct("." + getName()));
  }

  public void Exp.C_emitEncoderEncodeLimit(C_env env, int i) {
  }

  public void VariableSize.C_emitEncoderEncodeLimit(C_env env, int i) {
    env.println("labcomm2014_write_packed32(w, " + env.qualid + ".n_" + i + ");");
  }

  public void ArrayType.C_emitEncoderEncodeLimit(C_env env) {
    for (int i = 0 ; i < getNumExp() ; i++) {
      getExp(i).C_emitEncoderEncodeLimit(env, i);
    }
  }

  public void Decl.C_emitEncoderRegisterHandler(C_env env) {
    throw new Error(this.getClass().getName() + 
  		    ".C_emitEncoderRegisterHandler(C_env env)" + 
  		    " not declared");
  }
  
  protected void Decl.C_emitEncoderTypeRegister(C_env env) {
    env.println("int labcomm2014_encoder_type_register_" + 
		env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_encoder *e");
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println("//TODO: add error handling for dependencies");

    C_emitUserTypeDeps(env, null, true); 

    if(!isSampleDecl() || hasDependencies()) {
      env.println("return labcomm2014_internal_encoder_type_register(");
      env.indent();
      env.println("e,");
      env.println("&signature_" + env.prefix + getName() + "");
      env.unindent();
      env.println(");");
    } else {
      env.println("// the type has no dependencies, do nothing");
      env.println("return 0;");
    }
    env.unindent();
    env.println("}");
  }
  public void TypeDecl.C_emitEncoderRegisterHandler(C_env env) {
    C_emitEncoderTypeRegister(env);
  }
  
  public void SampleDecl.C_emitEncoderRegisterHandler(C_env env) {
    C_emitEncoderTypeRegister(env);
    env.println("int labcomm2014_encoder_register_" + 
		env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_encoder *e");
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    C_emitUserTypeDeps(env, null, false); //XXX HERE BE DRAGONS
                                          //currently set to false to turn off
                                          //outputting of code
    env.println("int result = labcomm2014_internal_encoder_register(");
    env.indent();
    env.println("e,");
    env.println("&signature_" + env.prefix + getName() + ",");
    env.println("(labcomm2014_encoder_function)encode_" + env.prefix + getName());
    env.unindent();
    env.println(");");
    env.println("if(result >= 0) {\n");
    env.indent();
    env.println("labcomm2014_encoder_type_register_" + env.prefix + getName()+"(e);");
    env.println("labcomm2014_internal_encoder_type_bind(");
    env.indent();
    env.println("e,");
    env.println("&signature_" + env.prefix + getName() + ",");
    env.println( (hasDependencies() ? "1" : "0")  + ");");
    env.unindent();
    env.println("}");
    env.unindent();
    env.println("return result;");
    env.unindent();
    env.println("}");
  }
}

aspect C_EncoderIoctl {

  public void Decl.C_emitEncoderIoctl(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitEncoderIoctl()" + 
		    " not declared");
  }

  public void TypeDecl.C_emitEncoderIoctl(C_env env) {
  }

  public void SampleDecl.C_emitEncoderIoctl(C_env env) {
    env.println("int labcomm2014_encoder_ioctl_" + env.prefix + getName() + "(");
    env.indent();
    env.println("struct labcomm2014_encoder *e,");
    env.println("int ioctl_action,");
    env.println("...");
    env.unindent();
    env.println(")");
    env.println("{");
    env.indent();
    env.println("int result;");
    env.println("va_list va;");
    env.println("va_start(va, ioctl_action);");
    env.println("result = labcomm2014_internal_encoder_ioctl(");
    env.indent();
    env.println("e, &signature_" + env.prefix + getName() + ", ");
    env.println("ioctl_action, va);");
    env.unindent();
    env.println("va_end(va);");
    env.println("return result;");
    env.unindent();
    env.println("}");
  }

}

aspect C_TypeDependencies {
  public void Decl.C_emitUserTypeDeps(C_env env, String via, boolean outputCode) {
    if( hasDependencies() ) {
        Iterator<Decl> it = type_dependencies().iterator();
        while(it.hasNext()) {
            Decl t = it.next();

            t.C_emitUserTypeDeps(env, t.getName(), outputCode);
            if(outputCode) {
               env.println("labcomm2014_encoder_type_register_"+env.prefix + t.getName()+"(e);");
            } else {  // Just output a comment
	        String refpath = (via == null) ? "directly" : "indirectly via "+via;
	       env.println(" //Depends ("+refpath+") on "+t.getName() );
            }
        }
    } 
  }

  public void Decl.C_emitUserTypeRefs(C_env env, String via, boolean outputCode) {
    if( isReferenced() ) {
        Iterator<Decl> it = type_references().iterator();
        while(it.hasNext()) {
            Decl t = it.next();

            t.C_emitUserTypeRefs(env, t.getName(), outputCode);
            if(outputCode) {
               env.println("labcomm2014_encoder_type_register_"+env.prefix + t.getName()+"(e);");
            } else {  // Just output a comment
	        String refpath = (via == null) ? "directly" : "indirectly via "+via;
	       env.println(" //Is referenced ("+refpath+")  by "+t.getName() );
            }
        }
    } 
  }
}

aspect C_Signature {

  public void ASTNode.C_emitSignature(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitSignature(C_env env)" + 
		    " not declared");
  }

  syn String Decl.C_DeclTypeString();
  eq SampleDecl.C_DeclTypeString() = "LABCOMM_SAMPLE";
  eq TypeDecl.C_DeclTypeString() = "LABCOMM_TYPEDEF";

  public void Decl.C_emitSignature(C_env env) {
    if( (true || isReferenced() || isSampleDecl())){
      Signature signature = getSignature();
      signature.C_emitSignature(env, !isSampleDecl());
      C_emitFlatSignature(env);
    } else {
      env.println("// not emitting signature for " + getName() +
                  "referenced=" + isReferenced() +
                  "sampledecl=" + isSampleDecl());
    }
  }

  public void ASTNode.C_emitFlatSignature(C_env env) {
    throw new Error(this.getClass().getName() + 
                    ".C_emitFlatSignature(C_env env)" + 
                    " not declared");
  }

  public void TypeDecl.C_emitFlatSignature(C_env env) {
    C_emitSizeofValue(env);
    env.println("static struct labcomm2014_signature " +
                "signature_" + env.prefix + getName() + " = {");
    env.indent();
    env.println("\"" + getName() + "\",");
    //env.println("sizeof_" + env.prefix + getName() + ",");
    //HERE BE DRAGONS? do we need sizeof for typedefs?
    env.println("NULL,");
    env.println("0,");
    env.println("NULL,");
    env.println("0,"); // index
    env.println("sizeof(signature_tree_" + env.prefix + getName() + "),");
    env.println("signature_tree_" + env.prefix + getName() + "");
    env.unindent();
    env.println(" };");
    env.println("const struct labcomm2014_signature " +
                "*labcomm2014_signature_" + env.prefix + getName() + 
                " = &signature_" + env.prefix + getName() + ";");
  }

  public void SampleDecl.C_emitFlatSignature(C_env env){
    env.println("static unsigned char signature_bytes_" + 
  	        env.prefix + getName() + "[] = {");
    SignatureList signature = flatSignature(env.version);
    for (int i = 0 ; i < signature.size() ; i++) {
      String comment = signature.getComment(i);
      if (comment != null) {
        env.println(signature.getIndent(i) + "// " + comment);
      }
      byte[] data = signature.getData(i, env.version);
      if (data != null) {
        env.print(signature.getIndent(i));
        for (int j = 0 ; j < data.length ; j++) {
          env.print(data[j] + ", ");
        }
        env.println("");
      }
    }
    env.println("};");

    C_emitSizeofValue(env);
    env.println("static struct labcomm2014_signature " +
                "signature_" + env.prefix + getName() + " = {");
    env.indent();
    env.println("\"" + getName() + "\",");
    env.println("sizeof_" + env.prefix + getName() + ",");
    env.println("sizeof(signature_bytes_" + env.prefix + getName() + "),");
    env.println("signature_bytes_" + env.prefix + getName() + ",");
    env.println("0,"); // index
    env.println("sizeof(signature_tree_" + env.prefix + getName() + "),");
    env.println("signature_tree_" + env.prefix + getName() + "");
    env.unindent();
    env.println(" };");
    env.println("const struct labcomm2014_signature " +
                "*labcomm2014_signature_" + env.prefix + getName() + 
                " = &signature_" + env.prefix + getName() + ";");
  }

    public void Signature.C_emitSignature(C_env env, boolean decl){
      getSignatureList().C_emitSignature(env, decl);
    }

    public abstract void SignatureLine.C_emitSignature(C_env env, boolean decl);

    public void TypeRefSignatureLine.C_emitSignature(C_env env, boolean isDecl){ 
      env.print(getIndentString());
    //  env.println("LABCOMM_SIGDEF_SIGNATURE(labcomm2014_signature_" + env.prefix + decl.getName() +"),");
      env.println("LABCOMM_SIGDEF_SIGNATURE(signature_" + env.prefix + decl.getName() +"),");
    }

    public void DataSignatureLine.C_emitSignature(C_env env, boolean decl){ 
          String comment = getComment();
          if (comment != null && comment.length() > 0) {
            env.println(getIndentString() + "// " + comment);
          }
          byte[] data = getData(env.version);
          if (data != null && data.length > 0) {
            env.print(getIndentString());
            env.print("LABCOMM_SIGDEF_BYTES("+data.length+", \"");
            for (int j = 0 ; j < data.length ; j++) {
              byte d = data[j];
              //if(d>='a'&&d<='z' || d>='A'&&d<='Z'|| d>='0'&&d<='9'  )
              //  env.print(""+(char)d);
              //else
              env.print("\\x"+Integer.toHexString(d));
            }
            env.println("\"),");
        }
    }
//
//
//      byte[] data = getData(env.version);
//        if (data != null) {
//            for (int j = 0 ; j < data.length ; j++) {
//                env.print(getIndentString());
//                //env.print("printf(\"labcomm2014_write_byte( w, (unsigned char)"+ String.format("0x%02X ", data[j]) +")\\n\"); ");
//                env.print("labcomm2014_write_byte( w, (unsigned char)"+ String.format("0x%02X ", data[j]) +"); ");
//                env.println("if (result != 0) { return result; }");
//            }
//            env.println();
//        }
//
//}
  public void SignatureList.C_emitSignature(C_env env, boolean decl) { 
  env.println("static struct labcomm2014_signature_data signature_tree_" + 
  	 env.prefix + parentDecl().getName() + "[] = {");
  env.indent();
  for (int i = 0 ; i < size() ; i++) {
    SignatureLine l = getSignatureLine(i);
    l.C_emitSignature(env, decl);
  }
  
  env.println("LABCOMM_SIGDEF_END");
  env.println("};");
  env.unindent();
  env.println();
  }



//  public void SampleDecl.C_emitSignature(C_env env) {
//    env.println("static unsigned char signature_bytes_" + 
//		       env.prefix + getName() + "[] = {");
//    SignatureList signature = signature(env.version);
//    for (int i = 0 ; i < signature.size() ; i++) {
//      String comment = signature.getComment(i);
//      if (comment != null) {
//        env.println(signature.getIndent(i) + "// " + comment);
//      }
//      byte[] data = signature.getData(i);
//      if (data != null) {
//        env.print(signature.getIndent(i));
//        for (int j = 0 ; j < data.length ; j++) {
//          env.print(data[j] + ", ");
//        }
//        env.println("");
//      }
//    }
//    env.println("};");
//    env.println("struct labcomm2014_signature labcomm2014_signature_" + 
//		env.prefix + getName() + " = {");
//    env.indent();
//    env.println("LABCOMM_SAMPLE, \"" + getName() + "\",");
//    env.println("(int (*)(struct labcomm2014_signature *, void *))labcomm2014_sizeof_" + 
//		env.prefix + getName() + ",");
//    env.println("sizeof(signature_bytes_" + env.prefix + getName() + "),");
//    env.println("signature_bytes_" + env.prefix + getName() + ",");
//    env.println("0");
//    env.unindent();
//    env.println(" };");
//  }

}
aspect C_Constructor {
  public void ASTNode.C_emitConstructor(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitConstructor(C_env env)" + 
		    " not declared");
  }

  public void Program.C_emitConstructor(C_env env) {
    env.println("LABCOMM_CONSTRUCTOR void init_" +
		env.prefix + "_signatures(void)");
    env.println("{");
    env.indent();
    env.println("static int initialized = 0;");
    env.println("if (initialized == 0) {");
    env.indent();
    env.println("initialized = 1;");
    for (int i = 0; i < getNumDecl(); i++) {
      getDecl(i).C_emitConstructor(env);
    }
    env.unindent();
    env.println("}"); 
    env.unindent();
    env.println("}"); 
  }

  public void TypeDecl.C_emitConstructor(C_env env) {
    if (isReferenced()) {
      env.println("labcomm2014_set_local_index(&signature_" +
                  env.prefix + getName() + ");");
    }
  }

  public void SampleDecl.C_emitConstructor(C_env env) {
    env.println("labcomm2014_set_local_index(&signature_" + 
		env.prefix + getName() + ");");
  }


  public void ASTNode.C_emitConstructorDeclaration(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_emitConstructorDeclaration(C_env env)" + 
		    " not declared");
  }

  public void Program.C_emitConstructorDeclaration(C_env env) {
    env.println("void init_" + env.prefix + "_signatures(void);");
  }

}

aspect C_Sizeof {

  public void Decl.C_emitSizeofDeclaration(C_env env) {
  }

  public void SampleDecl.C_emitSizeofDeclaration(C_env env) {
    env.println("extern int labcomm2014_sizeof_" + env.prefix + getName() +
        "(" + env.prefix + getName() + " *v);");
  }

  public int Decl.C_fixedSizeof() {
    return getType().C_fixedSizeof();
  }

  public void Decl.C_emitSizeof(C_env env) {
  }

  public void SampleDecl.C_emitSizeof(C_env env) {
    env = env.nestStruct("(*v)");
    env.println("int labcomm2014_sizeof_" + env.prefix + getName() +
        "(" + env.prefix + getName() + " *v)");
    env.println("{");
    env.indent();
    env.println("return labcomm2014_internal_sizeof(" +
                "&signature_" + env.prefix + getName() +
                ", v);");
    env.unindent();
    env.println("}");
  }

  public int Type.C_fixedSizeof() {
    throw new Error(this.getClass().getName() + 
            ".C_fixedSizeof()" + 
            " not declared");
  }

  public int VoidType.C_fixedSizeof() {
    return 0;
  }

  public int PrimType.C_fixedSizeof() {
    switch (getToken()) {
      case LABCOMM_BOOLEAN: { return 1; } 
      case LABCOMM_BYTE: { return 1; } 
      case LABCOMM_SHORT: { return 2; } 
      case LABCOMM_INT: { return 4; } 
      case LABCOMM_LONG: { return 8; }
      case LABCOMM_FLOAT: { return 4; }
      case LABCOMM_DOUBLE: { return 8; }
      case LABCOMM_SAMPLE: { return 4; }
      default: { 
    throw new Error(this.getClass().getName() + 
            ".C_fixedSizeof()" + 
            " unknown size (" + getName() + ")"); 
      } 
    }
  }

  public int UserType.C_fixedSizeof() {
    return lookupType(getName()).getType().C_fixedSizeof();
  }

  public int StructType.C_fixedSizeof() {
    int result = 0;
    for (int i = 0 ; i < getNumField() ; i++) {
      result += getField(i).getType().C_fixedSizeof();
    }
    return result;
  }

  public int ArrayType.C_fixedSizeof() {
    int elements = 1;
    for (int i = 0 ; i < getNumExp() ; i++) {
      int n = Integer.parseInt(((IntegerLiteral)getExp(i)).getValue());
      elements = elements * n;
    }
    return getType().C_fixedSizeof() * elements;
  }

  public void Decl.C_emitSizeofValue(C_env env) {
  }

  public void SampleDecl.C_emitSizeofValue(C_env env) {
    env = env.nestStruct("(*v)");
    env.println("static int sizeof_" + env.prefix + getName() + "(void *vv)");
    env.println("{");
    env.indent();
    env.println("int result = 0;");
    if (C_isDynamic()) {
      env.println(env.prefix + getName() + " *v = vv;");
      getType().C_emitSizeof(env);
    } else {
      env.println("result += " + C_fixedSizeof() + ";");
    }    
    env.println("return result;");
    env.unindent();
    env.println("}");
  }

  public void Type.C_emitSizeof(C_env env) {
    throw new Error(this.getClass().getName() + 
            ".C_emitSizeof(C_env env)" + 
            " not declared");
  }

  public void PrimType.C_emitSizeof(C_env env) {
    switch (getToken()) {
      case LABCOMM_STRING: { 
        env.print("{ int l = strlen(" + env.qualid + "); ");
    env.println("result += labcomm2014_size_packed32(l) + l; }"); 
      } break;
      default: { 
    throw new Error(this.getClass().getName() + 
            ".C_emitSizeof(C_env env)" + 
            " known size (" + getName() + ")"); 
      } 
    }
  }

  public void UserType.C_emitSizeof(C_env env) {
    lookupType(getName()).getType().C_emitSizeof(env);
  }

  public void StructType.C_emitSizeof(C_env env) {
    int fixed = 0;
    for (int i = 0 ; i < getNumField() ; i++) {
      if (getField(i).getType().C_isDynamic()) {
        getField(i).getType().C_emitSizeof(
        env.nestStruct("." + getField(i).getName()));
      } else {
        fixed += getField(i).getType().C_fixedSizeof();
      }
    }
    if (fixed > 0) {
      env.println("result += " + fixed + ";");
    }
  }

  public void Exp.C_emitSizeof(C_env env, int i) {
  }

  public void VariableSize.C_emitSizeof(C_env env, int i) {
    env.println("result += labcomm2014_size_packed32(" + 
                env.qualid + env.memberAccessor() + "n_" + i + ");");
  }

  public void ArrayType.C_emitSizeof(C_env env) {
    for (int i = 0 ; i < getNumExp() ; i++) {
      getExp(i).C_emitSizeof(env, i);
    }
    if (getType().C_isDynamic()) {
      env.println("{");
      env.indent();
      C_emitLoopVariables(env);
      for (int i = 0 ; i < getNumExp() ; i++) {
        String iterator = "i_" + env.depth + "_" + i;
        env.println("for (" + iterator + " = 0" +
                    " ; " +
                    iterator + " < " + getExp(i).C_getLimit(env, i) +
                    " ; " +
                    iterator + "++) {");
        env.indent();
      }
      C_emitCalcIndex(env);
      getType().C_emitSizeof(C_Nest(env));
      for (int i = 0 ; i < getNumExp() ; i++) {
        env.unindent();
        env.println("}");
      }
      env.unindent();
      env.println("}");
    } else {
      env.print("result += " + getType().C_fixedSizeof());
      for (int i = 0 ; i < getNumExp() ; i++) {
        env.print(" * " + getExp(i).C_getLimit(env, i));
      }
      env.println(";");      
    }
  }
}

aspect C_forAll {

  public void Program.C_emitForAll(C_env env) {
    env.print("#define LABCOMM_FORALL_SAMPLES_" + env.lcName + 
		"(func, sep)");
    env.indent();
    boolean needSeparator = false;
    for (int i = 0; i < getNumDecl(); i++) {
      String s = getDecl(i).C_forAll(env);
      if (s != null) {
	if (needSeparator) { env.print(" sep"); }
	env.println(" \\");
	env.print(s);
	needSeparator = true;
      }
    }
    env.println("");
    env.unindent();
  }
  
  public String Decl.C_forAll(C_env env) {
    return null;
  }

  public String SampleDecl.C_forAll(C_env env) {
    return "func(" + getName() + ", " + env.prefix + getName() + ")";
  }

}

aspect C_Info {

  public void Program.C_info(PrintStream out, String prefix, int version) {
    C_env env = new C_env("", "", prefix, out, version);
    for (int i = 0; i < getNumDecl(); i++) {
      getDecl(i).C_info(env);
    }
  }

  public void Decl.C_info(C_env env) {
    throw new Error(this.getClass().getName() + 
		    ".C_info((C_env env)" + 
		    " not declared");
  }

  public void TypeDecl.C_info(C_env env) {
    env.println(",C,typedef," + env.prefix + getName() + "," + 
                 env.prefix + getName() + "," +
                 C_info_type_or_void(env.prefix));
  }

  public void SampleDecl.C_info(C_env env) {
    env.println(",C,sample," + env.prefix + getName() + "," + 
                 env.prefix + getName() + "," +
                 C_info_type_or_void(env.prefix));
  }

  // make void types explicitly as they require special treatment
  // in encoder/decoder calls
  protected String Decl.C_info_type_or_void(String prefix) {
    if(isVoid() ) {
      return "void";
    } else {
      return prefix + getName() ;
    }
  }
}