package ovm.core.services.format;

import ovm.core.OVMBase;
import ovm.core.domain.Type;
import ovm.core.repository.TypeCodes;
import ovm.util.HashMap;
import s3.core.domain.S3Blueprint;
import s3.core.domain.S3Domain;
import s3.services.bootimage.JNIFormat;

/**
 * Move the C++ formatting conventions out of s3.services.j2c, so that
 * other code can use the same conventions.  Maybe this means that the
 * domain and blueprint counters should be exposed in the ovm
 * interfaces, but what happens to the stupid domain strings?
 *
 * @author <a href=mailto://baker29@cs.purdue.edu> Jason Baker </a>
 */
public class CxxFormat extends OVMBase {
    static public String format(S3Domain d) {
	int number = d.getUID();
	return number == 0 ? "e" : ("u" + number);
    }

    static private HashMap memoFormat = new HashMap();
    
    static public String format(S3Blueprint bp) {
	String ret = (String) memoFormat.get(bp);
	if (ret == null) {
	    ret = ((bp instanceof S3Blueprint.Primitive)
		   ? format((S3Blueprint.Primitive) bp)
		   : (bp instanceof S3Blueprint.Scalar)
		   ? format((S3Blueprint.Scalar) bp)
		   : format((S3Blueprint.Array) bp));
	    memoFormat.put(bp, ret);
	}
	return ret;
    }

    static private String format(S3Blueprint.Primitive bp) {
	switch(bp.getName().getTypeTag()) {
	case TypeCodes.BYTE:    return "jbyte";
	case TypeCodes.BOOLEAN: return "jboolean";
	case TypeCodes.CHAR:
	case TypeCodes.USHORT:  return "jchar";
	case TypeCodes.SHORT:   return "jshort";
	case TypeCodes.INT:     return "jint";
	case TypeCodes.UINT:    return "unsignedjint";
	case TypeCodes.LONG:    return "jlong";
	case TypeCodes.ULONG:   return "unsignedjlong";
	case TypeCodes.FLOAT:   return "jfloat";
	case TypeCodes.DOUBLE:  return "jdouble";
	case TypeCodes.VOID:    return "void";
	default:
	    throw new Error("unknown prim code:" + bp.getName().getTypeTag());
	}
    }

    static private String format(S3Blueprint.Scalar bp) {
	String ret = null;
	Type.Context ctx = bp.getType().getContext();
	S3Domain d = (S3Domain) ctx.getDomain();
	
	String n = bp.getName().asScalar().toClassInfoString().intern();
	    
// 	    if (d.number != 0 || ctx != d.getSystemTypeContext())
// 		;		// ignore puns
// 	    else if (n == "ovm/core/services/memory/VM_Address")
// 		ret = "void";
// // 	    else if (n == "ovm.core.services.memory.VM_Word")
// // 		ret = "junsignedint";
// 	    else if (n == "ovm/core/domain/Oop")
// 		ret = "OBJECT_HEADER";
	    
	if (ret == null)
	    {
		StringBuffer b = new StringBuffer();
		
		b.append(format(d));
		b.append("_");

		b.append(JNIFormat._.encode(n));

		// Domain may not uniquely identify this class!
		if (ctx != d.getSystemTypeContext()) {
		    b.append("__c");
		    b.append(ctx.getUID());
		}
		if (bp.isSharedState())
		    b.append("_static");
		ret = b.toString();
	    }
	return ret;
    }

    static private String format(S3Blueprint.Array bp) {
	StringBuffer b = new StringBuffer(format(bp.getS3Domain()));
	b.append("_Array<");
	S3Blueprint cmp = (S3Blueprint) bp.getComponentBlueprint();
	b.append(format(cmp));
	b.append(cmp.isReference() ? "*>" : ">");
	return b.toString();
    }

    static private HashMap encodings = new HashMap();
    /**
     * Encode a short identifier according to JNI conventions and
     * C++ rules.
     * 
     * @param id the identifier to encode
     *
     * @return
     *
     */
    static public String encode(String id) {
        String ret = (String) encodings.get(id);
        if (ret == null) {
	   ret = "ovm_" + JNIFormat._.encode(id);
           encodings.put(id, ret);
        }
        return ret;
    }
}
