/*
 * Copyright 2006-2007 National Institute of Advanced Industrial Science
 * and Technology (AIST), and contributors.
 *
 * 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.
 */

package ow.tool.msgcounter;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import ow.messaging.MessagingAddress;
import ow.messaging.Tag;
import ow.stat.MessagingCallback;
import ow.stat.MessagingCollector;
import ow.stat.StatConfiguration;
import ow.stat.StatFactory;

/**
 * Message Counter.
 * Instantiated by the {@link ow.tool.msgcounter.Main Main} class.
 */
public class MessageCounter implements MessagingCallback {
	private final StatConfiguration config;
	private MessagingCollector statCollector = null;
	private final Map<Integer,NumberAndLengthCounter> totalCounterMap =
		new HashMap<Integer,NumberAndLengthCounter>();
	private final Map<Integer,NumberAndLengthCounter> lapCounterMap =
		new HashMap<Integer,NumberAndLengthCounter>();
	private final SortedSet<Integer> tagSet = new TreeSet<Integer>();

	private final Set<MessagingAddress> nodeSet = new HashSet<MessagingAddress>();
	private int maxNumNode = 0;

	public MessageCounter(String transport, boolean noUPnP) {
		this.config = StatFactory.getDefaultConfiguration();

		if (transport != null) {
			this.config.setMessagingTransport(transport);
		}
		this.config.setDoUPnPNATTraversal(!noUPnP);
	}

	public void start() throws Exception {
		if (this.statCollector == null) {
			this.statCollector = StatFactory.getMessagingCollector(this.config);
			this.statCollector.start(this);
		}
	}

	public void stop() {
		if (this.statCollector != null) {
			this.statCollector.stop();
			this.statCollector = null;
		}
	}

	public synchronized void clear() {
		this.lapCounterMap.clear();
		this.totalCounterMap.clear();
		this.tagSet.clear();
	}

	/**
	 * Implements {@link MessagingCallback#messageSent(MessagingAddress, MessagingAddress, int)
	 * CallbackOnNodeStatus#messageSent()}.
	 */
	public synchronized void messageSent(MessagingAddress source, MessagingAddress target, int tag, int len) {
		NumberAndLengthCounter c = null;

		// lap
		c = this.lapCounterMap.get(tag);
		if (c == null) {
			c = new NumberAndLengthCounter();
			this.lapCounterMap.put(tag, c);
			this.tagSet.add(tag);
		}
		c.addLength(len);

		// total
		c = this.totalCounterMap.get(tag);
		if (c == null) {
			c = new NumberAndLengthCounter();
			this.totalCounterMap.put(tag, c);
			this.tagSet.add(tag);
		}
		c.addLength(len);

		// node set
		this.nodeSet.add(source);

		int size = this.nodeSet.size();
		if (size > this.maxNumNode) this.maxNumNode = size;
	}

	/**
	 * Implements {@link MessagingCallback#nodeFailed(MessagingAddress)
	 * CallbackOnNodeStatus#deleteNode()}.
	 */
	public void nodeFailed(MessagingAddress node) {
		this.nodeSet.remove(node);
	}

	public synchronized String getCountsString() {
		StringBuilder sb = new StringBuilder();
		int sumNumber, sumLength;

		sb.append("\n");

		// lap
		sb.append("Message count & length in byte (lap):\n");

		sumNumber = sumLength = 0;
		for (Integer i: this.tagSet) {
			NumberAndLengthCounter c = this.lapCounterMap.get(i);
			if (c == null) continue;

			int number = c.getNumber();
			int length = c.getLength();

			sumNumber += number;
			sumLength += length;

			sb.append(Tag.getNameByNumber(i));
			sb.append(", ");
			sb.append(number);
			sb.append(", ");
			sb.append(length);
			sb.append("\n");
		}

		sb.append("message count, length and # of nodes (lap), ");
		sb.append(sumNumber);
		sb.append(", ");
		sb.append(sumLength);
		sb.append(", ");
		sb.append(this.nodeSet.size());
		sb.append("\n\n");

		this.lapCounterMap.clear();

		// total
		sb.append("Message count & length in byte (total):\n");

		sumNumber = sumLength = 0;
		for (Integer i: this.tagSet) {
			NumberAndLengthCounter c = this.totalCounterMap.get(i);
			int number = c.getNumber();
			int length = c.getLength();

			sumNumber += number;
			sumLength += length;

			sb.append(Tag.getNameByNumber(i));
			sb.append(", ");
			sb.append(number);
			sb.append(", ");
			sb.append(length);
			sb.append("\n");
		}

		sb.append("message count, length and # of nodes (total), ");
		sb.append(sumNumber);
		sb.append(", ");
		sb.append(sumLength);
		sb.append(", ");
		sb.append(this.maxNumNode);
		sb.append("\n\n");

		return sb.toString();
	}
}
