//===-------------------------- Bindings.java -----------------------------===//
//
//                            The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

package org.j3.bindings;

import org.j3.config.Selected;
import org.j3.options.OptionSet;
import org.mmtk.plan.MutatorContext;
import org.mmtk.plan.Plan;
import org.mmtk.plan.TraceLocal;
import org.mmtk.plan.TransitiveClosure;
import org.mmtk.utility.heap.HeapGrowthManager;
import org.mmtk.utility.Constants;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.Collection;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Inline;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;

import org.vmutil.options.AddressOption;
import org.vmutil.options.BooleanOption;
import org.vmutil.options.EnumOption;
import org.vmutil.options.FloatOption;
import org.vmutil.options.IntOption;
import org.vmutil.options.MicrosecondsOption;
import org.vmutil.options.Option;
import org.vmutil.options.PagesOption;
import org.vmutil.options.StringOption;


public final class Bindings {
	  @Inline
	  private static Address vmkitgcmalloc(int size, ObjectReference type) {
	    Selected.Mutator mutator = Selected.Mutator.get();
	    int allocator = mutator.checkAllocator(size, 0, 0);
	    Address res = mutator.alloc(size, 0, 0, allocator, 0);
	    setType(res.toObjectReference(), type);
	    mutator.postAlloc(res.toObjectReference(), type, size, allocator);
	    return res;
	  }
	
	  @Inline
	  private static Address VTgcmalloc(int size, ObjectReference virtualTable) {
	    Selected.Mutator mutator = Selected.Mutator.get();
	    int allocator = mutator.checkAllocator(size, 0, 0);
	    Address res = mutator.alloc(size, 0, 0, allocator, 0);
	    res.store(virtualTable, Offset.zero().plus(hiddenHeaderSize()));
	    mutator.postAlloc(res.toObjectReference(), virtualTable, size, allocator);
	    return res;
	  }

	@Inline
	private static Address prealloc(int size) {
		Selected.Mutator mutator = Selected.Mutator.get();
		int allocator = mutator.checkAllocator(size, 0, 0);
		Address res = mutator.alloc(size, 0, 0, allocator, 0);
		return res;
	}
	
	@Inline
	private static void postalloc(ObjectReference object, ObjectReference type, int size) {
		Selected.Mutator mutator = Selected.Mutator.get();
		int allocator = mutator.checkAllocator(size, 0, 0);
		mutator.postAlloc(object, type, size, allocator);
	}

  @Inline
  private static boolean isLive(TraceLocal closure, ObjectReference obj) {
    return closure.isLive(obj);
  }

  @Inline
  private static void reportDelayedRootEdge(TraceLocal closure, Address obj) {
    closure.reportDelayedRootEdge(obj);
  }

  @Inline
  private static void processRootEdge(TraceLocal closure, Address obj, boolean traced) {
    closure.processRootEdge(obj, traced);
  }

  @Inline
  private static ObjectReference retainForFinalize(TraceLocal closure, ObjectReference obj) {
    return closure.retainForFinalize(obj);
  }

  @Inline
  private static ObjectReference retainReferent(TraceLocal closure, ObjectReference obj) {
    return closure.retainReferent(obj);
  }

  @Inline
  private static ObjectReference getForwardedReference(TraceLocal closure, ObjectReference obj) {
    return closure.getForwardedReference(obj);
  }

  @Inline
  private static ObjectReference getForwardedReferent(TraceLocal closure, ObjectReference obj) {
    return closure.getForwardedReferent(obj);
  }

  @Inline
  private static ObjectReference getForwardedFinalizable(TraceLocal closure, ObjectReference obj) {
    return closure.getForwardedFinalizable(obj);
  }

  @Inline
  private static void processEdge(TransitiveClosure closure, ObjectReference source, Address slot) {
    closure.processEdge(source, slot);
  }

  @Inline
  private static MutatorContext allocateMutator(int id) {
    Selected.Mutator mutator = new Selected.Mutator();
    mutator.initMutator(id);
    return mutator;
  }

  @Inline
  private static void freeMutator(MutatorContext mutator) {
    mutator.deinitMutator();
  }

  @Inline
  private static void boot(Extent minSize, Extent maxSize, String[] arguments) {
    if (arguments != null) {
      OptionSet.parseOptions(arguments);
    }
    HeapGrowthManager.boot(minSize, maxSize);
    Plan plan = Selected.Plan.get();
    plan.boot();
    plan.postBoot();
    plan.fullyBooted();
  }

