package s3.util;

import ovm.core.OVMBase;
import ovm.core.repository.RepositoryClass;
import ovm.core.repository.TypeName;
import ovm.core.domain.Domain;
import ovm.core.domain.DomainDirectory;
import ovm.core.domain.Field;
import ovm.core.domain.LinkageException;
import ovm.core.domain.Oop;
import ovm.core.domain.Type;
import ovm.core.repository.RepositoryMember;
import ovm.core.services.memory.VM_Address;
import ovm.core.services.memory.VM_Word;
import ovm.util.IdentityHashMap;
import ovm.util.Map;
import ovm.util.VM_AddressArray;
import ovm.util.VM_WordArray;
import s3.core.domain.S3ByteCode;
import s3.core.domain.S3Domain;
import s3.core.domain.S3Field;
import s3.services.bootimage.Ephemeral;
import s3.services.bootimage.ImageObserver;
import s3.util.PragmaTransformCallsiteIR.BCnothing;


/** This class, as it stands, is a puny proof-of-concept test bed for the
 *  evolution of the Type/Field/Method metadata in step with the object
 *  graph.  How it is ultimately done will likely bear little resemblance
 *  to this, but this may be enough to handle the existing
 *  cases for the nonce, and maybe even reveal some of the tricky bits.
 *  Populated here manually instead of automatically, for now.
 **/
public class EphemeralBureau implements Ephemeral.Void /* in more ways than one */ {
    
    private static Map map = new IdentityHashMap();

    static {
        S3Domain exd = (S3Domain)DomainDirectory.getExecutiveDomain();

        registerForExecutive( VM_Address.typeNameVMA,
        new EphemeralBureaucrat( exd.INT) {
            public Field fieldFor( RepositoryMember.Field rmf,
                                   Type.Compound declarer) {
                // a Field.Integer that says INT if you ask its type, but
                // still returns a descriptor matching name:LVM_Address;.
                // Well, that's still tacky, but it may be sufficient for
                // present purposes.
                return new S3Field.Integer( rmf, declarer) {
                    public void setUnrefined( Oop dst, Object value) {
                        VM_Address a = (VM_Address)value;
                        set( dst, (a != null ? a.asInt() : 0));
                    }
                    // comment out to return type from descriptor:
                    public Type getType() { return type; }
                };
            }
        });

        registerForExecutive( VM_Word.typeNameVMW,
        new EphemeralBureaucrat( exd.INT) {
            public Field fieldFor( RepositoryMember.Field rmf,
                                   Type.Compound declarer) {
                // a Field.Integer that says INT if you ask its type, but
                // still returns a descriptor matching name:LVM_Word;.
                // Well, that's still tacky, but it may be sufficient for
                // present purposes.
                return new S3Field.Integer( rmf, declarer) {
                    public void setUnrefined( Oop dst, Object value) {
                        VM_Word w = (VM_Word)value;
                        set( dst, w.asInt());
                    }
                    // comment out to return type from descriptor:
                    public Type getType() { return type; }
                };
            }
        });

        Type.Array arr_int = exd.makeType( exd.INT, 1);

        registerForExecutive( VM_WordArray.TYPENAME,
        new EphemeralBureaucrat( arr_int) {
            public Field fieldFor( RepositoryMember.Field rmf,
                                   Type.Compound declarer) {
                return new S3Field.Reference( rmf, declarer) {
                    public void setUnrefined( Oop dst, Object value) {
                        set( dst, null == value
                                ? null :  ((VM_WordArray)value).target());
                    }
                    public Type getType() { return type; }
                };
            }
        });

        registerForExecutive( VM_AddressArray.TYPENAME,
        new EphemeralBureaucrat( arr_int) {
            public Field fieldFor( RepositoryMember.Field rmf,
                                   Type.Compound declarer) {
                return new S3Field.Reference( rmf, declarer) {
                    public void setUnrefined( Oop dst, Object value) {
                        set( dst, null == value
                                ? null :  ((VM_AddressArray)value).target());
                    }
                    public Type getType() { return type; }
                };
            }
        });
        
        hackS3ByteCode();
    }
    
