// Copyright (c) 2005,2006 Toni Binci
package pacemaker;

import peersim.config.Configuration;
import peersim.config.IllegalParameterException;
import peersim.core.Node;

/**
 * @author Toni Binci
 * 
 * This class sums up all Firefly's traits, as found in literature.<p>
 * 
 * Big attentions is over config parameters, to be specified in .cfg files.
 * By choicing the right values (n-uple of parameters) inspired
 * by real values you can simulate a desidered species of firefly, 
 * or you can invent (experiment) new ones. 
 * Look at field "{@link #species}" for more.<p>
 * 
 * Note this class is abstract and not a direct implementation: a protocol should implement 
 * {@link peersim.core.Protocol} and its functionalities.
 *   
 * @see #species
 */
public abstract class Firefly {
	
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// ++++ .CFG PARAMETERS FOR FIREFLY
	// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	/**
	 * String name of the parameter used to suggest the simulator you want to 
	 * simulate a particular species of firefly. <p>
	 * 
	 * When you use this parameter, all other parameters are set automatically
	 * matching the real values for that species.<p>
	 * 
	 * But you can also continue to use them, if your intent is to start from a 
	 * real case and try out the effects of some modifications!  
	 * Your params will override the original values of specified species.<p> 
	 * Example:<p>
	 * <code>
	 * protocol.myfirefly
	 * {
	 * 		species <i>Malaccae</i>
	 * 		period 1500
	 * 	    ... others
	 * }
	 * </code><p>
	 * 
	 * We enlisted features found in these species:<p>
	 * <ol>
	 * <li>Pteroptyx<ol>
	 * 		<li>Malaccae
	 * 		<li>Cribellata
	 * 		<li>Tener
	 * 		<li>Valida</ol>
	 * <li>Photinus<ol>
	 * 		<li>Pyralis
	 * 		<li>Macdermotti
	 * 		<li>Versicolor
	 * 		<li>Carolinus
	 * 		<li>Concisus
	 * 		<li>Greeni
	 * 		<li>Consanguineous
	 * 		<li>Consimilis
	 * 		<li>Knulli
	 * 		<li>Carolinus
	 * 		<li>Pallens
	 * 		<li>Marginellus
	 * 		<li>Sabulosus
	 * 		<li>Umbratus
	 * 		<li>Collustrans
	 * 		<li>Ignitus</ol>
	 * <li>Photuris<ol>
	 * 		<li>Frontalis</ol>
	 * <li>Luciola<ol>
	 * 		<li>Pupilla
	 * 		<li>Cruciata</ol>
	 * </ol><p>
	 * To specify one, grab just the 2nd name of the firefly species 
	 * (Malaccae, Cribellata, ecc)<p> 
	 */	
	public static final String PAR_SPECIES = "species";
	public static String species;
	
	/**
	 * String name of the parameter used to determine the period of a firefly.<p>
	 * As in physics, its inverse is frequency. 
	 * Equivalent meanings are (to cope with different literature aliases):
	 * <ol>
	 * <li>(biological) flash period
	 * <li>(biological) flash interval
	 * <li>(physical) oscillator period
	 * <li>(computer science) cycle length
	 * </ol><p>
	 * To be honest, you can find any recombination of the above terms in literature ;-)
	 */
	public static final String PAR_PERIOD = "period";
	public static int period;
	
	/**
	 * String name of the paramater used to specify the duration of the light impulse.<p>
	 * A <b>flash is not instantaneous</b> (unlike the term suggest): its light is interely
	 * visible along its emission for fireflies (e.g flashes last 50msec in a 700msec
	 * period for some species).
	 * The remaining time (period minus pulse_duration) is usually named "dark period".
	 * Also, fireflies seems capable to recognize the form of the light emission, 
	 * that is believed to be a gaussian (Buck)<p>
	 * In nature this value is not longer than period. But we can experiment a lot. 
	 */
	public static final String PAR_PULSE_DURATION = "pulse_duration";
	public static int pulse_duration;
	