  @Inline
  private static ObjectReference copy(ObjectReference from,
                              ObjectReference virtualTable,
                              int size,
                              int allocator) {
	int wholeSize = size + hiddenHeaderSize();
    Selected.Collector plan = Selected.Collector.get();
    allocator = plan.copyCheckAllocator(from, wholeSize, 0, allocator);
    Address to = plan.allocCopy(from, wholeSize, 0, 0, allocator);
    memcpy(to, from.toAddress(), wholeSize);
    plan.postCopy(to.toObjectReference(), virtualTable, size, allocator);
    return to.toObjectReference();
  }

  @Inline
  private static native int hiddenHeaderSize();

  @Inline
  private static native void setType(ObjectReference obj, ObjectReference type);
  
  @Inline
  private static native void memcpy(
      Address to, Address from, int size);

  @Inline
  private static native void memcpy(
      ObjectReference to, ObjectReference from, int size);

  
  @Inline
  private static void arrayWriteBarrier(ObjectReference ref, Address slot, ObjectReference value) {
    if (Selected.Constraints.get().needsObjectReferenceWriteBarrier()) {
      Selected.Mutator mutator = Selected.Mutator.get();
      mutator.objectReferenceWrite(ref, slot, value, slot.toWord(), slot.toWord(), Constants.ARRAY_ELEMENT);
    } else {
      slot.store(value);
    }
  }
  
  @Inline
  private static void fieldWriteBarrier(ObjectReference ref, Address slot, ObjectReference value) {
    if (Selected.Constraints.get().needsObjectReferenceWriteBarrier()) {
      Selected.Mutator mutator = Selected.Mutator.get();
      mutator.objectReferenceWrite(ref, slot, value, slot.toWord(), slot.toWord(), Constants.INSTANCE_FIELD);
    } else {
      slot.store(value);
    }
  }
  
  @Inline
  private static void nonHeapWriteBarrier(Address slot, ObjectReference value) {
    if (Selected.Constraints.get().needsObjectReferenceNonHeapWriteBarrier()) {
      Selected.Mutator mutator = Selected.Mutator.get();
      mutator.objectReferenceNonHeapWrite(slot, value, slot.toWord(), slot.toWord());
    } else {
      slot.store(value);
    }
  }
  
  @Inline
  private static boolean writeBarrierCAS(ObjectReference src, Address slot, ObjectReference old, ObjectReference value) {
    if (Selected.Constraints.get().needsObjectReferenceWriteBarrier()) {
      Selected.Mutator mutator = Selected.Mutator.get();
      return mutator.objectReferenceTryCompareAndSwap(src, slot, old, value, slot.toWord(), slot.toWord(), Constants.INSTANCE_FIELD);
    } else {
      return slot.attempt(old, value);
    }
  }

  @Inline
  private static boolean needsWriteBarrier() {
    return Selected.Constraints.get().needsObjectReferenceWriteBarrier();
  }

  @Inline
  private static boolean needsNonHeapWriteBarrier() {
    return Selected.Constraints.get().needsObjectReferenceNonHeapWriteBarrier();
  }

  @Inline
  private static void collect(int why) {
    boolean userTriggered = why == Collection.EXTERNAL_GC_TRIGGER;
    boolean internalPhaseTriggered = why == Collection.INTERNAL_PHASE_GC_TRIGGER;

    if (Options.verbose.getValue() == 1 || Options.verbose.getValue() == 2) {
      if (userTriggered) {
        Log.write("[Forced GC]");
      } else if (internalPhaseTriggered) {
        Log.write("[Phase GC]");
      }
    }

    Plan.setCollectionTriggered();
    Plan.setCollectionTrigger(why);

    long startTime = VM.statistics.nanoTime();
    Selected.Collector.staticCollect();
    long elapsedTime = VM.statistics.nanoTime() - startTime;

    HeapGrowthManager.recordGCTime(((double)elapsedTime) / 1000000);

    if (Selected.Plan.get().lastCollectionFullHeap() && !internalPhaseTriggered) {
      if (Options.variableSizeHeap.getValue() && !userTriggered) {
        // Don't consider changing the heap size if gc was forced by System.gc()
        HeapGrowthManager.considerHeapSize();
      }
      HeapGrowthManager.reset();
    }

    Plan.collectionComplete();
  }
}
