/*
 * Driver_SampleP2P.java
 * @version 1.0
 *
 * Created on April 15, 2008, 1:00 PM
 *
 * @author Oliviu C. Ghica, Northwestern University
 */

package sidnet.stack.driver;

//import sidnet.deployment.FlightDrop;
import jist.swans.Constants; 

import jist.swans.misc.Mapper; 
import jist.swans.misc.Location; 
import jist.swans.misc.Util; 
import jist.swans.net.PacketLoss; 
import jist.swans.net.NetIp; 
import jist.swans.net.NetAddress; 

import sidnet.core.gui.SimGUI;
import sidnet.colorprofiles.ColorProfileAggregate;
import sidnet.core.gui.PanelContext;
import jist.runtime.JistAPI; 

import sidnet.core.simcontrol.SimManager;
import sidnet.core.interfaces.GPS;

import sidnet.stack.mac.Mac802_11e;
import jist.swans.mac.MacAddress; 

import jist.swans.radio.RadioFactory;
import jist.swans.radio.RadioInfo; 
import jist.swans.radio.RadioNoiseIndep; 
import jist.swans.field.Field; 
import jist.swans.field.Mobility; 
import jist.swans.field.Placement; 
import jist.swans.field.Spatial; 
import jist.swans.field.Fading; 
import jist.swans.field.PathLoss; 
import sidnet.stack.app.AppSampleP2P;
import sidnet.models.energy.batteries.Battery;
import sidnet.models.energy.batteries.IdealBattery;
import sidnet.models.energy.energyconsumption.EnergyManagementImpl;
import sidnet.models.energy.energyconsumption.EnergyModel;
import sidnet.models.energy.energyconsumption.EnergyModelImpl;
import sidnet.core.gui.GroupSelectionTool;

import sidnet.core.misc.GPSimpl;
import sidnet.core.misc.Location2D;
import sidnet.core.misc.LocationContext;
import sidnet.core.misc.Node;
import sidnet.models.senseable.phenomena.GenericDynamicPhenomenon;
import sidnet.models.senseable.phenomena.PhenomenaLayerInterface;
import sidnet.utilityviews.statscollector.StatsCollector;
import sidnet.stack.routing.HeartbeatProtocol;
import sidnet.stack.routing.ShortestGeographicalPathRouting;
import sidnet.utilityviews.EnergyMap;

public class Driver_SampleP2P{
   
    public static int nodes, fieldLength, time;
      
