/*
 * 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.Locale;
import java.util.Map;
import java.util.HashMap;
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.RouterService;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.downloader.ManagedDownloader;
import com.limegroup.gnutella.search.HostData;
import com.limegroup.gnutella.spam.SpamManager;
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 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 : "";
	}

	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();
                    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));
                } 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);
                
                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(SpamManager.instance().isSpam(rfd) ? 1 : 0);
                event.append("\n");
                break;
            }
                
                /* Connections */
                
            case kLWEventConnectionInitialized: {
                ManagedConnection c = (ManagedConnection) obj1;
                
                /* 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) obj1;
                
                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) 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());
                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) obj1;
                
                event.append(type);
                event.append(kSep);
                event.append(d.hashCode());
                event.append("\n");
                
                stateCache.remove(Integer.valueOf(d.hashCode()));
                
                /* Delete Aborted Download */
                
                if (d.canceled() && d.getFile() != null)
                    d.getFile().delete();
                break;
            }
                
            case kLWEventUpdateDownloadStats: {
                ManagedDownloader d = (ManagedDownloader) obj1;
                
                /* 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");
                
                Integer representationInfo = Integer.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) 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(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) obj1;
                
                event.append(type);
                event.append(kSep);
                event.append(u.hashCode());
                event.append("\n");
                
                stateCache.remove(Integer.valueOf(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());
                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 = Integer.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) 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");
            }
                
                /* Default */
                
            default: {
                event.append(type);
                event.append("\n");
                break;
            }
                
        }
        }
        
        if (event != null)
            AqMain.writeEvent(event.toString());
        
        
    }
    
}