content: package com.group_finity.mascot;
  
  import java.util.ArrayList;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import com.group_finity.mascot.config.Configuration;
  import com.group_finity.mascot.exception.BehaviorInstantiationException;
  import com.group_finity.mascot.exception.CantBeAliveException;
  
  /**
   * 
   * Maintains a list of mascot, the object to time.
   * 
   * Original Author: Yuki Yamada of Group Finity (http://www.group-finity.com/Shimeji/)
   * Currently developed by Shimeji-ee Group.
   */
  public class Manager {
  
  	private static final Logger log = Logger.getLogger(Manager.class.getName());
  
  	/**
  	* Interval timer is running.
  	*/
  	public static final int TICK_INTERVAL = 40;
  
  	/**
  	 * A list of mascot.
  	 */
  	private final List<Mascot> mascots = new ArrayList<Mascot>();
  
  	/**
  	* The mascot will be added later.
  	* (@Link ConcurrentModificationException) to prevent the addition of the mascot (@link # tick ()) are each simultaneously reflecting.
  	 */
  	private final Set<Mascot> added = new LinkedHashSet<Mascot>();
  
  	/**
  	* The mascot will be added later.
  	* (@Link ConcurrentModificationException) to prevent the deletion of the mascot (@link # tick ()) are each simultaneously reflecting.
  	 */
  	private final Set<Mascot> removed = new LinkedHashSet<Mascot>();
  
  	private boolean exitOnLastRemoved = true;
  	
  	private Thread thread;
  
  	public void setExitOnLastRemoved(boolean exitOnLastRemoved) {
  		this.exitOnLastRemoved = exitOnLastRemoved;
  	}
  
  	public boolean isExitOnLastRemoved() {
  		return exitOnLastRemoved;
  	}
  
  	public Manager() {
  
  		new Thread() {
  			{
  				this.setDaemon(true);
  				this.start();
  			}
  
  			@Override
  			public void run() {
  				while (true) {
  					try {
  						Thread.sleep(Integer.MAX_VALUE);
  					} catch (final InterruptedException ex) {
  					}
  				}
  			}
  		};
  	}
  	
  	public void start() {
  		if ( thread!=null && thread.isAlive() ) {
  			return;
  		}
  		
  		thread = new Thread() {
  			@Override
  			public void run() {
  
  				long prev = System.nanoTime() / 1000000;
  				try {
  					for (;;) {
  						for (;;) {
  							final long cur = System.nanoTime() / 1000000;
  							if (cur - prev >= TICK_INTERVAL) {
  								if (cur > prev + TICK_INTERVAL * 2) {
  									prev = cur;
  								} else {
  									prev += TICK_INTERVAL;
  								}
  								break;
  							}
  							Thread.sleep(1, 0);
  						}
  
  						tick();
  					}
  				} catch (final InterruptedException e) {
  				}
  			}
  		};
  		thread.setDaemon(false);
  		
  		thread.start();
  	}
  	
  	public void stop() {
  		if ( thread==null || !thread.isAlive() ) {
  			return;
  		}
  		thread.interrupt();
  		try {
  			thread.join();
  		} catch (InterruptedException e) {
  		}
  	}
  
  	private void tick() {
  
  		// Update the first environmental information
  		NativeFactory.getInstance().getEnvironment().tick();
  
  		synchronized (this.getMascots()) {
  
  			// Add the mascot if it should be added
  			for (final Mascot mascot : this.getAdded()) {
  				this.getMascots().add(mascot);
  			}
  			this.getAdded().clear();
  
  			// Remove the mascot if it should be removed
  			for (final Mascot mascot : this.getRemoved()) {
  				this.getMascots().remove(mascot);
  			}
  			this.getRemoved().clear();
  
  			// Advance mascot's time
  			for (final Mascot mascot : this.getMascots()) {
  				mascot.tick();
  			}
  			
  			// Advance mascot's time
  			for (final Mascot mascot : this.getMascots()) {
  				mascot.apply();
  			}
  		}
  
  		if (isExitOnLastRemoved()) {
  			if (this.getMascots().size() == 0) {
  				Main.getInstance().exit();
  			}
  		}
  	}
  
  	public void add(final Mascot mascot) {
  		synchronized (this.getAdded()) {
  			this.getAdded().add(mascot);
  			this.getRemoved().remove(mascot);
  		}
  		mascot.setManager(this);
  	}
  
  	public void remove(final Mascot mascot) {
  		synchronized (this.getAdded()) {
  			this.getAdded().remove(mascot);
  			this.getRemoved().add(mascot);
  		}
  		mascot.setManager(null);
  	}
  
  	public void setBehaviorAll(final String name) {
  		synchronized (this.getMascots()) {
  			for (final Mascot mascot : this.getMascots()) {
  				try {
  					mascot.setBehavior(Main.getInstance().getConfiguration(mascot.getImageSet()).buildBehavior(name));
  				} catch (final BehaviorInstantiationException e) {
  					log.log(Level.SEVERE, "Failed to initialize the following actions", e);
  					Main.showError( Main.getInstance( ).getLanguageBundle( ).getProperty( "FailedSetBehaviourErrorMessage" ) + "\n" + e.getMessage( ) + "\n" + Main.getInstance( ).getLanguageBundle( ).getProperty( "SeeLogForDetails" ) );
  					mascot.dispose();
  				} catch (final CantBeAliveException e) {
  					log.log(Level.SEVERE, "Fatal Error", e);
                                          Main.showError( Main.getInstance( ).getLanguageBundle( ).getProperty( "FailedSetBehaviourErrorMessage" ) + "\n" + e.getMessage( ) + "\n" + Main.getInstance( ).getLanguageBundle( ).getProperty( "SeeLogForDetails" ) );
  					mascot.dispose();
  				}
  			}
  		}
  	}	
  	
  	public void setBehaviorAll(final Configuration configuration, final String name, String imageSet) {
  		synchronized (this.getMascots()) {
  			for (final Mascot mascot : this.getMascots()) {
  				try {
  					if( mascot.getImageSet().equals(imageSet) ) {
  						mascot.setBehavior(configuration.buildBehavior(name));						
  					}
  				} catch (final BehaviorInstantiationException e) {
  					log.log(Level.SEVERE, "Failed to initialize the following actions", e);
  					Main.showError( Main.getInstance( ).getLanguageBundle( ).getProperty( "FailedSetBehaviourErrorMessage" ) + "\n" + e.getMessage( ) + "\n" + Main.getInstance( ).getLanguageBundle( ).getProperty( "SeeLogForDetails" ) );
  					mascot.dispose();
  				} catch (final CantBeAliveException e) {
  					log.log(Level.SEVERE, "Fatal Error", e);
  					Main.showError( Main.getInstance( ).getLanguageBundle( ).getProperty( "FailedSetBehaviourErrorMessage" ) + "\n" + e.getMessage( ) + "\n" + Main.getInstance( ).getLanguageBundle( ).getProperty( "SeeLogForDetails" ) );
  					mascot.dispose();
  				}
  			}
  		}
  	}
  
  	public void remainOne() {
  		synchronized (this.getMascots()) {
  			int totalMascots = this.getMascots().size();
  			for (int i = totalMascots - 1; i > 0; --i) {
  				this.getMascots().get(i).dispose();				
  			}
  		}
  	}	
  	
  	public void remainOne( String imageSet ) {
  		synchronized (this.getMascots()) {
  			int totalMascots = this.getMascots().size();
  			boolean isFirst = true;
  			for (int i = totalMascots - 1; i >= 0; --i) {
  				Mascot m = this.getMascots().get(i);
  				if( m.getImageSet().equals(imageSet) && isFirst) {
  					isFirst = false;
  				} else if( m.getImageSet().equals(imageSet) && !isFirst) {
  					m.dispose();
  				}
  			}
  		}
  	}
  
  	public int getCount() {
  		synchronized (this.getMascots()) {
  			return this.getMascots().size();
  		}
  	}
  
  	private List<Mascot> getMascots() {
  		return this.mascots;
  	}
  
  	private Set<Mascot> getAdded() {
  		return this.added;
  	}
  
  	private Set<Mascot> getRemoved() {
  		return this.removed;
  	}
  
  	public void disposeAll() {
  		synchronized (this.getMascots()) {
  			for (int i = this.getMascots().size() - 1; i >= 0; --i) {
  				this.getMascots().get(i).dispose();
  			}
  		}
  	}
  }
  