    static void hackS3ByteCode() {
        if ( ! ImageObserver._.isJ2c() ) // FIXME courtesy of cheap hack dept.
            return;
        Type.Scalar dt = VM_Address.fromObject( S3ByteCode.DEAD_CODE)
                         .asOop().getBlueprint().getType().asScalar();
        RepositoryClass rcdt = dt.getRepositoryClass();
        
        RepositoryMember.Field rmf = rcdt.getField( "bytes:[B");
        register( new NonwrittenReferenceField( rmf, dt));
        
        rmf = rcdt.getField(
            "exceptions_:[Lovm/core/repository/ExceptionHandler;");
        register( new NonwrittenReferenceField( rmf, dt));
        
        rmf = rcdt.getField(
             "attributes_:[Lovm/core/repository/Attribute;");
        register( new NonwrittenReferenceField( rmf, dt));
    }
    
    static class NonwrittenReferenceField extends S3Field.Reference {
        NonwrittenReferenceField( RepositoryMember.Field rmf,
                                  Type.Compound dt) {
            super( rmf, dt);
        }
        public void setUnrefined( Oop dst, Object value) {
            set( dst, null);
        }
    }
    
    static void register( Type t, EphemeralBureaucrat f)
    throws PragmaTransformCallsiteIR.BCdead {
        map.put( t, f);
    }
    
    static void register( Field f) {
        Type fdt = f.getDeclaringType();
        RepositoryClass rcdt = fdt.asScalar().getRepositoryClass();
        RepositoryMember.Field rmf =
            rcdt.getField( f.getSelector().getUnboundSelector());
        Field[] matches = (Field[])map.get( rmf);
        if ( null == matches ) {
            map.put( rmf, new Field[] { f });
            return;
        }
        Field[] grown = new Field [ 1 + matches.length ];
        int i;
        for ( i = 0; i < matches.length; ++ i ) {
            Field fi = matches [ i ];
            OVMBase.OVMassert( fi.getDeclaringType() != fdt,
                       "EphemeralBureau collision");
            grown [ i ] = fi;
        }
        grown [ i ] = f;
    }
    
    static void registerForExecutive( TypeName.Compound t, EphemeralBureaucrat f)
    throws PragmaTransformCallsiteIR.BCdead {
        Domain ed = DomainDirectory.getExecutiveDomain();
        Type.Context ctx = ed.getSystemTypeContext();
        register( ctx.typeForKnown( t), f);
    }
    
    public static Field fieldFor( RepositoryMember.Field rmf,
                                  Type.Compound declarer)
    throws PragmaTransformCallsiteIR.ReturnNull {
        // First check for a specific replacement for this field of this
        // declaring type.  If map.get(rmf) is nonnull, it is an array of
        // Field; return the one, if any, with declarer as its declarer.
        Field[] matches = (Field[])map.get( rmf);
        if ( null != matches ) {
            for ( int i = matches.length; i --> 0 ; ) {
                Field f = matches [ i ];
                if ( declarer == f.getDeclaringType() )
                    return f;
            }
        }
        // Nothing that specific, so look for a replacement that applies to
        // all fields with this field's declared value type. 
        Type.Context ctx = declarer.getContext();
        Type t;
        try {
            t = ctx.typeFor(rmf.getDescriptor().getType());
        } catch (LinkageException e) {
            return null; // if we don't know it, we can't have a replacement
        } catch (LinkageException.Runtime e) {
	    return null;
	}
        EphemeralBureaucrat eb = (EphemeralBureaucrat)map.get( t);
        return null == eb ? null : eb.fieldFor( rmf, declarer);
    }
    
    public static Type typeFor( Type t) throws BCnothing {
        EphemeralBureaucrat eb = (EphemeralBureaucrat)map.get( t);
        return null == eb ? t : eb.type;
    }

    static abstract class EphemeralBureaucrat {
        EphemeralBureaucrat( Type t) { type = t; }
        public abstract
        Field fieldFor( RepositoryMember.Field rmf, Type.Compound declarer);
        public final Type type;
    }
}
