
package org.eclipse.mddi.modelbus.adapter.infrastructure.serialize.modelbus_xmi;



import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.WeakHashMap;

import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EObject;




/**
 * @author P. Sriplakich
 *
 *
 */
public class IdTable {
    
    static Logger logger = Logger.getLogger(IdTable.class);
    
    
    IdGenerator idGenerator = new SimpleIdGenerator();
     
   
    /**
     * The map : model element -> ID.
     * This map is stored with WeakHashMap.
     * Therefore when the model element is no longer referenced in the program,
     * the map entry will be garbage collected.
    */
    private Map obj_id = new WeakHashMap();
    
    /**
     * The map : ID -> WeakReference to the model element.
     * The keys of this map are the values of the 'obj_id' map.
     * When the map entry in obj_id is removed (garbaged collected), 
     * the corresponding entry in id_ref will be also garbage collected 
     * at later time.
     * 
     */
    private Map id_ref = new WeakHashMap();
        

    
    public String getId(EObject o) {
        return (String) obj_id.get(o);
    }
    
    public String getOrCreateId(EObject o) {
          if(!obj_id.containsKey(o)) {
            String id = idGenerator.generateId(o, id_ref.keySet());
            assignId(id, o);
            return id;
          } else {
            return (String) obj_id.get(o);    
          }
    }
    
    public void assignId(String id, EObject o) {
      obj_id.put(o, id);
      id_ref.put(id, new WeakReference(o) );      
    }
    
    /**
     * 
     * create a new ID that do not exist in the ID set,
     * Then associate the object with old ID to the new ID
     * 
     * @param oldID
     * @param idSet
     * @return a new ID
     *
     */
    public String changeId(String oldID, Collection idSet) {
        EObject o = getModelElement(oldID);
        if(o==null) {
            return null;
        }
        String newID = idGenerator.generateId(o, id_ref.keySet(), idSet );
        assignId(newID, o);
        return newID;
    }
    
    
//    
//    public EObject remove(String id) {
//      EObject o = (EObject) id_obj.remove(id);  
//      if(o==null) return null; //id does not exist
//      obj_id.remove(o);
//      return o;
//    }
//    
//    public void removeAllIds(Collection ids) {
//        for(Iterator it = ids.iterator(); it.hasNext(); ) { 
//            String id = (String) it.next();    
//            remove(id);
//        }
//    }
//    
//    public void removeAllElements(Collection elems) {
//      for(Iterator it = elems.iterator(); it.hasNext();) {
//          remove((EObject) it.next());         
//      }    
//    }
//    
//    
//    public boolean remove(EObject o) {
//        String id = (String) obj_id.remove(o);
//        if(id==null) return false; //element does not exist
//        id_obj.remove(id);
//        return true;
//    }
    
    public void addAll(Collection modelElements) {
        for(Iterator it = modelElements.iterator(); it.hasNext(); ) { 
            getOrCreateId((EObject)it.next());
        }        
    }
    
//    public void retainAndAdd(Collection modelElements) {
//        Collection oldElementSet = getModel();
//        for(Iterator it = oldElementSet.iterator(); it.hasNext(); ) { 
//            EObject e = (EObject) it.next();
//            if(!modelElements.contains(e)) {
//              remove(e);    
//            }
//        }
//        addAll(modelElements);
//    }
    
       
    public void clear() {
        id_ref.clear();
        obj_id.clear();
    }
    
//    public boolean containsId(String id) {
//      return id_obj.containsKey(id);
//    }
//    
//    public boolean containsModelElement(EObject o) {
//      return obj_id.containsKey(o);    
//    }
    
    
//    public Collection getCommonIds(IdTable t) {
//        Collection s = getIdSet();
//        s.retainAll(t.id_obj.keySet());
//        return s;
//    }
    
//    public Collection getIdsNotExistIn(IdTable t) {
//        Collection s = getIdSet();
//        s.removeAll(t.id_obj.keySet());
//        return s;
//    }
    
    public EObject getModelElement(String id) {
        Reference r = (Reference) id_ref.get(id);
        if(r==null) return null;
        if(r.get()==null) {
            logger.error(
                    "object " +id +" has been garbage collected");
            return null;
        }
        return (EObject) r.get();
    }
    
    public List getFragmentFromIds(Collection ids) {
        List result = new Vector();
        for(Iterator it = ids.iterator(); it.hasNext();) {
          String id = (String) it.next();  
          EObject e = (EObject) getModelElement(id);
          if(e!=null) {
            result.add(e);    
          }
        }
        return result;
    }
//    
//    public List filterModelElementsInTable(Collection model) {
//        List result = new Vector();
//        for(Iterator it = model.iterator(); it.hasNext(); ) {
//          EObject e = (EObject) it.next();
//          if(containsModelElement(e)) {
//            result.add(e);    
//          }
//        } 
//        return result;
//    }
    
    
//    public IdTable getSubTable(Collection model) {
//        IdTable t = new IdTable();
//        for(Iterator it = model.iterator(); it.hasNext(); ) {
//            EObject e = (EObject) it.next();
//            t.assignId((String)obj_id.get(e) , e);
//        }
//        return t;
//    }
    
    
    /** 
     * 
     */
    public Collection getIdSet() {
       return id_ref.keySet();
    }
    
    public List getIdsFromFragment(Collection fragment) {
       List result = new Vector();
       for(Iterator it = fragment.iterator(); it.hasNext();) {
         EObject e = (EObject) it.next();
         String id = (String) obj_id.get(e);
         if(id!=null) {
           result.add(id);    
         }
       }
       return result;
    }
    
    public Collection getModel() {
       return obj_id.keySet();   
    }
    
}
