/*
 * Copyright (c) 2002-2004 David Keiichi Watanabe
 * davew@xlife.org
 *
 * Modified by (c) 2004-2005 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.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.Endpoint;
import com.limegroup.gnutella.GUID;
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.HTTPDownloader;
import com.limegroup.gnutella.downloader.ManagedDownloader;
import com.limegroup.gnutella.search.HostData;
import com.limegroup.gnutella.settings.ApplicationSettings;
import com.limegroup.gnutella.settings.ConnectionSettings;
import com.limegroup.gnutella.settings.DownloadSettings;
import com.limegroup.gnutella.settings.FilterSettings;
import com.limegroup.gnutella.settings.SharingSettings;
import com.limegroup.gnutella.settings.UltrapeerSettings;
import com.limegroup.gnutella.settings.UploadSettings;
import com.limegroup.gnutella.util.StringUtils;

public class AqDispatcher {
	/* Instances */

	private static int currentQueryIndex = -1;

	/* Dispatch */

	protected static void dispatchCommand(List v) throws Exception {

		String command = (String) v.get(0);
		String firstElement = (v.size() > 1) ? (String) v.get(1) : "";
		int intValue = 0;
		try {
			intValue = (v.size() > 1) ? new Integer(firstElement).intValue()
					: 0;
		} catch (Exception e) {
		}

		/* Ultrapeer Settings */

		if (command.equals("setEnableUltrapeer")) {
			UltrapeerSettings.DISABLE_ULTRAPEER_MODE.setValue(intValue == 0);
			return;
		}

		/* Query */

		if (command.equals("query")) {
			byte[] guid = RouterService.newQueryGUID();
			String queryIndex = ++currentQueryIndex + "";
			synchronized (AqEventHandler.queriesToIndices) {
				AqEventHandler.queriesToIndices.put(new GUID(guid), queryIndex);
			}
			synchronized (AqEventHandler.indicesToQueries) {
				AqEventHandler.indicesToQueries.put(queryIndex, new GUID(guid));
			}
			RouterService.query(guid, StringUtils
					.createQueryString(firstElement), "", null);
			return;
		}

		if (command.equals("queryWhatIsNew")) {
			byte[] guid = RouterService.newQueryGUID();
			String queryIndex = ++currentQueryIndex + "";
			synchronized (AqEventHandler.queriesToIndices) {
				AqEventHandler.queriesToIndices.put(new GUID(guid), queryIndex);
			}
			synchronized (AqEventHandler.indicesToQueries) {
				AqEventHandler.indicesToQueries.put(queryIndex, new GUID(guid));
			}
			RouterService.queryWhatIsNew(guid, null);
			return;
		}

		if (command.equals("removeQuery")) {
			for (int i = 1; i < v.size(); i++) {
				String index = (String) v.get(i);
				GUID guid;
				synchronized (AqEventHandler.indicesToQueries) {
					guid = (GUID) AqEventHandler.indicesToQueries.get(index);
				}

				if (guid == null)
					continue;

				RouterService.stopQuery(guid);
				synchronized (AqEventHandler.queriesToIndices) {
					AqEventHandler.queriesToIndices.remove(guid);
				}
				synchronized (AqEventHandler.indicesToQueries) {
					AqEventHandler.indicesToQueries.remove(index);
				}
				index = index + ":";
				synchronized (AqEventHandler.queryGUIDs) {
					for (Iterator itr = AqEventHandler.queryGUIDs.keySet()
							.iterator(); itr.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}
				synchronized (AqEventHandler.responses) {
					for (Iterator itr = AqEventHandler.responses.keySet()
							.iterator(); itr.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}
				synchronized (AqEventHandler.alts) {
					for (Iterator itr = AqEventHandler.alts.keySet().iterator(); itr
							.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}

			}
			return;
		}

		if (command.equals("removeAllQueries")) {
			synchronized (AqEventHandler.queriesToIndices) {
				for (Iterator itr = AqEventHandler.queriesToIndices.keySet()
						.iterator(); itr.hasNext(); RouterService
						.stopQuery((GUID) itr.next()))
					;
			}

			synchronized (AqEventHandler.queriesToIndices) {
				AqEventHandler.queriesToIndices.clear();
			}
			synchronized (AqEventHandler.indicesToQueries) {
				AqEventHandler.indicesToQueries.clear();
			}
			synchronized (AqEventHandler.queryGUIDs) {
				AqEventHandler.queryGUIDs.clear();
			}
			synchronized (AqEventHandler.responses) {
				AqEventHandler.responses.clear();
			}
			synchronized (AqEventHandler.alts) {
				AqEventHandler.alts.clear();
			}
			return;
		}

		if (command.equals("clearAllResults")) {
			for (int i = 1; i < v.size(); i++) {
				String index = (String) v.get(i);
				GUID guid;
				synchronized (AqEventHandler.indicesToQueries) {
					guid = (GUID) AqEventHandler.indicesToQueries.get(index);
				}

				if (guid == null)
					continue;

				index = index + ":";
				synchronized (AqEventHandler.queryGUIDs) {
					for (Iterator itr = AqEventHandler.queryGUIDs.keySet()
							.iterator(); itr.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}
				synchronized (AqEventHandler.responses) {
					for (Iterator itr = AqEventHandler.responses.keySet()
							.iterator(); itr.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}
				synchronized (AqEventHandler.alts) {
					for (Iterator itr = AqEventHandler.alts.keySet().iterator(); itr
							.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}

			}
			return;
		}

		/* Filter Settings */

		if (command.equals("setAdultFilter")) {
			FilterSettings.FILTER_WHATS_NEW_ADULT.setValue(intValue == 1);
			FilterSettings.FILTER_ADULT.setValue(intValue == 1);
			return;
		}

		if (command.equals("clearWords")) {
			String words[] = new String[0];
			FilterSettings.BANNED_WORDS.setValue(words);
			return;
		}

		if (command.equals("addWord")) {
			List oldWords = Arrays.asList(FilterSettings.BANNED_WORDS
					.getValue());
			List newWords = new ArrayList(oldWords);
			newWords.add(firstElement);
			String[] resultArray = (String[]) newWords
					.toArray(new String[newWords.size()]);
			FilterSettings.BANNED_WORDS.setValue(resultArray);
			return;
		}

		if (command.equals("removeWord")) {
			List oldWords = Arrays.asList(FilterSettings.BANNED_WORDS
					.getValue());
			List newWords = new ArrayList(oldWords);
			newWords.remove(firstElement);
			String[] resultArray = (String[]) newWords
					.toArray(new String[newWords.size()]);
			FilterSettings.BANNED_WORDS.setValue(resultArray);
			return;
		}

		if (command.equals("finishFilterSettings")) {
			RouterService.adjustSpamFilters();
			return;
		}

		/* Downloads */

		if (command.equals("download")) {
			GUID guid;
			synchronized (AqEventHandler.queryGUIDs) {
				guid = (GUID) AqEventHandler.queryGUIDs.get(firstElement);
			}
			Set preRFDs = new HashSet();
			Set alts = new HashSet();
			List otherRFDs = new LinkedList();

			for (int i = 1; i < v.size(); i++) {
				String index = (String) v.get(i);
				synchronized (AqEventHandler.responses) {
					if (AqEventHandler.responses.containsKey(index))
						preRFDs.add(AqEventHandler.responses.get(index));
				}
				synchronized (AqEventHandler.alts) {
					if (AqEventHandler.alts.containsKey(index))
						alts.addAll((Set) AqEventHandler.alts.get(index));
				}
			}
			RemoteFileDesc[] rfds = (RemoteFileDesc[]) preRFDs
					.toArray(new RemoteFileDesc[preRFDs.size()]);

			RemoteFileDesc sha1RFD = null;
			for (int i = 0; i < rfds.length; i++) {
				RemoteFileDesc next = rfds[i];
				next.setDownloading(true);
				next.setRetryAfter(0);
				if (next.getSHA1Urn() != null)
					sha1RFD = next;
				alts.remove(new Endpoint(next.getHost(), next.getPort()));
			}
			if (sha1RFD == null)
				sha1RFD = rfds[0];

			for (Iterator i = alts.iterator(); i.hasNext();) {
				Endpoint next = (Endpoint) i.next();
				otherRFDs.add(new RemoteFileDesc(sha1RFD, next));
			}

			RouterService.download(rfds, otherRFDs, guid, false);
			return;
		}

		if (command.equals("pauseDownload")) {
			for (Iterator i = AqEventHandler.downloads.iterator(); i.hasNext();) {
				ManagedDownloader d = (ManagedDownloader) i.next();
				if (d != null && d.hashCode() == intValue && d.isActive()) {
					d.pause();
					return;
				}
			}
			return;
		}

		if (command.equals("retryDownload")) {
			for (Iterator i = AqEventHandler.downloads.iterator(); i.hasNext();) {
				ManagedDownloader d = (ManagedDownloader) i.next();
				if (d != null && d.hashCode() == intValue && d.isInactive()) {
					d.resume();
					return;
				}
			}
			return;
		}

		if (command.equals("cancelDownload")) {
			for (Iterator i = AqEventHandler.downloads.iterator(); i.hasNext();) {
				ManagedDownloader d = (ManagedDownloader) i.next();
				if (d != null && d.hashCode() == intValue
						&& d.getState() != Downloader.HASHING) {
					d.manualCancel();
					return;
				}
			}
			return;
		}

		if (command.equals("restoreIncompleteFiles")) {
			try {
				File incompleteDir = SharingSettings.INCOMPLETE_DIRECTORY
						.getValue();
				File[] files = incompleteDir.listFiles();
				if (files != null) {
					for (int i = 0; i < files.length; i++) {
						File f = files[i];
						if (f.getName().startsWith("T-") && f.canRead()
								&& f.canWrite()) {
							try {
								RouterService.download(f);
							} catch (Exception e) {
							}
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}

		/* Download Settings */

		if (command.equals("setMaxSimDownload")) {
			DownloadSettings.MAX_SIM_DOWNLOAD.setValue(intValue);
			return;
		}

		if (command.equals("setDownloadSpeed")) {
			int oldValue = DownloadSettings.DOWNLOAD_SPEED.getValue();
			DownloadSettings.DOWNLOAD_SPEED.setValue(intValue);
			if (intValue != oldValue)
				HTTPDownloader.applyRate();
			return;
		}

		/* Uploads */

		if (command.equals("cancelUpload")) {
			for (Iterator i = AqEventHandler.uploads.iterator(); i.hasNext();) {
				Uploader u = (Uploader) i.next();
				if (u != null && u.hashCode() == intValue) {
					u.stop();
					return;
				}
			}
			return;
		}

		/* Upload Settings */

		if (command.equals("setMaxUploads")) {
			UploadSettings.HARD_MAX_UPLOADS.setValue(intValue);
			return;
		}

		if (command.equals("setUploadsPerPerson")) {
			UploadSettings.UPLOADS_PER_PERSON.setValue(intValue);
			return;
		}

		if (command.equals("setUploadSpeed")) {
			UploadSettings.UPLOAD_SPEED.setValue(intValue);
			return;
		}

		if (command.equals("setAllowPartialSharing")) {
			UploadSettings.ALLOW_PARTIAL_SHARING.setValue(intValue == 1);
			return;
		}

		/* Connections */

		if (command.equals("closeConnection")) {
			int port = new Integer((String) v.get(2)).intValue();
			for (Iterator i = RouterService.getConnectionManager()
					.getConnections().iterator(); i.hasNext();) {
				ManagedConnection c = (ManagedConnection) i.next();
				if (c != null && c.getAddress().equals(firstElement)
						&& c.getPort() == port) {
					RouterService.removeConnection(c);
					return;
				}
			}
			return;
		}

		if (command.equals("awakeConnection")) {
			RouterService.connect();
			return;
		}

		/* Connection Settings */

		if (command.equals("setConnectionSpeed")) {
			ConnectionSettings.CONNECTION_SPEED.setValue(intValue);
			return;
		}

		if (command.equals("setPort")) {
			int oldPort = ConnectionSettings.PORT.getValue();
			ConnectionSettings.PORT.setValue(intValue);
			if (oldPort != intValue) {
				try {
					RouterService.setListeningPort(intValue);
				} catch (IOException e) {
					ConnectionSettings.PORT.setValue(oldPort);
					e.printStackTrace();
				}
			}
			return;
		}

		if (command.equals("setUPnPType")) {
			boolean oldForce = ConnectionSettings.FORCE_IP_ADDRESS.getValue();
			int oldPort = ConnectionSettings.FORCED_PORT.getValue();

			if (intValue == 0) { // Enable UPnP
				if (!ConnectionSettings.UPNP_IN_USE.getValue())
					ConnectionSettings.FORCE_IP_ADDRESS.setValue(false);
				ConnectionSettings.DISABLE_UPNP.setValue(false);
			} else if (intValue == 1) { // Manual port foward
				ConnectionSettings.DISABLE_UPNP.setValue(false);
				ConnectionSettings.FORCE_IP_ADDRESS.setValue(true);
				ConnectionSettings.UPNP_IN_USE.setValue(false);
				ConnectionSettings.FORCED_PORT.setValue(ConnectionSettings.PORT
						.getValue());
			} else { // Disable UPnP
				ConnectionSettings.FORCE_IP_ADDRESS.setValue(false);
				ConnectionSettings.DISABLE_UPNP.setValue(true);
			}

			boolean newForce = ConnectionSettings.FORCE_IP_ADDRESS.getValue();
			int newPort = ConnectionSettings.FORCED_PORT.getValue();
			if (oldForce != newForce || (newForce && (oldPort != newPort)))
				RouterService.addressChanged();
			return;
		}

		if (command.equals("setUsesLocalePreferencing")) {
			ConnectionSettings.USE_LOCALE_PREF.setValue(intValue == 1);
			return;
		}

		/* doBrowseHost */

		if (command.equals("doBrowseHost")) {
			byte[] guid = RouterService.newQueryGUID();
			String queryIndex = ++currentQueryIndex + "";
			synchronized (AqEventHandler.queriesToIndices) {
				AqEventHandler.queriesToIndices.put(new GUID(guid), queryIndex);
			}
			synchronized (AqEventHandler.indicesToQueries) {
				AqEventHandler.indicesToQueries.put(queryIndex, new GUID(guid));
			}

			String host = firstElement;
			int port = new Integer((String) v.get(2)).intValue();
			HostData hd;
			synchronized (AqEventHandler.hosts) {
				hd = (HostData) AqEventHandler.hosts.get(host + ":" + port);
			}

			if (hd != null)
				RouterService.doAsynchronousBrowseHost(host, port, new GUID(
						guid), new GUID(hd.getClientGUID()), hd
						.getPushProxies(), hd.supportsFWTransfer());
			else
				RouterService.doAsynchronousBrowseHost(host, port, new GUID(
						guid), null, null, false);
			return;
		}

		/* Sharing Settings */

		if (command.equals("setDirectories")) {
			Set newDirectories = new HashSet();
			for (int i = 1; i < v.size(); i++) {
				String filename = (String) v.get(i);
				if (!filename.equals("")) {
					File f = new File(filename);
					if (f != null)
						newDirectories.add(f);
				}
			}
			Set oldDirectories = SharingSettings.DIRECTORIES_TO_SHARE
					.getValue();
			if (!newDirectories.equals(oldDirectories)) {
				SharingSettings.DIRECTORIES_TO_SHARE.setValue(newDirectories);
				RouterService.getFileManager().loadSettings();
			}
			return;
		}

		if (command.equals("setSaveDirectory")) {
			File oldDirectory = SharingSettings.getSaveDirectory();
			File newDirectory = (firstElement.equals("")) ? SharingSettings.DEFAULT_SAVE_DIR
					: new File(firstElement);
			if (newDirectory != null && !newDirectory.equals(oldDirectory))
				SharingSettings.setSaveDirectory(newDirectory);
			return;
		}

		if (command.equals("setIncompletePurgeTime")) {
			SharingSettings.INCOMPLETE_PURGE_TIME.setValue(intValue);
			return;
		}

		if (command.equals("setAllowFreeloaders")) {
			if (intValue == 1)
				SharingSettings.FREELOADER_ALLOWED.setValue(100);
			else
				SharingSettings.FREELOADER_ALLOWED.setValue(0);
			return;
		}

		if (command.equals("setFreeloaderFiles")) {
			SharingSettings.FREELOADER_FILES.setValue(intValue);
			return;
		}

		if (command.equals("setAllowCompleteSharing")) {
			SharingSettings.SHARE_DOWNLOADED_FILES_IN_NON_SHARED_DIRECTORIES
					.setValue(intValue == 1);
			return;
		}

		/* Proxy Settings */

		if (command.equals("setProxyType")) {
			ConnectionSettings.CONNECTION_METHOD.setValue(intValue);
			return;
		}

		if (command.equals("setProxyServer")) {
			ConnectionSettings.PROXY_HOST.setValue(firstElement);
			return;
		}

		if (command.equals("setProxyPort")) {
			ConnectionSettings.PROXY_PORT.setValue(intValue);
			return;
		}

		if (command.equals("setProxyUsername")) {
			ConnectionSettings.PROXY_USERNAME.setValue(firstElement);
			return;
		}

		if (command.equals("setProxyPassword")) {
			ConnectionSettings.PROXY_PASS.setValue(firstElement);
			return;
		}

		if (command.equals("setProxyPrivate")) {
			ConnectionSettings.USE_PROXY_FOR_PRIVATE.setValue(intValue == 1);
			return;
		}

		if (command.equals("setRequiresAuthentication")) {
			ConnectionSettings.PROXY_AUTHENTICATE.setValue(intValue == 1);
			return;
		}

		/* Application Settings */

		if (command.equals("setLanguage")) {
			if (firstElement.equals(""))
				ApplicationSettings.LANGUAGE.revertToDefault();
			else
				ApplicationSettings.LANGUAGE.setValue(firstElement);
			return;
		}

		/* Initialization Complete */

		if (command.equals("initComplete")) {
			AqMain.startup();
			return;
		}

		/* shutdown */

		if (command.equals("shutdown")) {
			AqMain.shouldShutdown = true;
			return;
		}

		/* Catch-All */

		System.err.println("AqDispatcher: Unhandled command: " + v);
		return;
	}

}