/*
 * MultiTreeRouting.java
 *
 * Created on March 20, 2007, 11:39 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package sidnet.stack.routing;


import sidnet.core.gui.PanelContext;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.net.NetAddress;
import jist.swans.net.NetInterface;
import jist.swans.net.NetMessage;
import jist.swans.mac.MacAddress;
import jist.swans.Constants;
import sidnet.colorprofiles.ColorProfileAggregate;
import sidnet.colorprofiles.ColorProfileGeneric;
import sidnet.core.misc.NodeEntry;

import sidnet.messages.MessageHeartbeat;


import sidnet.core.gui.*;
import java.util.Random;

import jist.runtime.JistAPI;
import jist.swans.app.AppInterface;
import sidnet.jist.swans.RouteInterface;
import sidnet.core.misc.Node;

public class HeartbeatProtocol implements RouteInterface.HeartbeatProtocol {

    private NetAddress localAddr;
    
    // entity hookup 
    /** Network entity. */
    private NetInterface netEntity;
    
    /** Self-referencing proxy entity. */
    private RouteInterface.HeartbeatProtocol self; 
    
    private AppInterface appInterface;
    
    private Node myNode;
    
    private RoutingTable routingTable;
    
    private boolean wakeAndBeatStarted = false;
    private boolean unregistered = false;
    
    private long sleepPeriod;
    
    /** Creates a new instance of HeartbeatProtocol */
    public HeartbeatProtocol(NetAddress localAddr, Node myNode, PanelContext hostPanelContext, long sleepPeriod) {
        this.localAddr = localAddr;
        this.myNode = myNode;
      
        self = (RouteInterface.HeartbeatProtocol)JistAPI.proxy(this, RouteInterface.HeartbeatProtocol.class);
        this.sleepPeriod = sleepPeriod;
    }
    
    public void netQueueFull(Message msg, MacAddress nextHopMac)
    {
        System.out.println("WARNING: Net Queue full");
    }
    
     public void peek(NetMessage msg, MacAddress lastHopMac, MessageAnno anno) {
        // DO NOTHING
     }
    
     public void send(NetMessage msg, MessageAnno anno) 
     {
        // DO NOTHING
     }
     
    public synchronized void wakeAndBeat(long sleepPeriod, boolean wakeAndBeatStarted)
    {
        if (myNode.getEnergyManagement().getBattery().getPercentageEnergyLevel() <= 0)
        {
            myNode.getNodeGUI().colorCode.mark(ColorProfileGeneric.DEAD, ColorProfileGeneric.FOREVER);
            myNode.getNodeGUI().repaint();
            return;
        }
        if (myNode.getEnergyManagement().getBattery().getPercentageEnergyLevel() < 5 && !unregistered)
        {
            unregistered = true;
            MessageHeartbeat messageHeartbeat = new MessageHeartbeat(MessageHeartbeat.UNREGISTER);
            myNode.getNodeGUI().colorCode.mark(ColorProfileGeneric.TRANSMIT, 200);
            myNode.getNodeGUI().repaint();
            JistAPI.sleepBlock(Constants.random.nextInt(100) * 100 * Constants.MILLI_SECOND); 
            netEntity.send(messageHeartbeat, NetAddress.ANY, Constants.NET_PROTOCOL_HEARTBEAT, Constants.NET_PRIORITY_D_BESTEFFORT, (byte)100, null);  // TTL 100'
            return;
        }
        if (!this.wakeAndBeatStarted || wakeAndBeatStarted)
        {
            wakeAndBeatStarted = true;
            JistAPI.sleepBlock(sleepPeriod);
            if (myNode.getEnergyManagement().getBattery().getPercentageEnergyLevel() < 20)
                sleepPeriod = 5 * Constants.MINUTE;
            ((RouteInterface.HeartbeatProtocol)self).wakeAndBeat(sleepPeriod, true);
        }
         
    }
     
    /* Receive a message from the network layer */
    public void receive(Message msg, NetAddress src, MacAddress lastHop, byte macId, NetAddress dst, byte priority, byte ttl, MessageAnno anno) 
    {   
        if (myNode.getEnergyManagement().getBattery().getPercentageEnergyLevel() <= 1)
        {
            myNode.getNodeGUI().colorCode.mark(ColorProfileGeneric.DEAD, ColorProfileGeneric.FOREVER);
            myNode.getNodeGUI().repaint();
            return;
        }
        
       // System.out.println("Node " + myNode.getID() + " received message from " + src); 
        
        // Pass all the non-heartbeat specific messages straight to the App layer
        if (! (msg instanceof MessageHeartbeat))
        {
            System.out.println("<WARNING>[HeartbeatProtocol] : receiving packets that are not primarely designed for the Heartbeat Protocol. Make sure you transmit your message to the proper routing algorithm (implementation)");
            return;
        }
         
        NodeEntry newEntry = new NodeEntry( lastHop,
                                            src,
                                            ((MessageHeartbeat)msg).getNCS_Location()
                                          );
        if (((MessageHeartbeat)msg).isUnregistering())
            myNode.neighboursList.remove(src);
        else
            if (!myNode.neighboursList.exist(lastHop))
                myNode.neighboursList.add(src, newEntry);
        myNode.getNodeGUI().colorCode.mark(ColorProfileAggregate.RECEIVE, 200);  
        myNode.getNodeGUI().repaint();
        if (!wakeAndBeatStarted)
            wakeAndBeat(sleepPeriod, wakeAndBeatStarted);
    }
    
    public RouteInterface.HeartbeatProtocol getProxy()
    {
        return self;
    }
   
    
    public void setNetEntity(NetInterface netEntity)
    {
        if(!JistAPI.isEntity(netEntity)) throw new IllegalArgumentException("expected entity");
        if(this.netEntity!=null) throw new IllegalStateException("net entity already set");
        
        this.netEntity = netEntity;
    }
   
    
   public void start()
   {
        //DO NOTHING
   }
}