    public static void main(String[] args)
    {
        /* Command line arguments is the best way to configure run-time parameters, for now */
        if(args.length < 3)
        {
           System.out.println("syntax: swans driver.Driver_SampleP2P <nodes> <length> <time>");
           System.out.println("    eg: swans driver.Driver_SampleP2P 5 100 50000");
           return;
        }
        
        System.out.println("Driver initialization started ... ");
        
        /* Parse command line arguments */
        nodes  = Integer.parseInt(args[0]);
        fieldLength = Integer.parseInt(args[1]);
        time   = Integer.parseInt(args[2]);
        
        /* Computing some statistics */
        float density = nodes / (float)(fieldLength/1000.0 * fieldLength/1000.0);
        
        System.out.println("nodes   = "+nodes);
        System.out.println("size    = "+fieldLength+" x "+fieldLength);
        System.out.println("time    = "+time+" seconds"); 
        System.out.print("Creating simulation nodes ... ");
        
        /* Create the simulation */
        Field f = createSim(nodes, fieldLength);
        
        System.out.println("Average density = "+f.computeDensity()*1000*1000+"/km^2");
        System.out.println("Average sensing = "+f.computeAvgConnectivity(true));
        System.out.println("Average receive = "+f.computeAvgConnectivity(false));
        
        // Indicates WHEN the JiST simulation should self-terminate (automatically)
        JistAPI.endAt(time * Constants.SECOND);
        
        System.out.println("Driver initialization complete!");     
    } 
    
  
  /**
   * Initialize simulation environment and field
   *
   * @param nodes number of nodes
   * @param length length of field
   * @return simulation field
   */
  public static Field createSim(int nodes, int length)
  {
    System.out.println("[Driver_SampleP2P] : createSim()");  
      
    /* Launch the SIDnet main graphical interface */       
    SimGUI simGUI = new SimGUI();
    
    /* Configure and start the simulation manager. Hook up control for GUI panels*/
    SimManager simManager = new SimManager(simGUI.getSimControlPanel(), null, SimManager.DEMO);
    simManager.managePanelContext(simGUI.getSensorsPanelContext());
    simManager.managePanelContext(simGUI.getUtilityPanelContext1());
    simManager.managePanelContext(simGUI.getUtilityPanelContext2());
    
    /* Configure the SWANS */
    
    /* Nodes deployment: random */
    Location.Location2D bounds = new Location.Location2D(length, length);
    Placement placement = new Placement.Random(bounds);
    
    /* Nodes mobility: static */
    Mobility mobility   = new Mobility.Static();
    
    /* Spatial configuration */
    Spatial spatial = new Spatial.HierGrid(bounds, 5);
    Fading fading = new Fading.None();
    PathLoss pathloss = new PathLoss.FreeSpace();
    Field field = new Field(spatial, fading, pathloss, mobility, Constants.PROPAGATION_LIMIT_DEFAULT);

    /* Build up the networking stack: APP, NETWORK, MAC */
    Mapper protMap = new Mapper(Constants.NET_PROTOCOL_MAX);
    protMap.mapToNext(Constants.NET_PROTOCOL_HEARTBEAT);
    protMap.mapToNext(sidnet.jist.swans.Constants.NET_PROTOCOL_INDEX_1);
    
    /* We'll assume no packet loss due to "random" conditions. Packets may still be lost due to collisions though */
    PacketLoss pl = new PacketLoss.Zero();
  
    /* ******************************************
     * Create the SIDnet-specific simulation environment  *
     * ******************************************/
    
    /* Creating the SIDnet nodes */
    Node[] myNode = new Node[nodes];
    LocationContext fieldContext = new LocationContext(length, length);
    
     /* StatsCollector Hook-up - to allow you to see a quick-stat including elapsed time, number of packet lost, and so on*/
    StatsCollector statistics = new StatsCollector(myNode, length, 0, 30 * Constants.SECOND);
    statistics.monitor("Time", StatsCollector.ITEM.TIME);
    statistics.monitor("DataPacketsSent", StatsCollector.ITEM.DATA_PACKETS_SENT_COUNT);
    statistics.monitor("DataPacketsReceived", StatsCollector.ITEM.DATA_PACKETS_RECEIVED_COUNT);
    statistics.monitor("DataPacketsReceivedPercentage", StatsCollector.ITEM.DATA_PACKETS_RECEIVED_PERCENTAGE);
    statistics.monitor("MaximumDataDeliveryLatency", StatsCollector.ITEM.DATA_PACKETS_MAXIMUM_DELIVERY_LATENCY);
    simManager.register(statistics, simGUI.getUtilityPanelContext2()); // Indicate where do you want this to show up on the GUI
    
    /* Create the sensor nodes. Initialize each node's data and network stack */
    for(int i=0; i<nodes; i++)
       myNode[i] = createNode(i, field, placement, protMap, 
           RadioFactory.createRadioInfoDefault802154(), pl, pl, simGUI.getSensorsPanelContext(), fieldContext, simManager, new IdealBattery(IdealBattery.INF, 3), statistics);   
    
    /* Configuring the sensorial layer - give the node something to sense, measure */
    PhenomenaLayerInterface phenomenaLayer = new GenericDynamicPhenomenon();
    simManager.register(phenomenaLayer,simGUI.getSensorsPanelContext());
    /* All the nodes will measure the same environment in this case, but this is not a limitation */
    for (int i = 0; i < nodes; i++)
         myNode[i].addSensor(phenomenaLayer);
    
    /* Allow simManager to handle nodes' GUI */
    simManager.register(myNode);
          
    /* EnergyMap hookup - give an overall view of the energy levels in the networks */
    EnergyMap energyMap = new EnergyMap(myNode); 
    simManager.register(energyMap, simGUI.getUtilityPanelContext1()); // Indicate where do you want this to show up on the GUI
    
    /* Add GroupInteraction capability - if you may want to be able to select a group of nodes */
    GroupSelectionTool gst = new GroupSelectionTool(myNode);
    simManager.register(gst, simGUI.getSensorsPanelContext());
    myNode[0].getNodeGUI().setGroupSelectionTool(gst); 
   
    /* Commence the simulation */
    simManager.run(null);
    
    System.out.println("Simulation Started");
    
    return field;
  } 
  
