/*
 * File    : PluginConfigImpl.java
 * Created : 10 nov. 2003
 * By      : epall
 * 
 * Azureus - a Java Bittorrent client
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.gudy.azureus2.pluginsimpl.local;

import java.io.File;
import java.util.*;

import org.gudy.azureus2.core3.config.*;
import org.gudy.azureus2.core3.config.impl.*;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.FileUtil;

import org.gudy.azureus2.plugins.PluginConfig;
import org.gudy.azureus2.plugins.PluginConfigListener;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.config.ConfigParameter;
import org.gudy.azureus2.plugins.config.PluginConfigSource;
import org.gudy.azureus2.pluginsimpl.local.config.*;

import com.aelitis.net.magneturi.MagnetURIHandler;

public class 
PluginConfigImpl
	implements PluginConfig 
{

	protected static Map<String,String>	external_to_internal_key_map = new HashMap<String,String>();
	private PluginConfigSourceImpl external_source = null;

	static{
		
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_UPLOAD_SPEED_KBYTES_PER_SEC, 		CORE_PARAM_INT_MAX_UPLOAD_SPEED_KBYTES_PER_SEC );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_UPLOAD_SPEED_SEEDING_KBYTES_PER_SEC, 		"Max Upload Speed Seeding KBs" );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC, 	CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_CONNECTIONS_GLOBAL, 				"Max.Peer.Connections.Total" );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_CONNECTIONS_PER_TORRENT, 			"Max.Peer.Connections.Per.Torrent" );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_DOWNLOADS, 						"max downloads" );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_ACTIVE, 							"max active torrents" );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_ACTIVE_SEEDING, 							"StartStopManager_iMaxActiveTorrentsWhenSeeding" );
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_UPLOADS, "Max Uploads");
		external_to_internal_key_map.put( CORE_PARAM_INT_MAX_UPLOADS_SEEDING, "Max Uploads Seeding");
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_MAX_UPLOAD_SPEED_SEEDING, "enable.seedingonly.upload.rate");
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_MAX_ACTIVE_SEEDING, "StartStopManager_bMaxActiveTorrentsWhenSeedingEnabled");
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_AUTO_SPEED_ON, CORE_PARAM_BOOLEAN_AUTO_SPEED_ON);
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_AUTO_SPEED_SEEDING_ON, CORE_PARAM_BOOLEAN_AUTO_SPEED_SEEDING_ON );
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_SOCKS_PROXY_NO_INWARD_CONNECTION, 	"Proxy.Data.SOCKS.inform" );
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_NEW_SEEDS_START_AT_TOP, 			CORE_PARAM_BOOLEAN_NEW_SEEDS_START_AT_TOP );
		external_to_internal_key_map.put( CORE_PARAM_STRING_LOCAL_BIND_IP, 						"Bind IP" );
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_FRIENDLY_HASH_CHECKING, 			"diskmanager.friendly.hashchecking" );
		external_to_internal_key_map.put( GUI_PARAM_INT_SWT_REFRESH_IN_MS,                      "GUI Refresh");
		external_to_internal_key_map.put( CORE_PARAM_BOOLEAN_NEW_TORRENTS_START_AS_STOPPED,     "Default Start Torrents Stopped");
		external_to_internal_key_map.put( CORE_PARAM_INT_INCOMING_TCP_PORT, "TCP.Listen.Port");
		external_to_internal_key_map.put( CORE_PARAM_INT_INCOMING_UDP_PORT, "UDP.Listen.Port");
		external_to_internal_key_map.put( CORE_PARAM_STRING_DEFAULT_SAVE_PATH, "Default save path");
		
		// Note: Not in PluginConfig.java because it's an UI option and
		//       not applicable to all UIs
		// TODO: Add a smarter way
		
		// Following parameters can be set directly (we don't have an alias for these values).
		String[] passthrough_params = new String[] {
				"Open MyTorrents", "IconBar.enabled", "Wizard Completed",
				"welcome.version.lastshown",
		};
		
		for (int i=0; i<passthrough_params.length; i++) {
			external_to_internal_key_map.put(passthrough_params[i], passthrough_params[i]);
		}
	}
	
	public void checkValidCoreParam(String name) {
		if (!external_to_internal_key_map.containsKey(name)) {
			throw new IllegalArgumentException("invalid core parameter: " + name);
		}
	}

	private static Map	fake_values_when_disabled;
	private static int	fake_values_ref_count;
	
	public static void
	setEnablePluginCoreConfigChange(
		boolean		enabled )
	{
		synchronized( PluginConfigImpl.class ){

			if ( enabled ){
				
				fake_values_ref_count--;
				
				if ( fake_values_ref_count == 0 ){
					
						// TODO: we could try and recover the faked values at this point
					
					fake_values_when_disabled = null;
				}				

			}else{
				fake_values_ref_count++;

				if ( fake_values_ref_count == 1 ){
					
					fake_values_when_disabled = new HashMap();
				}
			}
		}
	}
	
	private static Object
	getFakeValueWhenDisabled(
		String	key,
		String	name )
	{
		if ( name.startsWith(key)){
			return( null );
		}
		
		synchronized( PluginConfigImpl.class ){
			
			if ( fake_values_when_disabled != null ){
				
				return( fake_values_when_disabled.get( name ));
			}
		}
		
		return( null );
	}
	
	private static boolean
	setFakeValueWhenDisabled(
		String	key,
		String	name,
		Object	value )
	{
		if ( name.startsWith(key)){
			return( false );
		}
		
		synchronized( PluginConfigImpl.class ){
			
			if ( fake_values_when_disabled != null ){
				
				fake_values_when_disabled.put( name, value );
				
				return( true );
			}
		}
		
		return( false );
	}
	
	private PluginInterface	plugin_interface;
	private String 			key;
	private boolean         allow_key_modification;
  
	public 
	PluginConfigImpl(
		PluginInterface		_plugin_interface,
		String			 	_key ) 
	{
		plugin_interface	= _plugin_interface;
		
		key = _key + ".";
		allow_key_modification = true;
	}

	public boolean
	isNewInstall()
	{
		return( COConfigurationManager.isNewInstall());
	}
	
	public String
	getPluginConfigKeyPrefix()
	{
		return( key );
	}
	
	public void setPluginConfigKeyPrefix(String _key) {
		if (!allow_key_modification) {
			throw new RuntimeException("cannot modify key prefix - already in use");
		}
		
		if (_key.length() > 0 || plugin_interface.getPluginState().isBuiltIn()) {
			key = _key;
		} else {
			throw (new RuntimeException("Can't set Plugin Config Key Prefix to '"
					+ _key + "'"));
		}
	}
	
	//
	//
	// Helper methods which do everything required to get a parameter value.
	//
	//
	private boolean getBooleanParameter(String name, boolean _default, boolean map_name, boolean set_default) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return(((Boolean)obj).booleanValue());
		}
		if (map_name) {name = mapKeyName(name, false);}
		
		notifyParamExists(name);
		if (set_default) {COConfigurationManager.setBooleanDefault(name, _default);}
		else if (!hasParameter(name)) {return _default;}
		return COConfigurationManager.getBooleanParameter(name);
	}
	
	private int[] getColorParameter(String name, int[] _default, boolean map_name, boolean set_default) {
		Object obj = getFakeValueWhenDisabled(key, name);
		if (obj != null) {return (int[])obj;}
		
		if (map_name) {name = mapKeyName(name, false);}
		int[] result = getColorParameter0(name, _default, set_default);
		
		// Result array...
		if (result == null) {return null;}
		if (result.length == 3) {
			int[] result2 = new int[4];
			System.arraycopy(result, 0, result2, 0, 3);
			
			// Not sure what's the default result to return here for the override flag.
			//
			// I've just chosen zero for now.
			result2[3] = getIntParameter(name, 0, false, false);
			result = result2;
		}
		
		return result;
	}
	
	private int[] getColorParameter0(String name, int[] _default, boolean set_default) {
		Object obj = getFakeValueWhenDisabled(key, name);
		if (obj != null){
			return((int[])obj);
		}
		
		notifyRGBParamExists(name);
		if (set_default) {
			
			// No idea what to do with the override flag, and no idea what to do
			// when the value is null.
			if (_default != null) {
				COConfigurationManager.setIntDefault(name + ".red", _default[0]);
				COConfigurationManager.setIntDefault(name + ".green", _default[1]);
				COConfigurationManager.setIntDefault(name + ".blue", _default[2]);
			}
			
			else {
				// I'm not expecting this branch to be executed by any callers.
				throw new RuntimeException("color parameter default is null");
			}
			
		}
		else if (!hasParameter(name + ".red")) {return _default;}
		
		return new int[] {
			COConfigurationManager.getIntParameter(name + ".red"),
			COConfigurationManager.getIntParameter(name + ".green"),
			COConfigurationManager.getIntParameter(name + ".blue"),
			COConfigurationManager.getIntParameter(name + ".override"),
		};
	}
	
	private byte[] getByteParameter(String name, byte[] _default, boolean map_name, boolean set_default) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return((byte[])obj);
		}
		if (map_name) {name = mapKeyName(name, false);}
		
		notifyParamExists(name);
		if (set_default) {COConfigurationManager.setByteDefault(name, _default);}
		else if (!hasParameter(name)) {return _default;}
		return COConfigurationManager.getByteParameter(name);
	}
	
	private float getFloatParameter(String name, float _default, boolean map_name, boolean set_default) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return(((Float)obj).floatValue());
		}
		if (map_name) {name = mapKeyName(name, false);}
		
		notifyParamExists(name);
		if (set_default) {COConfigurationManager.setFloatDefault(name, _default);}
		else if (!hasParameter(name)) {return _default;}
		return COConfigurationManager.getFloatParameter(name);
	}
	
	private int getIntParameter(String name, int _default, boolean map_name, boolean set_default) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return(((Long)obj).intValue());
		}
		if (map_name) {name = mapKeyName(name, false);}
		
		notifyParamExists(name);
		if (set_default) {COConfigurationManager.setIntDefault(name, _default);}
		else if (!hasParameter(name)) {return _default;}
		return COConfigurationManager.getIntParameter(name);
	}

	private long getLongParameter(String name, long _default, boolean map_name, boolean set_default) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return(((Long)obj).longValue());
		}
		if (map_name) {name = mapKeyName(name, false);}
		
		notifyParamExists(name);
		if (set_default) {COConfigurationManager.setLongDefault(name, _default);}
		else if (!hasParameter(name)) {return _default;}
		return COConfigurationManager.getLongParameter(name);
	}
	
	private String getStringParameter(String name, String _default, boolean map_name, boolean set_default) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return((String)obj);
		}
		if (map_name) {name = mapKeyName(name, false);}
		
		notifyParamExists(name);		
		if (set_default) {COConfigurationManager.setStringDefault(name, _default);}
		else if (!hasParameter(name)) {return _default;}
		return COConfigurationManager.getStringParameter(name);
	}

	//
	//
	// Variants of the methods above, but which use the values from ConfigurationDefaults.
	//
	//
	private boolean getDefaultedBooleanParameter(String name, boolean map_name) {
		Object	obj = getFakeValueWhenDisabled(key,name);
		if ( obj != null ){
			return(((Boolean)obj).booleanValue());
		}
		return getBooleanParameter(name, ConfigurationDefaults.def_boolean == 1, map_name, false);
	}
	
	private byte[] getDefaultedByteParameter(String name, boolean map_name) {
		return getByteParameter(name, ConfigurationDefaults.def_bytes, map_name, false);
	}
	
	private int[] getDefaultedColorParameter(String name, boolean map_name) {
		int[] default_value = new int[] {
				ConfigurationDefaults.def_int,
				ConfigurationDefaults.def_int,
				ConfigurationDefaults.def_int,
				1
		};
		return getColorParameter(name, default_value, map_name, false);
	}
	
	private float getDefaultedFloatParameter(String name, boolean map_name) {
		return getFloatParameter(name, ConfigurationDefaults.def_float, map_name, false);
	}
	
	private int getDefaultedIntParameter(String name, boolean map_name) {
		return getIntParameter(name, ConfigurationDefaults.def_int, map_name, false);
	}

	private long getDefaultedLongParameter(String name, boolean map_name) {
		return getLongParameter(name, ConfigurationDefaults.def_long, map_name, false);
	}
	
	private String getDefaultedStringParameter(String name, boolean map_name) {
		return getStringParameter(name, ConfigurationDefaults.def_String, map_name, false);
	}


	
	//
	//
	// Core get parameter methods (backwardly compatible).
	//
	//

	/**
	 * @deprecated
	 */
	public boolean getBooleanParameter(String name) {
		return getDefaultedBooleanParameter(name, true);
	}

	/**
	 * @deprecated
	 */
	public boolean getBooleanParameter(String name, boolean default_value) {
		return getBooleanParameter(name, default_value, true, false);
	}
	
	/**
	 * @deprecated
	 */
	public byte[] getByteParameter(String name) {
		return getDefaultedByteParameter(name, true);
	}

	/**
	 * @deprecated
	 */
	public byte[] getByteParameter(String name, byte[] default_value) {
		return getByteParameter(name, default_value, true, false);
	}

	/**
	 * @deprecated
	 */
	public float getFloatParameter(String name) {
		return getDefaultedFloatParameter(name, true);
	}

	/**
	 * @deprecated
	 */
	public float getFloatParameter(String name, float default_value) {
		return getFloatParameter(name, default_value, true, false);
	}

	/**
	 * @deprecated
	 */
	public int getIntParameter(String name) {
		return getDefaultedIntParameter(name, true);
	}

	/**
	 * @deprecated
	 */
	public int getIntParameter(String name, int default_value) {
		return getIntParameter(name, default_value, true, false);
	}

	/**
	 * @deprecated
	 */
	public long getLongParameter(String name) {
		return getDefaultedLongParameter(name, true);
	}

	/**
	 * @deprecated
	 */
	public long getLongParameter(String name, long default_value) {
		return getLongParameter(name, default_value, true, false);
	}
	
	/**
	 * @deprecated
	 */
	public String getStringParameter(String name) {
		return getDefaultedStringParameter(name, true);
	}

	/**
	 * @deprecated
	 */
    public String getStringParameter(String name, String default_value) {
    	return getStringParameter(name, default_value, true, false);
    }

	//
	//
	// Core get parameter methods (newly named ones).
	//
	//

	public boolean getCoreBooleanParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedBooleanParameter(name, true);
	}

	public boolean getCoreBooleanParameter(String name, boolean default_value) {
		checkValidCoreParam(name);
		return getBooleanParameter(name, default_value, true, false);
	}
	
	public byte[] getCoreByteParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedByteParameter(name, true);
	}

	public byte[] getCoreByteParameter(String name, byte[] default_value) {
		checkValidCoreParam(name);
		return getByteParameter(name, default_value, true, false);
	}
	
	public int[] getCoreColorParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedColorParameter(name, true);
	}

	public int[] getCoreColorParameter(String name, int[] default_value) {
		checkValidCoreParam(name);
		return getColorParameter(name, default_value, true, false);
	}
	
	public float getCoreFloatParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedFloatParameter(name, true);
	}

	public float getCoreFloatParameter(String name, float default_value) {
		checkValidCoreParam(name);
		return getFloatParameter(name, default_value, true, false);
	}

	public int getCoreIntParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedIntParameter(name, true);
	}

	public int getCoreIntParameter(String name, int default_value) {
		checkValidCoreParam(name);
		return getIntParameter(name, default_value, true, false);
	}

	public long getCoreLongParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedLongParameter(name, true);
	}

	public long getCoreLongParameter(String name, long default_value) {
		checkValidCoreParam(name);
		return getLongParameter(name, default_value, true, false);
	}
	
	public String getCoreStringParameter(String name) {
		checkValidCoreParam(name);
		return getDefaultedStringParameter(name, true);
	}

    public String getCoreStringParameter(String name, String default_value) {
    	checkValidCoreParam(name);
    	return getStringParameter(name, default_value, true, false);
    }
    
	//
	//
	// Core set parameter methods (backwardly compatible).
	//
	//
    public void setBooleanParameter(String name, boolean value) {
		if ( setFakeValueWhenDisabled(key, name, new Boolean( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setByteParameter(String name, byte[] value) {
		if ( setFakeValueWhenDisabled(key, name, value )){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setColorParameter(String name, int[] value) {
    	setColorParameter(name, value, true);
    }
    
    public void setColorParameter(String name, int[] value, boolean override) {
		if ( setFakeValueWhenDisabled(key, name, value )){
			return;
		}
		COConfigurationManager.setRGBParameter(mapKeyName(name, true), value, override);
    }
    
    public void setFloatParameter(String name, float value) {
		if ( setFakeValueWhenDisabled(key, name, new Float( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setIntParameter(String name, int value) {
		if ( setFakeValueWhenDisabled(key, name, new Long( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setLongParameter(String name, long value) {
		if ( setFakeValueWhenDisabled(key, name, new Long( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setStringParameter(String name, String value) {
		if ( setFakeValueWhenDisabled(key, name, value)){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

	//
	//
	// Core set parameter methods (newly named ones).
	//
	//
    public void setCoreBooleanParameter(String name, boolean value) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, new Boolean( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setCoreByteParameter(String name, byte[] value) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, value )){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setCoreColorParameter(String name, int[] value) {
    	setCoreColorParameter(name, value, true);
    }
    
    public void setCoreColorParameter(String name, int[] value, boolean override) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, value)) {
			return;
		}
		COConfigurationManager.setRGBParameter(mapKeyName(name, true), value, override);
    }

    public void setCoreFloatParameter(String name, float value) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, new Float( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setCoreIntParameter(String name, int value) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, new Long( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setCoreLongParameter(String name, long value) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, new Long( value))){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    public void setCoreStringParameter(String name, String value) {
    	checkValidCoreParam(name);
		if ( setFakeValueWhenDisabled(key, name, value)){
			return;
		}
    	COConfigurationManager.setParameter(mapKeyName(name, true), value);
    }

    
	//
	//
	// Plugin get parameter methods.
	//
	//
	public boolean getPluginBooleanParameter(String name) {
		return getDefaultedBooleanParameter(this.key + name, false);
	}

	public boolean getPluginBooleanParameter(String name, boolean default_value) {
		return getBooleanParameter(this.key + name, default_value, false, true);
	}
	
	public byte[] getPluginByteParameter(String name) {
		return getDefaultedByteParameter(this.key + name, false);
	}

	public byte[] getPluginByteParameter(String name, byte[] default_value) {
		return getByteParameter(this.key + name, default_value, false, true);
	}

	public int[] getPluginColorParameter(String name) {
		return getDefaultedColorParameter(this.key + name, false);
	}

	public int[] getPluginColorParameter(String name, int[] default_value) {
		return getColorParameter(this.key + name, default_value, false, true);
	}
		
	public float getPluginFloatParameter(String name) {
		return getDefaultedFloatParameter(this.key + name, false);
	}

	public float getPluginFloatParameter(String name, float default_value) {
		return getFloatParameter(this.key + name, default_value, false, true);
	}

	public int getPluginIntParameter(String name) {
		return getDefaultedIntParameter(this.key + name, false);
	}

	public int getPluginIntParameter(String name, int default_value) {
		return getIntParameter(this.key + name, default_value, false, true);
	}

	public long getPluginLongParameter(String name) {
		return getDefaultedLongParameter(this.key + name, false);
	}

	public long getPluginLongParameter(String name, long default_value) {
		return getLongParameter(this.key + name, default_value, false, true);
	}
	
	public String getPluginStringParameter(String name) {
		return getDefaultedStringParameter(this.key + name, false);
	}

    public String getPluginStringParameter(String name, String default_value) {
    	return getStringParameter(this.key + name, default_value, false, true);
    }
    
	//
	//
	// Plugin set parameter methods.
	//
	//
    public void setPluginParameter(String name, boolean value) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setParameter(this.key + name, value);
    }

    public void setPluginParameter(String name, byte[] value) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setParameter(this.key + name, value);
    }

    public void setPluginParameter(String name, float value) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setParameter(this.key + name, value);
    }

    public void setPluginParameter(String name, int value) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setParameter(this.key + name, value);
    }

    public void setPluginParameter(String name, long value) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setParameter(this.key + name, value);
    }

    public void setPluginParameter(String name, String value) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setParameter(this.key + name, value);
    }

    public void setPluginColorParameter(String name, int[] value) {
    	setPluginColorParameter(name, value, true);
    }

    public void setPluginColorParameter(String name, int[] value, boolean override) {
    	notifyParamExists(this.key + name);
    	COConfigurationManager.setRGBParameter(this.key + name, value, override);
    }
    
   	//
	//
	// Core "unsafe" get parameter methods.
	//
	//

	public boolean getUnsafeBooleanParameter(String name) {
		return getDefaultedBooleanParameter(name, false);
	}

	public boolean getUnsafeBooleanParameter(String name, boolean default_value) {
		return getBooleanParameter(name, default_value, false, false);
	}
	
	public byte[] getUnsafeByteParameter(String name) {
		return getDefaultedByteParameter(name, false);
	}

	public byte[] getUnsafeByteParameter(String name, byte[] default_value) {
		return getByteParameter(name, default_value, false, false);
	}

	public int[] getUnsafeColorParameter(String name) {
		return getDefaultedColorParameter(name, false);
	}

	public int[] getUnsafeColorParameter(String name, int[] default_value) {
		return getColorParameter(name, default_value, false, false);
	}
	
	public float getUnsafeFloatParameter(String name) {
		return getDefaultedFloatParameter(name, false);
	}

	public float getUnsafeFloatParameter(String name, float default_value) {
		return getFloatParameter(name, default_value, false, false);
	}

	public int getUnsafeIntParameter(String name) {
		return getDefaultedIntParameter(name, false);
	}

	public int getUnsafeIntParameter(String name, int default_value) {
		return getIntParameter(name, default_value, false, false);
	}
	
	public long getUnsafeLongParameter(String name) {
		return getDefaultedLongParameter(name, false);
	}

	public long getUnsafeLongParameter(String name, long default_value) {
		return getLongParameter(name, default_value, false, false);
	}
	
	public String getUnsafeStringParameter(String name) {
		return getDefaultedStringParameter(name, false);
	}

    public String getUnsafeStringParameter(String name, String default_value) {
    	return getStringParameter(name, default_value, false, false);
    }

	//
	//
	// Core "unsafe" set parameter methods.
	//
	//
    public void setUnsafeBooleanParameter(String name, boolean value) {
		if ( setFakeValueWhenDisabled(key, name, new Boolean( value))){
			return;
		}
		notifyParamExists(name);
		COConfigurationManager.setParameter(name, value);
    }

    public void setUnsafeByteParameter(String name, byte[] value) {
		if ( setFakeValueWhenDisabled(key, name, value)){
			return;
		}
		notifyParamExists(name);
		COConfigurationManager.setParameter(name, value);
    }

    public void setUnsafeColorParameter(String name, int[] value) {
    	setUnsafeColorParameter(name, value, true);
    }
    
    public void setUnsafeColorParameter(String name, int[] value, boolean override) {
   		if ( setFakeValueWhenDisabled(key, name, value)){
			return;
		}
   		notifyRGBParamExists(name);
   		COConfigurationManager.setRGBParameter(name, value, override);
    }
    
    public void setUnsafeFloatParameter(String name, float value) {
		if ( setFakeValueWhenDisabled(key, name, new Float( value))){
			return;
		}
		notifyParamExists(name);
    	COConfigurationManager.setParameter(name, value);
    }

    public void setUnsafeIntParameter(String name, int value) {
		if ( setFakeValueWhenDisabled(key, name, new Long( value))){
			return;
		}
		notifyParamExists(name);
    	COConfigurationManager.setParameter(name, value);
    }

    public void setUnsafeLongParameter(String name, long value) {
		if ( setFakeValueWhenDisabled(key, name, new Long( value))){
			return;
		}
		notifyParamExists(name);
    	COConfigurationManager.setParameter(name, value);
    }

    public void setUnsafeStringParameter(String name, String value) {
		if ( setFakeValueWhenDisabled(key, name, value )){
			return;
		}
		notifyParamExists(name);
    	COConfigurationManager.setParameter(name, value);
    }
    
    //
    //
    // Get/set plugin list/map methods.
    //
    //
    
    public String[] getPluginStringListParameter(String key) {
    	notifyParamExists(this.key + key);
    	return COConfigurationManager.getStringListParameter(this.key + key).toArray();
    }

	 public void setPluginStringListParameter(String key, String[] value) {
		 notifyParamExists(this.key + key);
		 StringListImpl list_obj = new StringListImpl(Arrays.asList(value));
		 COConfigurationManager.setParameter(this.key+key, list_obj);
	 }
    
	 public List getPluginListParameter(String key, List default_value) {
		 notifyParamExists(this.key + key);
		return COConfigurationManager.getListParameter(this.key+key, default_value); 
	 }
	 
	 public void setPluginListParameter(String key, List value) {
		 notifyParamExists(this.key + key);
		 COConfigurationManager.setParameter(this.key+key, value);
	 }

	 public Map getPluginMapParameter(String key, Map default_value) {
		 notifyParamExists(this.key + key);
		return COConfigurationManager.getMapParameter(this.key+key, default_value); 
	 }
	 
	 public void setPluginMapParameter(String key, Map value) {
		 notifyParamExists(this.key + key);
		 COConfigurationManager.setParameter(this.key+key, value);
	 }
	 
	 public void setPluginParameter(String key, int value, boolean global) {
		 notifyParamExists(this.key + key);
		COConfigurationManager.setParameter(this.key+key, value);
		if (global) {
			MagnetURIHandler.getSingleton().addInfo(this.key+key, value);
		}
	 }

	public ConfigParameter
	getParameter(
		String		key )
	{
		return( new ConfigParameterImpl( mapKeyName(key, false)));
	}
	
	public ConfigParameter
	getPluginParameter(
	  	String		key )
	{
		return( new ConfigParameterImpl( this.key+key ));
	}
	
	public boolean removePluginParameter(String key) {
		notifyParamExists(this.key + key);
		return COConfigurationManager.removeParameter(this.key + key);
	}
	
	public boolean removePluginColorParameter(String key) {
		notifyParamExists(this.key + key);
		return COConfigurationManager.removeRGBParameter(this.key + key);
	}

	  public Map
	  getUnsafeParameterList()
	  {
		  Set params = COConfigurationManager.getAllowedParameters();
		  
		  Iterator	it = params.iterator();
		  
		  Map	result = new HashMap();
		  
		  while( it.hasNext()){
			  
			  try{
				  String	name = (String)it.next();
				  
				  Object val = COConfigurationManager.getParameter( name );
				  
				  if ( val instanceof String || val instanceof Long ){
					  
				  }else if ( val instanceof byte[]){
					  
					  val = new String((byte[])val, "UTF-8" );
					  
				  }else if ( val instanceof Integer ){
					  
					  val = new Long(((Integer)val).intValue());
	
				  }else if ( val instanceof List ){
					  
					  val = null;
					  
				  }else if ( val instanceof Map ){
					  
					  val = null;
					  
				  }else if ( val instanceof Boolean ){
					  
					  val = new Long(((Boolean)val).booleanValue()?1:0);
					  
				  }else if ( val instanceof Float || val instanceof Double ){
					  
					  val = val.toString();
				  }
				  
				  if ( val != null ){
					 
					  result.put( name, val );
				  }
			  }catch( Throwable e ){
				  
				  Debug.printStackTrace(e);
			  }
		  }
		  
		  return( result );
	  }
	
	public void
	save()
	{
		/**
		 * We won't redirect the save method to the external source if there is one
		 * (despite that being the previous behaviour) - the plugin might be setting
		 * core values.
		 */
		//if (this.external_source != null) {this.external_source.save(true);}
		COConfigurationManager.save();
	}
		
	public File
	getPluginUserFile(
		String	name )
	{
		
		String	dir = plugin_interface.getUtilities().getAzureusUserDir();
		
		File	file = new File( dir, "plugins" );

		String	p_dir = plugin_interface.getPluginDirectoryName();
		
		if ( p_dir.length() != 0 ){
			
			int	lp = p_dir.lastIndexOf(File.separatorChar);
			
			if ( lp != -1 ){
				
				p_dir = p_dir.substring(lp+1);
			}
			
			file = new File( file, p_dir );
			
		}else{
			
			String	id = plugin_interface.getPluginID();
			
			if ( id.length() > 0 && !id.equals( PluginInitializer.INTERNAL_PLUGIN_ID )){
			
				file = new File( file, id );
				
			}else{
				
				throw( new RuntimeException( "Plugin was not loaded from a directory" ));
			}
		}
	
		
		FileUtil.mkdirs(file);
		
		return( new File( file, name ));
	}
	
	public void
	addListener(
		final PluginConfigListener	l )
	{
		COConfigurationManager.addListener(
			new COConfigurationListener()
			{
				public void
				configurationSaved()
				{
					l.configSaved();
				}
			});
	}

	private String mapKeyName(String key, boolean for_set) {
		String result = (String)external_to_internal_key_map.get(key);
		if (result == null) {
			if (for_set) {
				throw new RuntimeException("No permission to set the value of core parameter: " + key);
			}
			else {
				return key;
			}
		}
		return result;
	}
	
	public boolean hasParameter(String param_name) {
		// Don't see any reason why a plugin should care whether it is looking
		// at a system default setting or not, so we'll do an implicit check.
		return COConfigurationManager.hasParameter(param_name, false);
	}
	
	public boolean hasPluginParameter(String param_name) {
		// We should not have default settings for plugins in configuration
		// defaults, so we don't bother doing an implicit check.
		notifyParamExists(this.key + param_name);
		return COConfigurationManager.hasParameter(this.key + param_name, true);
	}
	
	public void notifyRGBParamExists(String param) {
		notifyParamExists(param + ".red");
		notifyParamExists(param + ".blue");
		notifyParamExists(param + ".green");
		notifyParamExists(param + ".override");
	}
		
	// Not exposed in the plugin API.
	public void notifyParamExists(String param) {
		if (allow_key_modification && param.startsWith(this.key)) {
			allow_key_modification = false;
		}
		if (external_source != null && param.startsWith(this.key)) {
			external_source.registerParameter(param);
		}
	}
	
	public PluginConfigSource enableExternalConfigSource() {
		PluginConfigSourceImpl source = new PluginConfigSourceImpl(this, this.plugin_interface.getPluginID());
		setPluginConfigSource(source);
		return source;
	}
	
	public PluginConfigSource getPluginConfigSource() {
		return this.external_source;
	}
	
	public void setPluginConfigSource(PluginConfigSource source) {
		if (this.external_source != null) {
			throw new RuntimeException("external config source already associated!");
		}
		
		// We need a common key prefix, otherwise this won't work correctly.
		PluginConfigSourceImpl source_impl = (PluginConfigSourceImpl)source;
	    String used_key = source_impl.getUsedKeyPrefix();
	    if (used_key != null && !this.getPluginConfigKeyPrefix().startsWith(used_key)) {
	    	throw new RuntimeException("cannot use this config source object - incompatible prefix keys: " + used_key + " / " + this.getPluginConfigKeyPrefix());
	    }
		this.external_source = (PluginConfigSourceImpl)source;
	}
	
}
