/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

import java.util.HashMap;
import java.util.Set;

import org.omg.dds.core.Duration;
import org.omg.dds.core.InstanceHandle;
import org.omg.dds.core.ServiceEnvironment;
import org.omg.dds.core.WaitSet;
import org.omg.dds.domain.DomainParticipant;
import org.omg.dds.domain.DomainParticipantFactory;
import org.omg.dds.sub.DataReader;
import org.omg.dds.sub.ReadCondition;
import org.omg.dds.sub.Subscriber;
import org.omg.dds.sub.Subscriber.DataState;
import org.omg.dds.topic.ParticipantBuiltinTopicData;

/**
 * This examples application monitors the number of nodes that participate in a
 * DDS domain.
 *
 */
public class BuildInTopicsDataSubscriber {
    public static void main(String[] args) {

        try {
            boolean automatic = true;
            if (args.length > 1)
            {
                automatic = (args[0].equals("true"));
            }
            int domainId = 0;
            String hostName;
            /*
             * Select DDS implementation and initialize DDS ServiceEnvironment
             */
            System.setProperty(
                    ServiceEnvironment.IMPLEMENTATION_CLASS_NAME_PROPERTY,
                    "org.opensplice.dds.core.OsplServiceEnvironment");
            ServiceEnvironment env = ServiceEnvironment
                    .createInstance(BuildInTopicsDataSubscriber.class.getClassLoader());

            /* Resolve the singleton DomainParticipantFactory. */
            DomainParticipantFactory dpf = DomainParticipantFactory.getInstance(env);
            DomainParticipant participant = null;

            /* Optionally use the domainId provided as input parameter. */
            /* Create a DomainParticipant with default QoS and no listener. */
            if (args.length > 0) {
                try {
                    domainId = Integer.parseInt(args[0]);
                    participant = dpf.createParticipant(domainId);
                } catch (NumberFormatException e) {
                    System.err.println("Argument must be an integer");
                    participant = dpf.createParticipant();
                }
            } else {
                participant = dpf.createParticipant();
            }

            /* Check whether the participant has been created. */
            if (participant == null) {
                System.err.println("Could not connect to domain '" + domainId
                        + "'. Is OpenSplice running?");
                return;
            } else {
                System.out.println("Connected to domain '" + participant.getDomainId() + "'.");
            }

            /* Resolve the built-in Subscriber. */
            Subscriber builtinSubscriber = participant.getBuiltinSubscriber();

            /* Lookup the DataReader for the DCPSParticipant built-in Topic. */
            DataReader<ParticipantBuiltinTopicData> reader = builtinSubscriber.lookupDataReader("DCPSParticipant");

            System.out.print("Waiting for historical data... ");

            /* Make sure all historical data is delivered in the DataReader. */
            reader.waitForHistoricalData(Duration.infiniteDuration(env));

            System.out.println("done");

            /* Allocate a new Waitset */
            WaitSet waitset = WaitSet.newWaitSet(env);

            /* Create a new ReadCondition for the reader that matches all samples. */
            DataState ds = builtinSubscriber.createDataState().withAnyInstanceState().withAnySampleState().withAnyViewState();
            ReadCondition<?> condition = reader.createReadCondition(ds);

            /* Attach the condition to the waitset. */
            waitset.attachCondition(condition);


            /* Allocate a map to store node information later on. */
            /* The key of the map is the id of the node and the value is the */
            /* number of active participants on that node. */
            HashMap<Integer, Integer> nodes = new HashMap<Integer, Integer>();

            /* Allocate a map to store node information later on. */
            /* The key of the map is the id of the node and the value is the */
            /* name of the node. */
            HashMap<Integer, String> nodeNames = new HashMap<Integer, String>();

            /*
             * Block the current thread until the attached condition becomes true or
             * the user interrupts.
             */
            waitset.waitForConditions(Duration.infiniteDuration(env));
            boolean done = false;
            Set<InstanceHandle> participants = null;
            /* Initialise the number of participants for a node. */
            int participantCount = 0;

            /* Continue processing until interrupted. */
            while (!done) {

                /* In Java5 we cannot use the reader to read ParticipantBuiltinTopicData samples
                 * so we need to use the API call getDiscoveredParticipants in combination with getDiscoveredParticipantData
                 */
                participants = participant.getDiscoveredParticipants();
                /* Walk over each participant and request its userdata */
                for (InstanceHandle ih : participants) {
                    if (ih != null) {
                        ParticipantBuiltinTopicData data = participant.getDiscoveredParticipantData(ih);
                        if (data != null) {
                            /* Resolve the node identification. */
                            int nodeId = data.getKey().getValue()[0];

                            /* Check if we saw a participant for the node before. */
                            if (nodes.containsKey(nodeId)) {
                                /*
                                 * Resolve the actual number of participants on the
                                 * node.
                                 */
                                participantCount = nodes.get(nodeId);
                            }
                            /*
                             * The splicedaemon publishes the host-name in the
                             * user_data field.
                             */
                            if (data.getUserData().getValue().length != 0) {
                                hostName = new String(data.getUserData().getValue());
                                if (!hostName.startsWith("<TunerService>"))
                                {
                                  nodeNames.put(nodeId, hostName);
                                }
                                else
                                {
                                  hostName = null;
                                }
                            } else {
                                hostName = null;
                            }

                            /* Increase the number of participants. */
                            participantCount++;

                            /* Update the number of participants for the node. */
                            nodes.put(nodeId, participantCount);

                            /* If it's the first participant, report the node is up. */
                            if (participantCount == 1) {
                                System.out.println("Node '" + nodeId
                                        + "' started (Total nodes running: "
                                        + nodes.size() + ")");
                            }
                            if (hostName != null) {
                                System.out.println("Hostname for node '" + nodeId
                                        + "' is '" + hostName + "'.");
                            }
                            /* check if a participant has left */
                            if (participantCount > participants.size()) {
                                /* Decrease the number of participants. */
                                participantCount--;

                                /*
                                 * If no more participants exist, report the node is
                                 * down.
                                 */
                                if (participantCount == 0) {
                                    hostName = nodeNames.get(nodeId);
                                    nodeNames.remove(nodeId);
                                    nodes.remove(nodeId);

                                    if (hostName != null) {
                                        System.out.println("Node " + nodeId + " ("
                                                + hostName
                                                + ") stopped (Total nodes running: "
                                                + nodes.size() + ")");
                                    } else {
                                        System.out.println("Node " + nodeId
                                                + " stopped (Total nodes running: "
                                                + nodes.size() + ")");
                                    }
                                } else if (participantCount > 0) {
                                    nodes.put(nodeId, participantCount);
                                }
                            }
                         }
                    }

                }

                if (!automatic) {
                      /* Block the current thread until the attached condition becomes
                       * true or the user interrupts.
                       */
                      System.out.println("=== [BuiltInTopicsDataSubscriber] Waiting ... ");
                      waitset.waitForConditions(Duration.infiniteDuration(env));
                      done = true;
                } else {
                    done = true;
                }
            }
        } catch(Exception e) {
            System.out.println("Error occured: " + e.getMessage());
            e.printStackTrace();
        }

    }

}
