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


import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.mddi.modelbus.adapter.infrastructure.model_manipulation.ModelWrapper;

/**
 * @author P. Sriplakich
 *
 *
 */
public class UpdaterByCopy {
    
	String format = "EMF";
    
    IdTable originalModelIdTable;    
    Collection originalFragment;
    
    IdTable updatedCopyIdTable;
    Collection updatedFragment;
    
    Collection resultFragment = new HashSet();
    
    Collection resultFragmentIds;
    
    Collection addedElementIds;
    Collection removedElementIds;    
    
    public UpdaterByCopy(IdTable _originalModelIdTable
            ,Collection _originalFragment
            ,IdTable _updatedCopyIdTable
            ,Collection _updatedFragment) {
        
        originalModelIdTable = _originalModelIdTable;
        originalFragment = _originalFragment;
        updatedCopyIdTable = _updatedCopyIdTable;
        updatedFragment = _updatedFragment;
        
        setFormat("EMF");
        
    }
    
    public void update() {  
        
        //Collection updatedModel = updatedCopyIdTable.getModel();

        Collection originalModelIds = originalModelIdTable.getIdsFromFragment(originalFragment);
        
        resultFragmentIds = updatedCopyIdTable.getIdsFromFragment(updatedFragment);
        
        addedElementIds  = new HashSet(resultFragmentIds);
        addedElementIds.removeAll( originalModelIds );
        
        removedElementIds = originalModelIdTable.getIdsFromFragment(originalFragment);
        removedElementIds.removeAll(resultFragmentIds);           
        
        Collection commonIds = new HashSet(originalModelIds);
        commonIds.retainAll( resultFragmentIds );
        
        resultFragment.addAll( originalModelIdTable.getFragmentFromIds(commonIds) );
        // create new elements.
        for(Iterator it = addedElementIds.iterator(); it.hasNext();) {
            String id = (String) it.next(); 
            EObject updatedElem = updatedCopyIdTable.getModelElement(id);  
            EClass c = updatedElem.eClass();
            ModelWrapper w = ModelWrapper.getRegistry(format);
            if(w==null) throw new RuntimeException("no factory found " +format);            
            EObject newElem = w.create(c);
            originalModelIdTable.assignId(id, newElem);  
            resultFragment.add(newElem);
        }         

        // update properties of all elements (existing and added elements)
        for(Iterator it = resultFragmentIds.iterator(); it.hasNext();) {
            String id = (String) it.next();
            EObject originalElem = originalModelIdTable.getModelElement(id);
            EObject updatedElem = updatedCopyIdTable.getModelElement(id);
            copyElement(originalElem, updatedElem);   
        }        
        
     
    }
    
    public Collection getRemovedElementIds() {
        return removedElementIds;
    }
    
    public Collection getAddedElements() {        
        return originalModelIdTable.getFragmentFromIds(addedElementIds);
    }
    
    public Collection getAddedElementIds() {
       return addedElementIds;    
    }
    
    public Collection getResultFragment() {
        return resultFragment;
    }    
    
    public Collection getResultFragmentIds() {
        return resultFragmentIds;
    }
    
    void copyElement(EObject originalElem, EObject updatedElem) {
        
        //copy all attributes
        for(Iterator it=originalElem.eClass().getEAllAttributes().iterator(); it.hasNext(); ) {
            EAttribute a = (EAttribute) it.next();
            if(a.isChangeable() && !a.isDerived()) {
                Object oldValue = originalElem.eGet(a);
                Object updatedValue = updatedElem.eGet(a);
                // overwrite old value
                originalElem.eSet(a, updatedValue);
            }
        }
        //copy all references
        for(Iterator it=updatedElem.eClass().getEAllReferences().iterator(); it.hasNext(); ) {
          EReference r = (EReference) it.next();
          if(r.isChangeable() && !r.isDerived()) {
              if(r.isMany()) {
                 List oldLinkList = (List) originalElem.eGet(r);
                 List newLinkList = (List) updatedElem.eGet(r);
                 
                 // calculate internal links
                 List oldInternalLinkList = new Vector(oldLinkList);
                 oldInternalLinkList.retainAll(originalFragment);
                 List newInternalLinkList = new Vector(newLinkList);
                 newInternalLinkList.retainAll(updatedFragment);
                     
                 List oldInternalLinkIds = originalModelIdTable.getIdsFromFragment(oldInternalLinkList);
                 List newInternalLinkIds = updatedCopyIdTable.getIdsFromFragment(newInternalLinkList);

                 // detect links to remove
                 List idsToRemove = new Vector(oldInternalLinkIds);
                 idsToRemove.removeAll(newInternalLinkIds);
                 
                 // Remove links
                 for(Iterator it2 = idsToRemove.iterator(); it2.hasNext(); ) {
                     String id = (String) it2.next();                    
                     oldLinkList.remove(originalModelIdTable.getModelElement(id));
                 }
                 
                 // detect links to add
                 List idsToAdd = new Vector(newInternalLinkIds);
                 idsToAdd.removeAll(oldInternalLinkIds);
                 
                 // add links
                 for(Iterator it2 = idsToAdd.iterator(); it2.hasNext(); ) {
                     String id = (String) it2.next();                    
                     oldLinkList.add(originalModelIdTable.getModelElement(id));
                 }                 
                 
              } else {
                 EObject oldLink = (EObject) originalElem.eGet(r);
                 EObject newLink = (EObject) updatedElem.eGet(r);
                 
                 if( newLink!=null) {
                   if(updatedFragment.contains(newLink)) {
                     String id = updatedCopyIdTable.getId(newLink);
                     originalElem.eSet(r, originalModelIdTable.getModelElement(id));
                   } 
                 } else if(oldLink!=null) {
                    if(originalFragment.contains(oldLink)) {
                      originalElem.eUnset(r);  
                    }
                 }
              }
          }
        }
    }
    
    
    
    /**
     * @return Returns the format.
     */
    public String getFormat() {
        return format;
    }
    /**
     * @param format The format to set.
     */
    public void setFormat(String format) {
        this.format = format;       
    }
}


