/*
 * Created on 2004-08-10
 */
package pl.zalewski.mikolaj.symbdev;

import java.util.*;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.preference.IPreferenceStore;

/**
 * @author mikolaj
 *
 * This object holds the list of all Win32 compilers autodetected or configured
 * by the user
 */
public class Win32ToolsList {
  static final String P_ENTRY_NAME="Win32ToolsList.name-";
  static final String P_ENTRY_PATH="Win32ToolsList.path-";
  static final String P_ENTRY_TARGET="Win32ToolsList.target-";
  static final String P_ID_LIST="Win32ToolsList.idList";
  
  static final String EXTENSION_POINT=SymbdevPlugin.getUniqueIdentifier()+".Win32ToolDetect";
  static final String DETECTOR_NAME_ATTRIBUTE = "name";
  static final String DETECTOR_CLASS_ATTRIBUTE = "class";

	
  protected Win32ToolsList()
  {
  	synchronized(this)
	{
  		toolMap=new TreeMap();
  		store=SymbdevPlugin.getDefault().getPreferenceStore();
  		loadIds();
  		autoDetect();
	}
  }
  
  protected static Win32ToolsList instance;
  
  public static Win32ToolsList getInstance()
  {
  	if (instance==null)
 		instance=new Win32ToolsList();
  	return instance;
  }
  
  /**
   * Stores the entry in the preferences file. If an id is not assigned, a new
   * id is genereated and stored in the entry object
   * @param entry the entry to store
   */
  public synchronized void storeEntry(Win32ToolEntry entry)
  {
  	boolean newId=false;
  	if (entry.id==-1)
  	{
  		entry.id=allocNewId();
  	    entry.name=entry.name.replaceAll(" \\(autodetected\\)", "");
  		newId=true;
  	}
  	
  	storeEntryData(entry);
  	toolMap.put(new Integer(entry.id), entry);
  	if (newId)
  		storeIdsList();
  }
  
  public synchronized void removeEntry(int id)
  {
  	toolMap.remove(new Integer(id));
  	storeIdsList();
  	clearEntryData(id);
  }
  
  /**
   * @return An iterator through the Win32ToolEntry objects
   */
  public synchronized Iterator getEntries()
  {
  	return toolMap.values().iterator();
  }
  
  public synchronized boolean isEmpty()
  {
    return toolMap.isEmpty();
  }
  
  /**
   * Gives the entry for the given id 
   * @param id the id
   * @return the entry for that id or null if not found
   */
  public synchronized Win32ToolEntry getEntry(int id)
  {
  	return (Win32ToolEntry)toolMap.get(new Integer(id));
  }
  
  protected void storeEntryData(Win32ToolEntry entry)
  {
  	store.setValue(P_ENTRY_NAME+entry.id, entry.name);
  	store.setValue(P_ENTRY_PATH+entry.id, entry.path);
  	store.setValue(P_ENTRY_TARGET+entry.id, entry.target);
  }
  
  protected void addDetectedEntry(Win32ToolEntry entry)
  {
  	entry.name+=" (autodetected)";
  	toolMap.put(new Integer(entry.id), entry);
  }
  
  protected void autoDetect()
  {
  	IExtensionRegistry registry = Platform.getExtensionRegistry();
    IExtensionPoint extensionPoint =
      registry.getExtensionPoint(EXTENSION_POINT);
    IConfigurationElement[] members =
      extensionPoint.getConfigurationElements();
    
    IWin32ToolConsumer consumer = new IWin32ToolConsumer() {
    	/* (non-Javadoc)
		 * @see pl.zalewski.mikolaj.symbdev.IWin32ToolConsumer#entryDetected(pl.zalewski.mikolaj.symbdev.Win32ToolEntry)
		 */
		public void entryDetected(Win32ToolEntry entry) {
			addDetectedEntry(entry);
		}
    };
    
//  For each service:
    for (int m = 0; m < members.length; m++) {
    	IConfigurationElement member = members[m];
        IExtension extension = member.getDeclaringExtension();
        String pluginLabel =  extension.getNamespace();
        String detectorName = member.getAttribute(DETECTOR_NAME_ATTRIBUTE);

        Object callback=null;
        try {
        	callback = member.createExecutableExtension(DETECTOR_CLASS_ATTRIBUTE);
        }
        catch (CoreException e)
		{
        	System.out.println(this.getClass().getName()+": couldn't initialize "+pluginLabel+" extension");
        	e.printStackTrace();
        	continue;
		}
        if (!(callback instanceof IWin32ToolDetector))
        {
        	System.out.println(this.getClass().getName()+": extension "+
        			pluginLabel+"/"+detectorName+" of invalid type - ignored");
        	continue;
        }
        
        IWin32ToolDetector detector = (IWin32ToolDetector)callback;
        detector.detect(consumer);
       }

  }
  
  protected Win32ToolEntry loadEntryData(int id)
  {
    Win32ToolEntry result=new Win32ToolEntry(false);
    result.name=store.getString(P_ENTRY_NAME+id);
    result.path=store.getString(P_ENTRY_PATH+id);
    result.target=store.getString(P_ENTRY_TARGET+id);
    result.autodetected=false;
    result.id=id;
    return result;
  }
  
  protected void clearEntryData(int id)
  {
  	store.setValue(P_ENTRY_NAME+id, "");
  	store.setValue(P_ENTRY_PATH+id, "");
  	store.setValue(P_ENTRY_TARGET+id, "");  	
  }
  
  protected void storeIdsList()
  {
  	String string;
  	Iterator it=toolMap.keySet().iterator();
  	if (!it.hasNext()) return;
  	string=""+((Integer)it.next()).intValue();
  	while (it.hasNext())
  	{
  		Integer id=(Integer)it.next();
  		string+=","+id.intValue();
  	}
  	store.setValue(P_ID_LIST, string);
  }
  
  protected void loadIds()
  {
  	String idList;
  	idList=store.getString(P_ID_LIST);
	int idx=0, newidx;
	if (idList.equals("")) return;
	while (idx<idList.length())
	{
		newidx=idList.indexOf(',', idx);
		if (newidx==-1) newidx=idList.length();
		int id=Integer.valueOf(idList.substring(idx, newidx)).intValue();
		Win32ToolEntry entry=loadEntryData(id);
	  	toolMap.put(new Integer(id), entry);		
		idx=newidx+1;
	}  	
  }
  
  protected int allocNewId()
  {
  	int i;
  	for (i=1; i<Win32ToolEntry.AUTODETECTED_MIN; i++)
  		if (!toolMap.containsKey(new Integer(i)))
  			return i; 
  	return 0;
  }
  
  // maps ids to tools entries
  TreeMap toolMap;
  IPreferenceStore store;
}