	/**
	 * String name of the paramater used to specify the inert (or refractory) 
	 * post-flash period of a firefly. <p>
	 * Just after flashing, a firefly cannot re-flash because she need some time to 
	 * refill the its energy subsystem capable of lighting. 
	 * In this state, like the "{@link #prep}" parameter, the 
	 * firefly seems "refractory" to input in the sense she cannot do another flash.
	 * However, who knows if she continues (or not) to "keep in mind" flashes 
	 * albeit unable to emit light? <p>
	 * In computer science, we can think it as a state of "resource busy": we cannot
	 * perform action for a while (here: flashing).<p>
	 * In nature this value is not longer than period. But we can experiment a lot. 
	 */
	public static final String PAR_INERT = "inert";
	public static int inert;
	
	/**
	 * String name of the paramater used to specify the preparatory 
	 * pre-flash period of a firefly. <p>
	 * To do flashing, a firefly must prepare it: the neural stimulus originates
	 * from brain and reach the abdomen where the light-capable subsystem
	 * builds up the chemical reaction responsible for light emission.
	 * A certain amount of time are required for this.  <p>
	 * In this state, like the "{@link #inert}" parameter, the 
	 * firefly seems "refractory" to input in the sense she are full committed in 
	 * preparing its flash and cannot reverse or delay the started action.
	 * However, who knows if she continues (or not) to "keep in mind" flashes 
	 * albeit unable to control its irreversible flash?<p> 
	 * In computer science, we can think it as a state of "resource busy": we cannot
	 * perform action for a while (here: undoing flash).<p>
	 * In nature this value is not longer than period. But we can experiment a lot. 
	 */
	public static final String PAR_PREP = "prep";
	public static int prep;
	
	
	/**
	 * String name of the paramater used to specify the "reaction" model.<p>
	 * When receiving (input) flashes, a firefly that wants to get in sync with other
	 * can "react" by altering some of its internal parameters (phase, frequency).
	 * Consider that there are situations where input DO NOT trigger alterations.
	 * So we mean: <i>What's the model the firefly obey?<i> <p>
	 * Possible values (classification by Ermentrout 1991):<ol>
	 * <li>ADVANCE: for "Phase Advance" - Buck 1978
	 * <li>DELAY: for "Phase Delay" - Buck 1978
	 * <li>MAS: for Mirollo And Strogatz's model - Mirollo and Strogatz, 1990
	 * <li>ADAPTIVE: for Ermentrout's own adaptive model - Ermentrout 1991
	 * </ol>
	 */
	public static final String PAR_MODEL = "model";
	public static String model;
	
	/**
	 * String name of the paramater used to determine the form of the 
	 * Phase Response Curve (PRC) for the {@link #PAR_MODEL "MAS" model}.<p>
	 * See {@link MaSModel} for more info about PRC.<p>
	 * 
	 * That value must be an integer and is interpred as a parameter named '<b>b</b>' 
	 * that influences the form of the PRC in the plane. (Mirollo and Strogatz 1990, Fig. 3)<p>
	 * 
	 * <b>b</b> must be in the range ]0,+inf] for the definition of f(x).
	 * When <b>b</b> approaches 0, f(x) resembles the identity function (y=x). 
	 * But when <b>b</b> exactly equal 0, the function shape degenerates. 
	 * So, in this implementation we accept 0: when you supply it we will
	 * substitute the above f(x) with identity f(x)=x. 
	 * @see MaSModel
	 */
	public static final String PAR_B = "b";
	public static int b;
	
	/**
	 * String name of the paramater used to determine the pulse strength parameter
	 * named <i>epsilon</i> for the {@link Firefly#PAR_MODEL "MAS" model}.<p>
	 * Epsilon is real number in [0,1] and models how much an incoming
	 * flash will influence the state of the receiving Firefly. 
	 * In the "MAS" model the firefly flashes when PRC reaches (1,1), epsilon
	 * is a constant that makes the firefly state jump toward (1,1).
	 * The bigger epsilon, the bigger the probability that an incoming flash 
	 * will trigger an immediate flash in the receiving firefly.
	 * @see MaSModel
	 */	
	public static final String PAR_EPSILON = "epsilon"; 
	public static double epsilon;
	
