package s3.core.services.memory;

import ovm.core.OVMBase;
import ovm.core.domain.Blueprint;
import ovm.core.domain.ObjectModel;
import ovm.core.domain.Oop;
import ovm.core.execution.Activation;
import ovm.core.execution.Native;
import ovm.core.services.memory.MovingGC;
import ovm.core.services.memory.VM_Address;
import ovm.util.Mem;
import s3.core.domain.S3Blueprint;

/** VM_Interface used with the S3HalfSpaceAllocator, not to be confused with
 *  the MMTk component of similar name.
 **/
public class S3VMInterface extends OVMBase{
  //private OVMDispatcher dispatcher;
  //private Iterator threads;
  //private OVMThread currthread;
  private S3HalfSpaceAllocator allocator;
  public static S3VMInterface instance;
  
  public S3VMInterface(S3HalfSpaceAllocator al){
    allocator = al;
    }
  
  /**
   * Triggers a collection
   */
  public void garbageCollect(){
    allocator.garbageCollect();
  }

  /**
   * Forwards a copy of the object to wherever the allocator puts new objects.
   * Marks the object as forwarded, and fills in the forwarding address
   * @return the address to which the object has been moved
   * FIXME document why return type VM_Address rather than Oop or MovingGC?
   * FIXME is this fixed by public spec? if not could declare object MovingGC
   **/
  public VM_Address moveObject(Oop object, Blueprint parent){
    MovingGC o = (MovingGC)object.asAnyOop();
    if(o.isForwarded()){
      return o.getForwardAddress();
    }
    Blueprint bp = o.getBlueprint();
    int size = bp.getVariableSize(o);
    return moveObject(o, VM_Address.fromObject(allocator.getMem(size)));
  }
  
  /**
   * Moves the object to a given address. The address must point to an allocated
   * area large enough to fit the object
   * @return the address to which the object has been moves. Normally the same as the
   * to parameter, unless the object has already been forwarded
   * FIXME document why return type VM_Address rather than Oop?
   **/
  public VM_Address moveObject(Oop object, VM_Address to){
    MovingGC o = (MovingGC)object.asAnyOop();
    if(o.isForwarded()){
      return o.getForwardAddress();
    }
    int size = o.getBlueprint().getVariableSize(o);
    Mem.the().cpy(to, VM_Address.fromObject(o), size);
    o.markAsForwarded(to);
    return to;
  }
  
  /**
   * Checks to see if an object has been forwarded. Returns true if so.
   * FIXME do we need this cos it's part of JMTk interface?  If so, should we hack
   * some sort of pragma so it inlines straight to the object model op?  Otherwise
   * the indirection sort of squanders the benefit of the object model rewriting....
   */
  public boolean hasObjectMoved(Oop object){
    return ((MovingGC)object.asAnyOop()).isForwarded();
  }
  
  public static void findAllRoots(S3AddressQueue rootLocations,
				  S3AddressQueue objmodObjects,
				  Activation topFrame){
    VM_Address nextref;
    LocalReferenceIterator lrt =  LocalReferenceIterator.factory().make(topFrame);
    while(lrt.hasNext()){
      nextref = lrt.next();
      if(nextref.getAddress().isNonNull()){
	rootLocations.insert(nextref);
      }
    }

    
    SpaceScanner scanner = new SpaceScanner(Native.getImageBaseAddress(),
					    Native.getImageEndAddress());
    
    Native.print_string("Scanning\n");
    S3Blueprint currbp;
    //Field.Iterator fields;
    //Field currfield;
    VM_Address currobj;
    int count = 0;
    int objmod = 0;
    int arr = 0;
    int obj = 0;

    while(scanner.hasNext()){
      currobj = scanner.next();
      MovingGC o = (MovingGC)currobj.asAnyOop();
      for ( int k = ObjectModel.getObjectModel().maxReferences(); k --> 0; ) {
	MovingGC ref = (MovingGC)o.getReference( k).asAnyOop();
	if ( null == ref ) continue;
	if(instance.allocator.inFromSpace(VM_Address.fromObject(o.getReference(k)))){
	  objmodObjects.insert(currobj);
	  objmod++; count++;
	}
      }
      currbp = (S3Blueprint)o.getBlueprint();
      
      if(currbp.isArray()){
	if(((S3Blueprint)currbp.asArray().getComponentBlueprint()).isReference()){
	  for(int i=0; i<currbp.asArray().getLength(o); i++){
	    VM_Address component = (currobj.add(((S3Blueprint.Array)currbp.asArray()).byteOffset(i))).getAddress();
	    if(!component.isNull()){
	      if(instance.allocator.inFromSpace(component)){
		rootLocations.insert(currobj.add(((S3Blueprint.Array)currbp.asArray()).byteOffset(i)));
		arr++; count++;
	      }
	    }
	  }
	}
      }
      else{
	int[] refmap = currbp.getRefMap();
	for(int i=0; i<refmap.length; i++){
	  VM_Address fldaddress = currobj.add(refmap[i]);
	  VM_Address fldcontent = fldaddress.getAddress();
	  if(!(fldcontent.isNull()) && instance.allocator.inFromSpace(fldcontent)){
	    rootLocations.insert(fldaddress);
	    obj++; count++;
	  }
	}
      }
    }
    Native.print_string("Scanning found " + count + " roots. Objmod: " + objmod + " arr: " + arr + " obj: " + obj + "\n");
  }
}
