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

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

import com.limegroup.gnutella.Endpoint;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.ManagedConnection;
import com.limegroup.gnutella.MediaType;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.SaveLocationException;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.downloader.CantResumeException;
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.spam.SpamManager;
import com.limegroup.gnutella.util.FileUtils;
import com.limegroup.gnutella.util.NetworkUtils;
import com.limegroup.gnutella.util.StringUtils;

public class AqDispatcher {
	/* Instances */

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

	private static int currentQueryIndex = -1;

	/* Dispatch */

	protected static void dispatchCommand(List args) throws Exception {

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

		if (command.equals("")) {
			return;
		}

		/* Query */

		else if (command.equals("query")) {
			GUID guid = new GUID(RouterService.newQueryGUID());
			String queryIndex = String.valueOf(++currentQueryIndex);
			AqEventHandler.queries.put(guid, queryIndex);
			AqEventHandler.indices.put(queryIndex, guid);
            RouterService.query(guid.bytes(), StringUtils
                                .createQueryString(strValue), "", null);
		}
        
		else if (command.equals("queryFindMoreSources")) {
			GUID guid = new GUID(RouterService.newQueryGUID());
			String queryIndex = String.valueOf(++currentQueryIndex);
			AqEventHandler.queries.put(guid, queryIndex);
			AqEventHandler.indices.put(queryIndex, guid);
            MediaType type = null;
            String ext = FileUtils.getFileExtension(strValue);
            if (ext != null)
                type = MediaType.getMediaTypeForExtension(ext);
			RouterService.query(guid.bytes(),
                                         StringUtils.createQueryString(strValue, true), "", type);
		}

		else if (command.equals("queryWhatIsNew")) {
			GUID guid = new GUID(RouterService.newQueryGUID());
			String queryIndex = String.valueOf(++currentQueryIndex);
			AqEventHandler.queries.put(guid, queryIndex);
			AqEventHandler.indices.put(queryIndex, guid);
            RouterService.queryWhatIsNew(guid.bytes(), null);
		}
        
		else if (command.equals("doBrowseHost")) {
			GUID guid = new GUID(RouterService.newQueryGUID());
			String queryIndex = String.valueOf(++currentQueryIndex);
			AqEventHandler.queries.put(guid, queryIndex);
			AqEventHandler.indices.put(queryIndex, guid);

			String host = strValue;
			int port = new Integer((String) args.get(2)).intValue();
			if (host.equals("localhost")) {
				host = NetworkUtils.ip2string(RouterService
						.getExternalAddress());
				port = RouterService.getPort();
			}
			HostData hd = (HostData) AqEventHandler.hosts
					.get(host + ":" + port);

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

		else if (command.equals("removeQuery")) {
			for (int i = 1; i < args.size(); i++) {
				String index = (String) args.get(i);
				GUID guid = (GUID) AqEventHandler.indices.get(index);
				RouterService.stopQuery(guid);
				AqEventHandler.queries.remove(guid);
				AqEventHandler.indices.remove(index);
			}
		}

		else if (command.equals("removeAllQueries")) {
			synchronized (AqEventHandler.queries) {
				for (Iterator itr = AqEventHandler.queries.keySet().iterator(); itr
						.hasNext(); RouterService.stopQuery((GUID) itr.next()))
					;
			}
			AqEventHandler.queries.clear();
			AqEventHandler.indices.clear();
			AqEventHandler.queryGUIDs.clear();
			AqEventHandler.responses.clear();
			AqEventHandler.locations.clear();
		}

		else if (command.equals("clearAllResults")) {
			for (int i = 1; i < args.size(); i++) {
				String index = (String) args.get(i);
				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.locations) {
					for (Iterator itr = AqEventHandler.locations.keySet()
							.iterator(); itr.hasNext();) {
						String queryString = (String) itr.next();
						if (queryString.startsWith(index))
							itr.remove();
					}
				}
			}
		}

		/* Downloads */

		else if (command.equals("download")) {
			Set preResponses = new HashSet();
			Set preLocations = new HashSet();

			GUID guid = null;
			for (int i = 1; i < args.size(); i++) {
				String index = (String) args.get(i);
				guid = (GUID) AqEventHandler.queryGUIDs.get(index);
				preResponses.add(AqEventHandler.responses.get(index));
				preLocations.addAll((Set) AqEventHandler.locations.get(index));
			}

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

			List postLocations = new LinkedList();
			for (Iterator i = preLocations.iterator(); i.hasNext();) {
				Endpoint next = (Endpoint) i.next();
				postLocations.add(new RemoteFileDesc(sha1RFD, next));
			}

			try {
				RouterService
						.download(postResponses, postLocations, guid, true);
			} catch (SaveLocationException e) {
                LOG.error(e);
                
                if (e.getErrorCode() == SaveLocationException.FILE_IS_ALREADY_DOWNLOADED_TO) {
                    try {
                        RouterService
                        .download(postResponses, postLocations, guid, true,
                                  SharingSettings.getSaveDirectory(),
                                  Integer.toHexString(sha1RFD.hashCode()).toUpperCase() + "-" + sha1RFD.getFileName());
                    } catch (SaveLocationException e2) {
                        LOG.error(e);
                    }
                }
                
			}
		}

		else if (command.equals("pauseDownload")) {
			for (Iterator i = RouterService.getDownloadManager().getDownloads(); i
					.hasNext();) {
				ManagedDownloader d = (ManagedDownloader) i.next();
				if (d.hashCode() == intValue)
					d.pause();
			}
		}

		else if (command.equals("retryDownload")) {
			for (Iterator i = RouterService.getDownloadManager().getDownloads(); i
					.hasNext();) {
				ManagedDownloader d = (ManagedDownloader) i.next();
				if (d.hashCode() == intValue)
					d.resume();
			}
		}

		else if (command.equals("cancelDownload")) {
			for (Iterator i = RouterService.getDownloadManager().getDownloads(); i
					.hasNext();) {
				ManagedDownloader d = (ManagedDownloader) i.next();
				if (d.hashCode() == intValue)
					d.cancel();
			}
		}

		/* Uploads */

		else if (command.equals("cancelUpload")) {
			for (Iterator i = RouterService.getUploadManager()
					.getUploads(); i.hasNext();) {
				Uploader u = (Uploader) i.next();
				if (u.hashCode() == intValue)
					u.stop();
			}
		}

		/* Connections */

		else if (command.equals("closeConnection")) {
			int port = new Integer((String) args.get(2)).intValue();
			for (Iterator i = RouterService.getConnectionManager()
					.getConnections().iterator(); i.hasNext();) {
				ManagedConnection c = (ManagedConnection) i.next();
				if (c.getAddress().equals(strValue) && c.getPort() == port) {
					RouterService.removeConnection(c);
					break;
				}
			}
		}

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

		/* Spam */

		else if (command.equals("addSpamFiles")) {
			List rfds = new ArrayList();
			for (int i = 1; i < args.size(); i++) {
				String index = (String) args.get(i);
				rfds.add(AqEventHandler.responses.get(index));
			}
			RemoteFileDesc[] resultArray = (RemoteFileDesc[]) rfds
					.toArray(new RemoteFileDesc[rfds.size()]);
			SpamManager.instance().handleUserMarkedSpam(resultArray);
		}

		else if (command.equals("removeSpamFiles")) {
			List rfds = new ArrayList();
			for (int i = 1; i < args.size(); i++) {
				String index = (String) args.get(i);
				rfds.add(AqEventHandler.responses.get(index));
			}
			RemoteFileDesc[] resultArray = (RemoteFileDesc[]) rfds
					.toArray(new RemoteFileDesc[rfds.size()]);
			SpamManager.instance().handleUserMarkedGood(resultArray);
		}

		else if (command.equals("clearSpamFiles")) {
			SpamManager.instance().clearFilterData();
		}

		/* Ultrapeer Settings */

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

		/* Filter Settings */

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

		else if (command.equals("setBannedWords")) {
			List newWords = new ArrayList();
			for (int i = 1; i < args.size(); i++) {
				newWords.add((String) args.get(i));
			}
			String[] resultArray = (String[]) newWords
					.toArray(new String[newWords.size()]);
			FilterSettings.BANNED_WORDS.setValue(resultArray);
		}

		else if (command.equals("applyFilterSettings")) {
			RouterService.adjustSpamFilters();
		}

		/* Download Settings */

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

		else if (command.equals("setDownloadSpeed")) {
			DownloadSettings.DOWNLOAD_SPEED.setValue(intValue);
		}

		else if (command.equals("applyDownloadSpeed")) {
			HTTPDownloader.applyRate();
		}

		/* Upload Settings */

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

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

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

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

		/* Connection Settings */

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

		else if (command.equals("setPort")) {
			if (NetworkUtils.isValidPort(intValue))
				ConnectionSettings.PORT.setValue(intValue);
		}

		else if (command.equals("applyPort")) {
			try {
				RouterService.setListeningPort(ConnectionSettings.PORT
						.getValue());
				RouterService.addressChanged();
			} catch (IOException e) {
			}
		}

		else if (command.equals("setUPnPType")) {
			if (intValue == 0) {
				// Enable UPnP
				if (ConnectionSettings.DISABLE_UPNP.getValue()) {
					ConnectionSettings.FORCE_IP_ADDRESS.revertToDefault();
					ConnectionSettings.FORCED_PORT.revertToDefault();
				}
				ConnectionSettings.DISABLE_UPNP.setValue(false);
			} else if (intValue == 1) {
				// Manual port foward
				ConnectionSettings.FORCE_IP_ADDRESS.setValue(true);
				ConnectionSettings.FORCED_PORT.setValue(ConnectionSettings.PORT
						.getValue());
				ConnectionSettings.DISABLE_UPNP.setValue(true);
			} else if (intValue == 2) {
				// Disable UPnP
				ConnectionSettings.FORCE_IP_ADDRESS.revertToDefault();
				ConnectionSettings.FORCED_PORT.revertToDefault();
				ConnectionSettings.DISABLE_UPNP.setValue(true);
			}
		}

		else if (command.equals("applyUPnPType")) {
			RouterService.addressChanged();
		}

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

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

		else if (command.equals("setProxyServer")) {
			ConnectionSettings.PROXY_HOST.setValue(strValue);
		}

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

		else if (command.equals("setProxyUsername")) {
			ConnectionSettings.PROXY_USERNAME.setValue(strValue);
		}

		else if (command.equals("setProxyPassword")) {
			ConnectionSettings.PROXY_PASS.setValue(strValue);
		}

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

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

		/* Sharing Settings */

		else if (command.equals("setDirectories")) {
			Set directories = new HashSet();
			for (int i = 1; i < args.size(); i++) {
				String filename = (String) args.get(i);
				if (!filename.equals("")) {
					File f = new File(filename);
					if (f != null)
						directories.add(f);
				}
			}
			SharingSettings.DIRECTORIES_TO_SHARE.setValue(directories);
		}

		else if (command.equals("applyDirectories")) {
			RouterService.getFileManager().loadSettings();
		}

		else if (command.equals("setSaveDirectory")) {
			try {
				if (strValue.equals(""))
					SharingSettings
							.setSaveDirectory(SharingSettings.DEFAULT_SAVE_DIR);
				else
					SharingSettings.setSaveDirectory(new File(strValue));
			} catch (IOException e) {
                LOG.error(e);
                SharingSettings.DIRECTORY_FOR_SAVING_FILES.revertToDefault();
			}
		}

		else if (command.equals("applySaveDirectory")) {
			File[] incompleteFiles = SharingSettings.INCOMPLETE_DIRECTORY
					.getValue().listFiles();
			for (int i = 0; i < incompleteFiles.length; i++) {
				try {
					RouterService.download(incompleteFiles[i]);
				} catch (CantResumeException e) {
				} catch (SaveLocationException e) {
				}
			}
		}

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

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

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

		/* Application Settings */

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

		/* Core Bridge */

		else if (command.equals("start")) {
			AqMain.start();
		}

		else if (command.equals("shutdown")) {
			RouterService.shutdown();
			AqMain.shutdown();
		}

		/* Catch All */

		else {
			LOG.warn("Unhandled command: " + args);
		}
	}
}