   /**
   * Configures each node representation and network stack
   *
   * @param int id      a numerical value to represent the id of a node. Will correspond to the IP address representation
   * @param Field       the field properties
   * @param Placement   information regarding positions length of field
   * @param Mapper      network stack mapper
    *@param RadioInfo.RadioInfoShared   configuration of the radio
    *@param plIn        property of the PacketLoss for incoming data packet
    *@param plOut       property of the PacketLoss for outgoing data packet
    *@param hostPanelContext    the context of the panel this node will be drawn
    *@param fieldContext        the context of the actual field this node is in (for GPS)
    *@param simControl          handle to the simulation manager
    *@param Battery     indicate the battery that will power this particular node
    *@param StatsCollector the statistical collector tool
   */
    public static Node createNode(int id,
                                  Field field,
                                  Placement placement,
                                  Mapper protMap,
                                  RadioInfo radioInfoShared,
                                  PacketLoss plIn,
                                  PacketLoss plOut, 
                                  PanelContext hostPanelContext,
                                  LocationContext fieldContext,
                                  SimManager simControl,
                                  Battery battery,
                                  StatsCollector stats)
  {
    /* create entities */
    Location nextLocation = placement.getNextLocation();
        
    /* Create an individual battery, since no two nodes can be powered by the same battery. The specs of the battery are the same though */
    Battery individualBattery = new IdealBattery(battery.getCapacity(), battery.getVoltage());

    /* Set the battery and the energy consumption profile */
    EnergyModel energyModel = new EnergyModelImpl(null, battery);
    energyModel.setID(id);

    /* Create the node and nodeGUI interface for this node */
    Node node = new Node(id, new EnergyManagementImpl(energyModel, battery), hostPanelContext, fieldContext, new ColorProfileAggregate(), simControl);
    RadioNoiseIndep radio = new RadioNoiseIndep(radioInfoShared);
    
    /* Put a 'GPS' (must to) to obtain the location information (for this assignment, for gaphical purposes only */
    GPS gps = new GPSimpl(new Location2D((int)nextLocation.getX(), (int)nextLocation.getY()));
    gps.configure(new LocationContext(fieldContext));
    node.setGPS(gps);
    
    /* Plug the Mac Layer */    
    Mac802_11e mac = new Mac802_11e(new MacAddress(id), radio.getRadioInfo(), energyModel, 0, node);
    
    /* Configure the Network Layer */
    NetIp net = new NetIp(new NetAddress(id), protMap, plIn, plOut);
    
    /* Configure the Routing Layer */
    HeartbeatProtocol heartbeatProtocol = new HeartbeatProtocol(net.getAddress(), node, hostPanelContext, 30 * Constants.MINUTE);
    ShortestGeographicalPathRouting shortestGeographicalPathRouting = new ShortestGeographicalPathRouting(node);
    node.setIP(net.getAddress());
    
    /* Configure the Application Layer */
    AppSampleP2P app = new AppSampleP2P(node, sidnet.jist.swans.Constants.NET_PROTOCOL_INDEX_1, stats);
    
    
    /* Hook up the network stack's layers */
    heartbeatProtocol.setNetEntity(net.getProxy());
    shortestGeographicalPathRouting.setNetEntity(net.getProxy());
    
    field.addRadio(radio.getRadioInfo(), radio.getProxy(), nextLocation);
    field.startMobility(radio.getRadioInfo().getId());
    radio.setFieldEntity(field.getProxy());
    radio.setMacEntity(mac.getProxy());
    mac.setRadioEntity(radio.getProxy());
    byte intId = net.addInterface(mac.getProxy());
    mac.setNetEntity(net.getProxy(), intId);
    
    shortestGeographicalPathRouting.setAppInterface(app.getAppProxy());
    
    net.setProtocolHandler(sidnet.jist.swans.Constants.NET_PROTOCOL_INDEX_1, shortestGeographicalPathRouting.getProxy());
    net.setProtocolHandler(Constants.NET_PROTOCOL_HEARTBEAT, heartbeatProtocol.getProxy());
      
    app.setNetEntity(net.getProxy());
    
    /* Here we actually start this node's application layer execution. It is important to observe
       that we don't actually call the app's run() method directly, but through its proxy, which allows JiST engine to actually decide when this call will
       be actually made.*/
    app.getAppProxy().run(null);
    
    return node;
  } 
 
}
