/*
 * Copyright (c) 2002-2004 David Keiichi Watanabe
 * davew@xlife.org
 *
 * Modified by (c) 2004-2006 heavy_baby
 * heavy_baby@users.sourceforge.jp
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package jp.sourceforge.cabos;

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.limegroup.gnutella.FileDesc;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.InsufficientDataException;
import com.limegroup.gnutella.ManagedConnection;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.downloader.ManagedDownloader;
import com.limegroup.gnutella.search.HostData;
import com.limegroup.gnutella.util.StringUtils;
import com.limegroup.gnutella.xml.LimeXMLDocument;
import com.limegroup.gnutella.xml.LimeXMLNames;

public class AqEvent {
	/* Instances */

	private static final Log LOG = LogFactory.getLog(AqEvent.class);

	private static Map stateCache = Collections.synchronizedMap(new HashMap());

	private static int currentLocalIndex = -1;

	/* State */

	private static final String kSep = "<aq/>";

	/* Event Codes */

	protected static final int kLWEventQueryResult = 1;

	protected static final int kLWEventConnectionInitialized = 2;

	protected static final int kLWEventConnectionClosed = 3;

	protected static final int kLWEventConnectionsUpdated = 4;

	protected static final int kLWEventAddDownload = 5;

	protected static final int kLWEventRemoveDownload = 6;

	protected static final int kLWEventUpdateDownloadStats = 7;

	protected static final int kLWEventDownloadsUpdated = 8;

	protected static final int kLWEventAddUpload = 9;

	protected static final int kLWEventRemoveUpload = 10;

	protected static final int kLWEventUpdateUploadStats = 11;

	protected static final int kLWEventUploadsUpdated = 12;

	protected static final int kLWEventAddSharedFile = 13;

	protected static final int kLWEventCoreConnected = 98;

	protected static final int kLWEventCoreInitialized = 99;

	/* Signal */

	private static String decodeValue(String string) {
		if (string == null)
			return "";
		if (string.length() == 0)
			return "";
		string = StringUtils.replace(string, "&amp;", "&");
		string = StringUtils.replace(string, "&lt;", "<");
		string = StringUtils.replace(string, "&gt;", ">");
		string = StringUtils.replace(string, "&apos;", "\'");
		string = StringUtils.replace(string, "&quot;", "\"");
		return string;
	}

	private static String fixValue(String string) {
		return (string != null) ? string : "";
	}

	protected static void signalEvent(int type) {
		signalEvent(type, null, null, null);
	}

	protected static void signalEvent(int type, Object obj) {
		signalEvent(type, obj, null, null);
	}

	protected static void signalEvent(int type, Object obj, Object obj1) {
		signalEvent(type, obj, obj1, null);
	}

	protected static void signalEvent(int type, Object obj, Object obj2,
			Object obj3) {
		StringBuffer event = new StringBuffer();

		switch (type) {

		/* Queries */

		case kLWEventQueryResult: {
			RemoteFileDesc rfd = (RemoteFileDesc) obj;
			HostData hd = (HostData) obj2;
			Set locs = (Set) obj3;
			GUID guid = new GUID(hd.getMessageGUID());

			/* index */

			String queryIndex = (String) AqEventHandler.queries.get(guid);
			String localIndex = String.valueOf(++currentLocalIndex);
			String putIndex = queryIndex + ":" + localIndex;

			/* note RFD */

			AqEventHandler.queryGUIDs.put(putIndex, guid);
			AqEventHandler.responses.put(putIndex, rfd);
			AqEventHandler.locations.put(putIndex, locs);

			/* bitrate & xml metadata */

			String bitrate = "";
			String seconds = "";
			String artist = "";
			String album = "";
			String title = "";

			if (rfd.getXMLDocument() != null) {
				LimeXMLDocument xml = rfd.getXMLDocument();
				bitrate = fixValue(xml.getValue(LimeXMLNames.AUDIO_BITRATE));
				seconds = fixValue(xml.getValue(LimeXMLNames.AUDIO_SECONDS));
				artist = decodeValue(xml.getValue(LimeXMLNames.AUDIO_ARTIST));
				album = decodeValue(xml.getValue(LimeXMLNames.AUDIO_ALBUM));
				title = decodeValue(xml.getValue(LimeXMLNames.AUDIO_TITLE));
			}

			/* browse host */

			String address = hd.getIP() + ":" + hd.getPort();

			if (hd.isBrowseHostEnabled())
				AqEventHandler.hosts.put(address, hd);

			event.append(type);
			event.append(kSep);
			event.append(queryIndex);
			event.append(kSep);
			event.append(localIndex);
			event.append(kSep);
			event.append(locs.size());
			event.append(kSep);
			event.append(address);
			event.append(kSep);
			event.append(hd.isBrowseHostEnabled() ? 1 : 0);
			event.append(kSep);
			event.append(rfd.getSHA1Urn());
			event.append(kSep);
			event.append(rfd.getFileName());
			event.append(kSep);
			event.append(rfd.getSpeed());
			event.append(kSep);
			event.append(rfd.getSize());
			event.append(kSep);
			event.append(bitrate);
			event.append(kSep);
			event.append(seconds);
			event.append(kSep);
			event.append(artist);
			event.append(kSep);
			event.append(album);
			event.append(kSep);
			event.append(title);
			event.append("\n");
			break;
		}

			/* Connections */

		case kLWEventConnectionInitialized: {
			ManagedConnection c = (ManagedConnection) obj;

			/* display language */

			String displayLanguage = (new Locale(c.getLocalePref()))
					.getDisplayLanguage();

			/* connection type */

			String connectionType;
			if (c.isSupernodeClientConnection())
				connectionType = "Leaf";
			else if (c.isClientSupernodeConnection())
				connectionType = "Ultrapeer";
			else if (c.isSupernodeSupernodeConnection())
				connectionType = "Peer";
			else
				connectionType = "Standard";

			event.append(type);
			event.append(kSep);
			event.append(c.getAddress());
			event.append(":");
			event.append(c.getPort());
			event.append(kSep);
			event.append(c.getUserAgent());
			event.append(kSep);
			event.append(displayLanguage);
			event.append(kSep);
			event.append(connectionType);
			event.append("\n");
			break;
		}

		case kLWEventConnectionClosed: {
			ManagedConnection c = (ManagedConnection) obj;

			event.append(type);
			event.append(kSep);
			event.append(c.getAddress());
			event.append(":");
			event.append(c.getPort());
			event.append("\n");
			break;
		}

			/* Downloads */

		case kLWEventAddDownload: {
			ManagedDownloader d = (ManagedDownloader) obj;

			event.append(type);
			event.append(kSep);
			event.append(d.hashCode());
			event.append(kSep);
			event.append(d.getSaveFile() != null ? d.getSaveFile().getName()
					: "");
			event.append(kSep);
			event.append(d.getState());
			event.append(kSep);
			event.append(d.getContentLength());
			event.append(kSep);
			event.append(d.getAmountRead());
			event.append(kSep);
			event.append(d.getSHA1Urn());
			event.append("\n");
			break;
		}

		case kLWEventRemoveDownload: {
			ManagedDownloader d = (ManagedDownloader) obj;

			event.append(type);
			event.append(kSep);
			event.append(d.hashCode());
			event.append("\n");

			stateCache.remove(String.valueOf(d.hashCode()));

			/* Delete Aborted Download */

			if (d.canceled() && d.getFile() != null)
				d.getFile().delete();
			break;
		}

		case kLWEventUpdateDownloadStats: {
			ManagedDownloader d = (ManagedDownloader) obj;

			/* bandwidth */

			float measured = d.getMeasuredBandwidth() * 1024;
			float average = d.getAverageBandwidth() * 1024;

			event.append(type);
			event.append(kSep);
			event.append(d.hashCode());
			event.append(kSep);
			event.append(d.getState());
			event.append(kSep);
			event.append(d.getContentLength());
			event.append(kSep);
			event.append(d.getAmountRead());
			event.append(kSep);
			event.append(measured);
			event.append(kSep);
			event.append(average);
			event.append(kSep);
			event.append(d.getActiveHostCount());
			event.append(kSep);
			event.append(d.getBusyHostCount());
			event.append(kSep);
			event.append(d.getPossibleHostCount());
			event.append(kSep);
			event.append(d.getQueuedHostCount());
			event.append(kSep);
			event.append((d.getFile() != null) ? d.getFile().getAbsolutePath()
					: "");
			event.append(kSep);
			event.append(d.getActiveHostAddresses());
			event.append("\n");

			String representationInfo = String.valueOf(d.hashCode());
			if (event.toString().equals(
					(String) stateCache.get(representationInfo)))
				event = null;
			else
				stateCache.put(representationInfo, event.toString());
			break;
		}

			/* Uploads */

		case kLWEventAddUpload: {
			Uploader u = (Uploader) obj;

			event.append(type);
			event.append(kSep);
			event.append(u.hashCode());
			event.append(kSep);
			event.append(u.getFileName());
			event.append(kSep);
			event.append(u.getHost());
			event.append(kSep);
			event.append(u.getGnutellaPort());
			event.append(kSep);
			event.append(u.getUserAgent());
			event.append(kSep);
			event.append(u.getState());
			event.append(kSep);
			event.append(u.getFileSize());
			event.append(kSep);
			event.append(u.getTotalAmountUploaded());
			event.append(kSep);
			event.append(u.getFileDesc().getPath());
			event.append(kSep);
			event.append(u.isBrowseHostEnabled() ? 1 : 0);
			event.append("\n");
			break;
		}

		case kLWEventRemoveUpload: {
			Uploader u = (Uploader) obj;

			event.append(type);
			event.append(kSep);
			event.append(u.hashCode());
			event.append("\n");

			stateCache.remove(String.valueOf(u.hashCode()));
			break;
		}

		case kLWEventUpdateUploadStats: {
			Uploader u = (Uploader) obj;

			float measured;
			try {
				measured = u.getMeasuredBandwidth() * 1024;
			} catch (InsufficientDataException e) {
				measured = 0;
			}
			float average = u.getAverageBandwidth() * 1024;

			event.append(type);
			event.append(kSep);
			event.append(u.hashCode());
			event.append(kSep);
			event.append(u.getState());
			event.append(kSep);
			event.append(u.getFileSize());
			event.append(kSep);
			event.append(u.getTotalAmountUploaded());
			event.append(kSep);
			event.append(measured);
			event.append(kSep);
			event.append(average);
			event.append("\n");

			String representationInfo = String.valueOf(u.hashCode());
			if (event.toString().equals(
					(String) stateCache.get(representationInfo)))
				event = null;
			else
				stateCache.put(representationInfo, event.toString());
			break;
		}

			/* Sharing */

		case kLWEventAddSharedFile: {
			FileDesc f = (FileDesc) obj;

			event.append(type);
			event.append(kSep);
			event.append(f.getSHA1Urn());
			event.append("\n");
			break;
		}

			/* Default */

		default: {
			event.append(type);
			event.append("\n");
			break;
		}

		}

		if (event != null)
			AqMain.writeEvent(event.toString());

	}

}