package ch.zhaw.ads;

import java.util.*;
import java.lang.reflect.*;

public class Storage {
	
  public static StringBuffer log = new StringBuffer();
  private static List<Collectable> root;
  private static List<Collectable> heap;
  private static List<Collectable> finalizeStage;
   
   static {
   		 clear();
   }
   
   public static void clear() {
   		root = new LinkedList<Collectable>();
   		heap = new LinkedList<Collectable>();
   		finalizeStage = new LinkedList<Collectable>();
   }
   
   /* add  root object */
   public static void addRoot(Collectable obj) {
     root.add(obj);
   }
   
   // create a collectable object of class cls
   public static Collectable _new(String cls, Object arg){
     Collectable obj = null;
     try {
       // create an object and call constructor
       Constructor cst = Class.forName("ch.zhaw.ads."+cls).getConstructor(new Class[]{arg.getClass()});
       obj = (Collectable)cst.newInstance(new Object[]{arg});
       // add object to heap
       heap.add(obj);
       log.append("new " + obj.toString()+"\n");  
     }
     catch (Exception ex) {
       log.append("error creating object " + cls+"\n");
     }
     return (Collectable)obj;
   }

   /* remove object from heap */
   public static void delete(Collectable obj) {
     if (heap.remove(obj))
        log.append("delete " + obj.toString()+"\n");
     else
        log.append("error trying to delete not existing object " + obj.toString()+"\n");
   }
 
   /* get all root objects */
   public static Iterable<Collectable> getRoot() {
      return new LinkedList<Collectable>(root);
   }

   /* get heap */
   public static Iterable<Collectable> getHeap() {
      return new LinkedList<Collectable>(heap);
   }
   
   /* get finalzed stage */
   public static Iterable<Collectable> getFinalizeStage() {
      return new LinkedList<Collectable>(finalizeStage);
   }

   /* get references to collectables of an object */
   public static Iterable<Collectable> getRefs (Collectable obj) {
      // get all fields of an object
      Field[] fields = obj.getClass().getFields();
      List<Collectable> fieldList = new LinkedList<Collectable>();
      for (int i = 0; i < fields.length; i++) {
         try {Object o = fields[i].get(obj);   
           if (o instanceof Collectable) fieldList.add((Collectable)o);
         }
         catch (Exception ex) {}
      }
      return fieldList;
   }  

   /* dump an iterator */
   public static void dump(String s,Iterable itr) {
    log.append(s); 
    for (Object o: itr) log.append(" " + o.toString());
    log.append("\n");
   }

   public static String getLog() {return log.toString();}
 
   private static void mark(Collectable obj) {
      if (!obj.isMarked()) {
         obj.setMark(true);
         for (Collectable c : getRefs(obj)) 
            mark(c);
      }
   }
   
   private static void sweep() {
      for (Collectable c : getHeap()) {
          if (!c.isMarked()) {
          	  if (!finalizeStage.contains(c)) {
          	  	finalizeStage.add(c);
          	  	c.finalize();
          	  } else {
          	  	finalizeStage.remove(c);
                delete(c);          	  
          	  }
          }
          else {
              c.setMark(false);
          }
      }
   }

   public static void gc() {
     log.append("collect\n");  
     for (Collectable c : getRoot())
        mark(c);
     sweep();
   }


}