/*****************************************************************
 JADE - Java Agent DEvelopment Framework is a framework to develop 
 multi-agent systems in compliance with the FIPA specifications.
 Copyright (C) 2000 CSELT S.p.A. 

 GNU Lesser General Public License

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation, 
 version 2.1 of the License. 

 This library 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the
 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA  02111-1307, USA.
 *****************************************************************/

package jade.core.nodeMonitoring;

//#APIDOC_EXCLUDE_FILE
// Take care that the DOTNET build file (dotnet.xml) uses this file (it is copied just after the preprocessor excluded it)
//#J2ME_EXCLUDE_FILE

import jade.core.Node;
import jade.util.Logger;

import java.io.IOException;
import java.nio.ByteBuffer;
//#DOTNET_EXCLUDE_BEGIN
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;

//#DOTNET_EXCLUDE_END

/*#DOTNET_INCLUDE_BEGIN
 import System.Net.*;
 import System.Net.Sockets.*;
 #DOTNET_INCLUDE_END*/

/**
 * The <code>UDPMonitorClient</code> sends UDP ping messages 
 * in a specified interval to the main container.
 *
 * @author Roland Mungenast - Profactor
 * @since JADE 3.3
 */
class UDPMonitorClient {

	private boolean running = false;
	private boolean terminating = false;
	private boolean sendTerminationFlag = false;

	//#DOTNET_EXCLUDE_BEGIN
	private DatagramChannel channel;

	//#DOTNET_EXCLUDE_END

	/*#DOTNET_INCLUDE_BEGIN
	 private UdpClient channel;
	 private IPEndPoint receivePoint;
	 #DOTNET_INCLUDE_END*/

	private String serverHost;
	private int serverPort;
	private ByteBuffer ping;
	private int pingDelay;
	private Node node;
	private long key;
	private Thread sender;

	private Logger logger;

	/**
	 * Private class which sends ping messages in regular time intervals
	 * 
	 * @author Roland Mungenast - Profactor
	 * @since JADE 3.3
	 * @author Federico Pieri - ERXA
	 * @since JADE 3.3.NET
	 */
	private class Sender implements Runnable {

		public void run() {

			while (running) {
				updatePing();
				//#DOTNET_EXCLUDE_BEGIN
				try {
					try {
						channel.send(ping, new InetSocketAddress(serverHost, serverPort));
					} 
					catch (Exception e) {
						logger.log(Logger.WARNING, "Error sending UDP ping message to "+serverHost+":"+serverPort+" for node " + node.getName());
					} 
					Thread.sleep(pingDelay - 5);
				}
				catch (InterruptedException e) {
					// ignore --> the ping with the termination flag has to be sent immediately
				}
				//#DOTNET_EXCLUDE_END
				/*#DOTNET_INCLUDE_BEGIN
				try {
				channel.Send(ping.getUByte(), ping.capacity(), serverHost, serverPort);
				Thread.sleep(pingDelay - 5);
				} 
				catch (Exception e) 
				{
				logger.log(Logger.WARNING,"Error sending UDP ping message to "+serverHost+":"+serverPort+" for node " + node.getName());
				}
				#DOTNET_INCLUDE_END*/
			}

			try {
				//#DOTNET_EXCLUDE_BEGIN
				channel.close();
			} catch (IOException e) {
				//#DOTNET_EXCLUDE_END
				/*#DOTNET_INCLUDE_BEGIN
				 channel.Close();
				 } 
				 catch (Exception e) 
				 {
				 #DOTNET_INCLUDE_END*/
				if (logger.isLoggable(Logger.FINER))
					logger.log(Logger.FINER, "Error closing UDP channel");
			}
		}

		private void updatePing() {
			String nodeName = node.getName();
			ping = ByteBuffer.allocate(4 + nodeName.length() + 1);
			ping.position(0);
			ping.putInt(nodeName.length());
			ping.put(nodeName.getBytes());

			if (terminating && sendTerminationFlag) {
				ping.put((byte) 1);
			} else {
				ping.put((byte) 0);
			}
			
			if (terminating) {
				running = false;
			}

			ping.position(0);
		}
	}

	/**
	 * Constructor
	 * @param node Node for which to send ping messages
	 * @param serverHost hostname of the server
	 * @param serverPort port on which the server is listening for ping messages
	 */
	public UDPMonitorClient(Node node, String serverHost, int serverPort, int pingDelay, long key) {
		logger = Logger.getMyLogger(this.getClass().getName());
		this.node = node;
		this.serverHost = serverHost;
		this.serverPort = serverPort;
		this.pingDelay = pingDelay;
		this.key = key;
	}

	public String getServerHost() {
		return serverHost;
	}
	
	public long getKey() {
		return key;
	}
	
	void setPingDelay(int delay) {
		pingDelay = delay;
		sender.interrupt();
	}
	
	/**
	 * Start sending UDP ping messages to the node failure server
	 * @throws IOException if the 
	 */
	public void start() throws IOException {
		//#DOTNET_EXCLUDE_BEGIN
		channel = DatagramChannel.open();
		//#DOTNET_EXCLUDE_END
		/*#DOTNET_INCLUDE_BEGIN
		 channel = new UdpClient();
		 #DOTNET_INCLUDE_END*/
		running = true;
		sender = new Thread(new Sender());
		sender.start();

		if (logger.isLoggable(Logger.CONFIG))
			logger.log(Logger.CONFIG, "UDP monitoring client started.");
	}

	/**
	 * Stop sending UDP ping messages
	 */
	public void stop(boolean sendTerminationFlag) {
		terminating = true;
		this.sendTerminationFlag = sendTerminationFlag;
		sender.interrupt();

		if (logger.isLoggable(Logger.CONFIG))
			logger.log(Logger.CONFIG, "UDP monitoring client stopped.");

	}
	
	boolean isActive() {
		return sender != null && sender.isAlive();
	}
}
