/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2004-2007 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * 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; either version 2.1 of the License, or any later
 * version.
 *
 * 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.
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.discovery.internal.enroller;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.discovery.base.comm.DiscEvent;
import org.ow2.jonas.discovery.internal.utils.DiscoveryHelper;
import org.ow2.jonas.lib.management.domain.DomainMonitor;
import org.ow2.jonas.lib.util.Log;

/**
 * @author <a href="mailto:Takoua.Abdellatif@inria.fr">Takoua Abdellatif </a>
 * @version 1.0
 */
public class DiscoveryListener implements Runnable {
    /**
     * Management notification type for <i>discovery</i> events
     */
    public static final String DISCOVERY_TYPE = "jonas.management.discovery";

    private static int RECEIVE_BUFFER_SIZE = 1024;
    /**
     * My enroller's listening port
     */
    private int             port;
    /**
     * My enroller's multicast IP address
     */
    private InetAddress     groupAddress;
    /**
     * Time-to-live for multicatst packets
     */
    private int             ttl = 1; // why 1 ??
    /**
     * Used to receive multicasted discovery events
     */
    private MulticastSocket multicastSocket;
    /**
     * Socket state
     */
    private boolean         notStopped          = true;
    private static Logger logger = Log.getLogger(Log.JONAS_DISCOVERY_PREFIX);

    /**
     * Constructs a DiscoveryListener associated to the Enroller
     * @param enroller Enroller to which this thread is associated
     */
    public DiscoveryListener(Enroller enroller) {
        this.port = enroller.getListeningPort();
        try {
            this.groupAddress = InetAddress.getByName(enroller.getListeningIp());
            this.ttl = enroller.getTimeToLive();
        } catch (UnknownHostException e) {
            logger.log(BasicLevel.ERROR, e);
        }
    }

    /**
     * Creates a MulticastSocket and joins the group of multicas host
     * identified by the InetAddress <code>groupAddress</code>
      */
    private void join() {
        try {
            multicastSocket = new MulticastSocket(port);
            multicastSocket.setTimeToLive(ttl);
            multicastSocket.joinGroup(groupAddress);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "multicast ip address is "
                        + groupAddress);
                logger.log(BasicLevel.DEBUG, "multicast port is " + port);
            }
        } catch (IOException e) {
            logger.log(BasicLevel.ERROR, "io problem");
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private DatagramPacket getDatagram(int length) {
        return new DatagramPacket(new byte[length], length);
    }

    public void run() {
        // Join the group in order to receive multicast messages
        join();
        try {
            while (notStopped) {
                DatagramPacket datagram = null;
                Object objReceived = null;
                try {
                    datagram = getDatagram(RECEIVE_BUFFER_SIZE);
                    multicastSocket.receive(datagram);
                    objReceived = DiscoveryHelper.bytesToObject(datagram.getData());
                } catch (RuntimeException e) {
                    logger.log(BasicLevel.DEBUG, "Host received other packet than DataGramSocket packet. That caused following Exception: \n"+e);
                    objReceived = null;
                }catch (IOException e) {
                	logger.log(BasicLevel.DEBUG, "Host received other packet than DataGramSocket packet. That caused following Exception: \n"+e);
                    objReceived = null;
				}
                if (objReceived != null) {
                    if (objReceived instanceof DiscEvent) {
                        // Treat DiscEvents
                        DiscEvent event = (DiscEvent) objReceived;
                        // Trust the datagram packet for source address instead of the message.
                        event.setSourceAddress(datagram.getAddress().getHostAddress());
                        DomainMonitor dm = DomainMonitor.getInstance();
                        if (dm == null) {
                            logger.log(BasicLevel.WARN, "No DomainMonitor");
                        } else {
                            dm.discoveryNotification(event);
                        }
                    }
                }
                datagram = null;
            }
        } catch (ClassNotFoundException e) {
            logger.log(BasicLevel.DEBUG, "Following exception ocuured in DiscoveryListener: "+e);
        }
    }


    public void stopListener() {
        notStopped = false;
    }

}