package s3.services.bootimage;

import java.io.PrintWriter;

import ovm.core.repository.Selector;
import ovm.core.repository.TypeName;
import ovm.core.domain.ObjectModel;
import ovm.core.domain.Type;
import ovm.core.repository.RepositoryClass;
import ovm.core.repository.RepositoryMember;
import ovm.core.repository.UnboundSelector;
import ovm.core.services.format.CFormat;
import ovm.util.Arrays;
import ovm.util.Comparator;
import s3.core.domain.S3Blueprint;
import s3.core.domain.S3Domain;
import s3.core.domain.S3Field;

public class StructGenerator extends BootBase {

    private PrintWriter file_;
    public StructGenerator(PrintWriter f) {
        file_ = f;
    }

    public void dumpVTBL(S3Blueprint bp) {
        // FIXME get right classnames for members, get overloading right

        Selector.Method[] selectors = bp.getSelectors();
        boolean[] overloaded = JNIFormat.markOverloaded(selectors);
	
        TypeName.Compound thisClass = bp.getName().asCompound();
	StringBuffer buf = new StringBuffer();
	CFormat._.format(thisClass, buf).append("_VTBL {\n");
        buf.append("    struct java_lang_Object _parent_;\n");
	buf.append("    int length;\n");
	for (int i = 0; i < selectors.length; i++) {
            buf.append("    struct ovm_core_domain_Code *");
	    JNIFormat._.format(selectors[i], overloaded[i], buf).append(";\n");
        }
        buf.append("};\n");
	w(buf.toString());
        file_.flush();
    }

    static boolean once = false;

    public void dumpStruct(S3Blueprint bp) {
        if (!once) {
            ObjectModel om = ObjectModel.getObjectModel();
            w(om.toCStruct());
            once = true;
        }
        if (bp instanceof S3Blueprint.Array) {
            dumpStructArray(bp);
        } else {
            dumpStructPlain(bp);
        }
        file_.flush();
    }

    //----------------PRIVATE-----------------------------------

    private static class FieldByOffsetComparator implements Comparator {
	final S3Blueprint bp;
	final S3Domain domain;
	public FieldByOffsetComparator(S3Blueprint bp) {
	    this.bp = bp;
	    this.domain = (S3Domain) bp.getType().getDomain();
	    
	}	    
	private int getOffset(RepositoryMember.Field f) {
	    UnboundSelector.Field sel = f.getUnboundSelector();
	    if ( bp.isSharedState() ) { // FIXME and up pops another special case
                return ((S3Field)domain.getMemberResolver()
                    .resolveStaticField( (Type.Class)bp.getType(), sel, null)) //yuck
                    .getOffset();
	    }
            return ((S3Field)domain.getMemberResolver()
		.resolveInstanceField( (Type.Class)bp.getType(), sel, null))
		.getOffset();
	}
	public int compare(Object lhs, Object rhs) {
	    int lho = getOffset((RepositoryMember.Field) lhs);
	    int rho = getOffset((RepositoryMember.Field) rhs);
	    return lho - rho;
	}
    }

    private void dumpStructPlain(S3Blueprint bp) {
        S3Blueprint parent = bp.getParentBlueprint();
	w(CFormat._.format(bp.getName()));
        w(" {\n");
        if (parent != null) {
            w("  ");
	    w(CFormat._.format(parent.getName()));
            w(" _parent_;\n");
        } else
            w(" struct HEADER _parent_;\n");

        RepositoryClass rc = bp.getType().asScalar().getRepositoryClass();
        RepositoryMember.Field[] fieldsArr = bp.isSharedState()
                                           ? rc.getStaticFields()
                                           : rc.getInstanceFields();
        Arrays.sort(fieldsArr, new FieldByOffsetComparator(bp));
        for (int i = 0; i < fieldsArr.length; i++) {
	    w("  ");
	    w(CFormat._.format(fieldsArr[i].getUnboundSelector()));
	    w(";\n");
        }
        w("};\n");
    }
    
    private void dumpStructArray(S3Blueprint bp) {
        TypeName.Array tname = bp.getName().asArray();
        TypeName compname = tname.getComponentTypeName();
	w(CFormat._.format(tname));
        w(" {\n    struct java_lang_Object _parent_;\n    int length;\n    ");
	w(CFormat._.format(compname));
        if (!compname.isPrimitive())
            w("*");
        w(" values[0];\n};\n");
    }

    private void w(String s) {
        file_.write(s);
    }
} // End of StructGenerator