	/**
	 * String name of the paramater used to determine maximal period to which the
	 * Fyrefly can adapt, for the {@link Firefly#PAR_MODEL "ADAPTIVE" model}.<p>
	 * Can't be smaller of the Firefly {@link #PAR_PERIOD period} parameter.
	 * @see AdaptiveModel
	 */	
	public static final String PAR_MAX = "max"; 
	public static int max;

	/**
	 * String name of the paramater used to determine minimal period to which the
	 * Fyrefly can adapt, for the {@link Firefly#PAR_MODEL "ADAPTIVE" model}.<p>
	 * Can't be bigger of the Firefly {@link #PAR_PERIOD period} parameter.
	 * @see AdaptiveModel
	 */	
	public static final String PAR_MIN = "min"; 
	public static int min;

	/**
	 * String name of the paramater used to determine the relaxation parameter 
	 * that measures the rate at which the Firefly will return to its natural frequency
	 * (natural_frequency is 1/{@link #period period parameter}).<p>
	 * Used for the {@link Firefly#PAR_MODEL "ADAPTIVE" model}.<p>
	 * In literature (Ermentrout, 1991), it's named <i>epsilon</i> but it's not the same
	 * {@link #epsilon} of the {@link Firefly#PAR_MODEL "MAS" model}. So I renamed it.<p>
	 * @see AdaptiveModel
	 */	
	public static final String PAR_RELAXATION = "relaxation"; 
	public static double relaxation;
	
	/*
	 * 
	 */
	public Firefly(String prefix)
	{	
		species = Configuration.getString(prefix+"."+PAR_SPECIES, null).toUpperCase();
		// species == null when unspecified
		int index = -1; //index = -1 when species don't exists
		if (species != null) {
			//System.out.println("size "+Species.data.size+" size "+Species.data[0].size);
			for (int i=0; i<Species.data.length; i++) 
				if (Species.data[i][Species.NAME].equalsIgnoreCase(species)) {
					index = i; 
					break; 
				}
		    if (index == -1) System.out.println("ERROR: Inexistant species name: "+species);
		}

		if (index == -1 || Species.data[index][Species.PERIOD] == null) 
			period = Configuration.getInt(prefix+"."+PAR_PERIOD);
		else period = Configuration.getInt(prefix+"."+PAR_PERIOD, Integer.parseInt(Species.data[index][Species.PERIOD]));

		if (index == -1 || Species.data[index][Species.PULSE_DURATION] == null) 
			pulse_duration = Configuration.getInt(prefix+"."+PAR_PULSE_DURATION);
		else pulse_duration = Configuration.getInt(prefix+"."+PAR_PULSE_DURATION, Integer.parseInt(Species.data[index][Species.PULSE_DURATION]));

		if (index == -1 || Species.data[index][Species.PREP] == null) 
			prep = Configuration.getInt(prefix+"."+PAR_PREP);
		else prep = Configuration.getInt(prefix+"."+PAR_PREP, Integer.parseInt(Species.data[index][Species.PREP]));

		if (index == -1 || Species.data[index][Species.INERT] == null) 
			inert = Configuration.getInt(prefix+"."+PAR_INERT);
		else inert = Configuration.getInt(prefix+"."+PAR_INERT, Integer.parseInt(Species.data[index][Species.INERT]));
		
		if (index == -1 || Species.data[index][Species.MODEL] == null) 
			model = Configuration.getString(prefix+"."+PAR_MODEL).toUpperCase();
		else model = Configuration.getString(prefix+"."+PAR_MODEL, Species.data[index][Species.MODEL]).toUpperCase();
		
		if (model.equals("MAS")) {
			if (index == -1 || Species.data[index][Species.B] == null) 
				b = Configuration.getInt(prefix+"."+PAR_B);
			else b = Configuration.getInt(prefix+"."+PAR_B, Integer.parseInt(Species.data[index][Species.B]));
	
			if (index == -1 || Species.data[index][Species.EPSILON] == null) 
				epsilon = Configuration.getDouble(prefix+"."+PAR_EPSILON);
			else epsilon = Configuration.getDouble(prefix+"."+PAR_EPSILON, Double.parseDouble(Species.data[index][Species.EPSILON]));
		}
		
		if (model.equals("ADAPTIVE")) {
			if (index == -1 || Species.data[index][Species.MAX] == null) 
				max = Configuration.getInt(prefix+"."+PAR_MAX);
			else max = Configuration.getInt(prefix+"."+PAR_MAX, Integer.parseInt(Species.data[index][Species.MAX]));
	
			if (index == -1 || Species.data[index][Species.MIN] == null) 
				min = Configuration.getInt(prefix+"."+PAR_MIN);
			else min = Configuration.getInt(prefix+"."+PAR_MIN, Integer.parseInt(Species.data[index][Species.MIN]));
			
			if (index == -1 || Species.data[index][Species.RELAXATION] == null) 
				relaxation = Configuration.getDouble(prefix+"."+PAR_RELAXATION);
			else relaxation = Configuration.getDouble(prefix+"."+PAR_RELAXATION, Double.parseDouble(Species.data[index][Species.RELAXATION]));

		}
		
		// sanity checks
		if (period < 0) throw new IllegalParameterException ("period","value < 0");
		if (pulse_duration < 0) throw new IllegalParameterException ("pulse_duration","value < 0");
		if (prep < 0) throw new IllegalParameterException ("prep","value < 0");
		if (inert < 0) throw new IllegalParameterException ("inert","value < 0");
		if (!model.equals("ADVANCE") && !model.equals("DELAY") && !model.equals("MAS") && !model.equals("ADAPTIVE")) 
			throw new IllegalParameterException ("model","acceptable values are ADVANCE,DELAY,MAS,ADAPTIVE");
		if (model.equals("MAS")) {
			if (b < 0) throw new IllegalParameterException ("b","value < 0");
			if (epsilon < 0.0d || epsilon > 1.0d) throw new IllegalParameterException ("epsilon","value out of [0,1]");
		}
		if (model.equals("ADAPTIVE")) {
			if (max < period) throw new IllegalParameterException ("max","value < period");
			if (min > period) throw new IllegalParameterException ("min","value > period");
			if (min < 0) throw new IllegalParameterException ("min","value < 0");
		}		

		initSchedule();
	}

