/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
/*
 * Created on 24-Oct-2004
 *
 */
package uk.ac.ic.doc.kevlar.core.framework;

import java.util.HashSet;
import java.util.Set;

import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.ILoader;
import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.ILoadingEventListener;
import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.LoaderProvider;
import uk.ac.ic.doc.kevlar.core.framework.typecheck.ITypeChecker;
import uk.ac.ic.doc.kevlar.core.framework.typecheck.TypeChecker;

/**
 * @author Daniel Burke and Steve Zymler
 *
 */
public class Framework implements IFramework, ILoadingEventListener
{
    private long contextCount = 0;
    
    // LOCK OBTAINING ORDER
    // WARNING: Lock the contexts set before using it
    // DOUBLE WARNING: Lock a context from contexts before you lock the framework
    // if you intend to lock a context.
    private Set<Context> contexts;
    private Set<IFrameworkListener> listeners;
    private IProgramRepository programRep;
    private ITypeChecker typeChecker ;
    
    
    // interface implementation
    
    public IContext createNewContext(IContextListener listener) 
    {
        synchronized(contexts)
	    {
	        Context newContext = new Context(contextCount, this, listener);
	        contextCount++;
	        
	        contexts.add(newContext);
	        return newContext;
	    }
    }
        
    public synchronized void executeContext(IContext context) 
    {        
        // WARNING: Be careful, if you need to use any field of framework
        // other than contexts, you need to lock the framework too.
        // watch out for deadlock if you do this
        // You must lock a context before doing anything to it
        // You must lock a context before you may lock the framework
     
    	context.execute();	    
    }
    
    public synchronized IProgramRepository getProgramRepository() 
    {
        return programRep;
    }
    
    public void removeContext( IContext context ) 
    {
        synchronized(contexts)
        {
	        if(!contexts.contains(context))
	        {
	            throw new IllegalArgumentException("Not a context of this framework");
	        }
	        
	        synchronized(context)
	        {
	        
		        ((Context)context).destroy();        
		        contexts.remove(context);
	        }
        }
    }        
    
    // constructor
    
    public Framework() 
    {
        contexts = new HashSet<Context>();
        listeners = new HashSet<IFrameworkListener>();
       
        programRep = new ProgramRepository();
        LoaderProvider.getLoader().addEventObserver( this );
        
        programRep.rehashPrograms();
        
        typeChecker = new TypeChecker( LoaderProvider.getLoader()
                									 .getTypeLoader()
                									 .getTypeTree() );                 
    }
    
    // getter
    
    public synchronized ITypeChecker getTypeChecker()
    {
        return typeChecker ;
    }
	
    // update tree and repository and inform the contexts
    
	public synchronized void typeTreeUpdated(ILoader source) 
	{
		// update type tree
		typeChecker.setTypeTree( source.getTypeLoader().getTypeTree() );
		for( ILoadingEventListener listener : contexts )
		{
			listener.typeTreeUpdated( source );
		}						
	}

	public synchronized void availableProgramsUpdated(ILoader source) 
	{
		//rehash program repository
		programRep.rehashPrograms();
		for( ILoadingEventListener listener : contexts )
		{
			listener.availableProgramsUpdated( source );
		}	
		for( IFrameworkListener listener : listeners )
		{
			listener.availableProgramsUpdated();
		}
	}
	
	public synchronized void addFrameworkListener(IFrameworkListener listener) 
	{		
		listeners.add( listener );
	}
}
