/*
 * Copyright (c) 2002-2004 David Keiichi Watanabe
 * davew@xlife.org
 *
 * Modified by (c) 2004-2008 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.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 org.limewire.io.NetworkUtils;
import org.limewire.util.StringUtils;

import com.limegroup.gnutella.FileDesc;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.InsufficientDataException;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.connection.RoutedConnection;
import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.search.HostData;
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 final Map stateCache = 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 kLWEventBrowseHostFailed = 14;
    
	protected static final int kLWEventCoreConnected = 98;
    
	protected static final int kLWEventCoreInitialized = 99;
    
	/* Signal */
    
	private static String decodeValue(String string) {
		if (string == null || 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 : "";
	}
    
	private static String fixUserAgent(String agent) {
        if (agent == null) return "";
        if (agent.indexOf("Cabos") == -1 && agent.indexOf("Acqlite") == -1) return agent;
        return agent.substring(agent.indexOf("(") + 1, agent.indexOf(")"));
	}
    
	protected static void signalEvent(int type) {
		signalEvent(type, null);
	}
    
    public static void signalEvent(int type, Object obj1) {
        StringBuffer event = new StringBuffer();
        Thread.yield();
        
        synchronized (AqEvent.class) {
            switch (type) {
                
                /* Queries */
                
                case kLWEventQueryResult: {
                    AqResponse response = (AqResponse) obj1;
                    RemoteFileDesc rfd = response.getRFD();
                    HostData hd = response.getData();
                    Set locs = response.getLocs();
                    GUID guid = new GUID(hd.getMessageGUID());
                    
                    if (!AqEventHandler.queries.containsKey(guid)) return;
                    
                    /* index */
                    
                    Integer queryIndex = (Integer)AqEventHandler.queries.get(guid);
                    int localIndex = ++currentLocalIndex;
                    event.append(queryIndex);
                    event.append(":");
                    event.append(localIndex);
                    String putIndex = event.toString();
                    event.setLength(0);
                    
                    /* note RFD */
                    
                    AqEventHandler.responses.put(putIndex, response);
                    
                    /* bitrate & xml metadata */
                    
                    String bitrate;
                    String seconds;
                    String artist;
                    String album;
                    String title;
                    
                    if (rfd.getXMLDocument() != null) {
                        LimeXMLDocument xml = rfd.getXMLDocument();
                        /* video or audio */
                        bitrate = xml.getValue(LimeXMLNames.VIDEO_BITRATE);
                        seconds = xml.getValue(LimeXMLNames.VIDEO_LENGTH);
                        artist = xml.getValue(LimeXMLNames.VIDEO_DIRECTOR);
                        album = xml.getValue(LimeXMLNames.VIDEO_PRODUCER);
                        title = xml.getValue(LimeXMLNames.VIDEO_TITLE);
                        
                        bitrate = (bitrate != null) ? bitrate : xml.getValue(LimeXMLNames.AUDIO_BITRATE);
                        seconds = (seconds != null) ? seconds : xml.getValue(LimeXMLNames.AUDIO_SECONDS);
                        artist  = (artist  != null) ? artist  : xml.getValue(LimeXMLNames.AUDIO_ARTIST);
                        album   = (album   != null) ? album   : xml.getValue(LimeXMLNames.AUDIO_ALBUM);
                        title   = (title   != null) ? title   : xml.getValue(LimeXMLNames.AUDIO_TITLE);
                        
                        bitrate = fixValue(bitrate);
                        seconds = fixValue(seconds);
                        artist = decodeValue(artist);
                        album = decodeValue(album);
                        title = decodeValue(title);
                    } else {
                        bitrate = "";
                        seconds = "";
                        artist = "";
                        album = "";
                        title = "";
                    }
                    
                    /* browse host */
                    
                    event.append(hd.getIP());
                    event.append(":");
                    event.append(hd.getPort());
                    String address = event.toString();
                    event.setLength(0);
                    
                    if (hd.isBrowseHostEnabled())
                        AqEventHandler.hosts.put(address, hd);
                    
                    /* spam checking */
                    boolean isSpam = AqMain.core.getSpamManager().isSpam(rfd) ||
                        locs.size() > 10;
                    
                    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(kSep);
                    event.append(isSpam ? 1 : 0);
                    event.append("\n");
                    break;
                }
                    
                    /* Connections */
                    
                case kLWEventConnectionInitialized: {
                    RoutedConnection c = (RoutedConnection) obj1;
                    
                    /* display language */
                    
                    String displayLanguage = (new Locale(c.getLocalePref())).getDisplayLanguage();
                    
                    /* connection type */
                    
                    String connectionType;
                    if (c.getConnectionCapabilities().isSupernodeClientConnection())
                        connectionType = "Leaf";
                    else if (c.getConnectionCapabilities().isClientSupernodeConnection())
                        connectionType = "Ultrapeer";
                    else if (c.getConnectionCapabilities().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(fixUserAgent(c.getConnectionCapabilities().getUserAgent()));
                    event.append(kSep);
                    event.append(displayLanguage);
                    event.append(kSep);
                    event.append(connectionType);
                    event.append("\n");
                    break;
                }
                    
                case kLWEventConnectionClosed: {
                    RoutedConnection c = (RoutedConnection) obj1;
                    
                    event.append(type);
                    event.append(kSep);
                    event.append(c.getAddress());
                    event.append(":");
                    event.append(c.getPort());
                    event.append("\n");
                    break;
                }
                    
                    /* Downloads */
                    
                case kLWEventAddDownload: {
                    Downloader d = (Downloader) obj1;
                    
                    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().ordinal());
                    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: {
                    Downloader d = (Downloader) obj1;
                    
                    event.append(type);
                    event.append(kSep);
                    event.append(d.hashCode());
                    event.append("\n");
                    
                    stateCache.remove(new Integer(d.hashCode()));
                    
                    /* Delete Aborted Download */
                    
                    if (d.canceled() && d.getFile() != null)
                        d.getFile().delete();
                    break;
                }
                    
                case kLWEventUpdateDownloadStats: {
                    Downloader d = (Downloader) obj1;
                    
                    /* bandwidth */
                    
                    float measured;
                    try {
                    	measured = d.getMeasuredBandwidth() * 1024;
                    } catch (InsufficientDataException e) {
                    	measured = 0;
                    }
                    float average = d.getAverageBandwidth() * 1024;
                    
                    event.append(type);
                    event.append(kSep);
                    event.append(d.hashCode());
                    event.append(kSep);
                    event.append(d.getState().ordinal());
                    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.getNumHosts());
                    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");
                    
                    Integer representationInfo = new Integer(d.hashCode());
                    if (event.toString().equals(stateCache.get(representationInfo)))
                        event = null;
                    else
                        stateCache.put(representationInfo, event.toString());
                    break;
                }
                    
                    /* Uploads */
                    
                case kLWEventAddUpload: {
                    Uploader u = (Uploader) obj1;
                    
                    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(fixUserAgent(u.getUserAgent()));
                    event.append(kSep);
                    event.append(u.getState().ordinal());
                    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(NetworkUtils.isValidPort(u.getGnutellaPort()) ? 1 : 0);
                    event.append("\n");
                    break;
                }
                    
                case kLWEventRemoveUpload: {
                    Uploader u = (Uploader) obj1;
                    
                    event.append(type);
                    event.append(kSep);
                    event.append(u.hashCode());
                    event.append("\n");
                    
                    stateCache.remove(new Integer(u.hashCode()));
                    break;
                }
                    
                case kLWEventUpdateUploadStats: {
                    Uploader u = (Uploader) obj1;
                    
                    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().ordinal());
                    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");
                    
                    Integer representationInfo = new Integer(u.hashCode());
                    if (event.toString().equals(stateCache.get(representationInfo)))
                        event = null;
                    else
                        stateCache.put(representationInfo, event.toString());
                    break;
                }
                    
                    /* Sharing */
                    
                case kLWEventAddSharedFile: {
                    FileDesc f = (FileDesc) obj1;
                    
                    event.append(type);
                    event.append(kSep);
                    event.append(f.getSHA1Urn());
                    event.append("\n");
                    break;
                }
                    
                    /* Browsing */
                    
                case kLWEventBrowseHostFailed: {
                    GUID guid = (GUID)obj1;
                    
                    if (!AqEventHandler.queries.containsKey(guid)) return;
                    
                    /* index */
                    
                    Integer queryIndex = (Integer) AqEventHandler.queries.get(guid);
                    
                    event.append(type);
                    event.append(kSep);
                    event.append(queryIndex);
                    event.append("\n");
                    break;
                }
                    
                    /* Default */
                    
                default: {
                    event.append(type);
                    event.append("\n");
                    break;
                }
            }
        }
        
        if (event != null)
            AqMain.writeEvent(event.toString());
    }
}