	/* 
	 */
	public Object clone()
	{
		Firefly f = null;
		try { f = (Firefly) super.clone(); }
		catch( CloneNotSupportedException e ) {} // never happens
		f.initSchedule();
		return f;
	}

	/**
	 * Run just after {@link #Firefly(String) contructor} or {@link #clone()}, 
	 * here we must set up a first event delivered to self:  
	 * because execution took place in a node when an event is delivered, 
	 * without this such first event, this node won't start.<p> 
	 */
	public abstract void initSchedule();
	
	/**
	 * Do Emission of a Firefly Flash.<p>
	 * Here you implement a 'firefly flash' so you should keep in mind some characteristics of 
	 * that flash: <p><ol>
	 * <li><b>direction</b>: firefly can decide the direction to emit light. usually is a 180° cone.<p>
	 * <li><b>sensibility</b>: Firefly's eyes able to sense flashes within 3 meters circa (Buck).<p>
	 * <li><b>visibility</b>: vegetation (...) can create obstacles to line-of-sight.<p>
	 * </ol>
	 */
	public abstract void doFlash();

	/**
	 * Performed by Firefly when she senses a light impulse.<p>
	 * In addition to the list of things (see {@link #doFlash()}) a 
	 * good implementation should address these:<p><ol>
	 * <li>Transmission Delay: Flashes travels at light speed in nature, 
	 * but messages do not in networks.
	 * <li>Model: The models (ADVANCE,DELAY,MAS,ADAPTIVE...) are applied here: 
	 * choice how to react to the event. 
	 * </ol><p>
	 * The parameters are inspired by the signature of 
	 * {@link peersim.edsim.EDProtocol#processEvent(Node, int, Object)}
	 * because very probably this method is called by it.<p>
	 */
	public abstract void receive(Node my_node, int my_pid, Object received_event);
	
}
