/*
 * 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.io.File;
import java.io.IOException;
import java.net.UnknownHostException;
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 org.limewire.io.ConnectableImpl;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.io.NetworkUtils;
import org.limewire.util.FileUtils;

import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.MediaType;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.SaveLocationException;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.connection.RoutedConnection;
import com.limegroup.gnutella.downloader.CantResumeException;
import com.limegroup.gnutella.search.HostData;
import com.limegroup.gnutella.settings.ApplicationSettings;
import com.limegroup.gnutella.settings.ConnectionSettings;
import com.limegroup.gnutella.settings.DHTSettings;
import com.limegroup.gnutella.settings.DownloadSettings;
import com.limegroup.gnutella.settings.FilterSettings;
import com.limegroup.gnutella.settings.NetworkSettings;
import com.limegroup.gnutella.settings.SSLSettings;
import com.limegroup.gnutella.settings.SearchSettings;
import com.limegroup.gnutella.settings.SharingSettings;
import com.limegroup.gnutella.settings.UltrapeerSettings;
import com.limegroup.gnutella.settings.UploadSettings;
import com.limegroup.gnutella.util.QueryUtils;

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 Throwable {
        
       	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(AqMain.core.getSearchServices().newQueryGUID());
            Integer queryIndex = new Integer(++currentQueryIndex);
            AqEventHandler.queries.put(guid, queryIndex);
            AqEventHandler.indices.put(queryIndex, guid);
            try {
                AqMain.core.getSearchServices().query(guid.bytes(), QueryUtils.createQueryString(strValue), "", null);
            } catch (Throwable t) {
                LOG.error(t);
            }
        }
        
        else if (command.equals("queryFindMoreSources")) {
            GUID guid = new GUID(AqMain.core.getSearchServices().newQueryGUID());
            Integer queryIndex = new Integer(++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);
            try {
            	AqMain.core.getSearchServices().query(guid.bytes(), QueryUtils.createQueryString(strValue, true), "", type);
            } catch (Throwable t) {
                LOG.error(t);
            }
        }
        
        else if (command.equals("queryWhatIsNew")) {
            GUID guid = new GUID(AqMain.core.getSearchServices().newQueryGUID());
            Integer queryIndex = new Integer(++currentQueryIndex);
            AqEventHandler.queries.put(guid, queryIndex);
            AqEventHandler.indices.put(queryIndex, guid);
            AqMain.core.getSearchServices().queryWhatIsNew(guid.bytes(), null);
        }
        
        else if (command.equals("doBrowseHost")) {
            GUID guid = new GUID(AqMain.core.getSearchServices().newQueryGUID());
            Integer queryIndex = new Integer(++currentQueryIndex);
            AqEventHandler.queries.put(guid, queryIndex);
            AqEventHandler.indices.put(queryIndex, guid);
            
            String host;
            int port;
            if (strValue.equals("localhost")) {
                host = NetworkUtils.ip2string(AqMain.core.getAcceptor().getExternalAddress());
                port = AqMain.core.getAcceptor().getPort(false);
            } else {
                host = strValue;
                try {
                    port = new Integer((String) args.get(2)).intValue();
                } catch (NumberFormatException e) {
                    port = 6346;
                }
            }
            
            
            HostData hd = (HostData) AqEventHandler.hosts.get(host + ":" + port);
            
            try {
                if (hd != null)
                	AqMain.core.getSearchServices().doAsynchronousBrowseHost(
							new ConnectableImpl(host, port, false),
							guid,
                            new GUID(hd.getClientGUID()),
                            hd.getPushProxies(),
                            hd.supportsFWTransfer());
                else
                	AqMain.core.getSearchServices().doAsynchronousBrowseHost(
							new ConnectableImpl(host, port, false),
							guid, null, null, false);
            } catch (Throwable t) {
                LOG.error(t);
            }
        }
        
        else if (command.equals("removeQuery")) {
            for (int i = 1; i < args.size(); i++) {
                Integer index = new Integer((String) args.get(i));
                GUID guid = (GUID)AqEventHandler.indices.get(index);
                AqMain.core.getSearchServices().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();
                     AqMain.core.getSearchServices().stopQuery((GUID)itr.next()))
                    ;
            }
            AqEventHandler.queries.clear();
            AqEventHandler.indices.clear();
            AqEventHandler.responses.clear();
        }
        
        else if (command.equals("clearAllResults")) {
            for (int i = 1; i < args.size(); i++) {
                String index = (String) args.get(i) + ":";
                synchronized (AqEventHandler.responses) {
                    for (Iterator itr = AqEventHandler.responses.keySet().iterator(); itr.hasNext();) {
                        String key = (String) itr.next();
                        if (key.startsWith(index))
                            itr.remove();
                    }
                }
            }
        }
        
        /* Downloads */
        
        else if (command.equals("download")) {
            args.remove(0); // remove command name argument
            RemoteFileDesc[] rfds = new RemoteFileDesc[args.size()];
            Set alts = new HashSet();
            
            GUID guid = null;
            for (int i = 0; i < args.size(); i++) {
                String index = (String) args.get(i);
                AqResponse response = (AqResponse)AqEventHandler.responses.get(index);
                if (guid == null) guid = new GUID(response.getData().getMessageGUID());
                rfds[i] = response.getRFD();
                alts.addAll(response.getLocs());
            }
            
            RemoteFileDesc sha1RFD = null;
            for (int i = 0; i < rfds.length; i++) {
                RemoteFileDesc next = rfds[i];
                next.setRetryAfter(0);
                if (next.getSHA1Urn() != null)
                    sha1RFD = next;
                alts.remove(next);
            }
            if (sha1RFD == null)
                sha1RFD = rfds[0];
            
            List altList = new LinkedList();
            for (Iterator i = alts.iterator(); i.hasNext();) {
                IpPort next = (IpPort) i.next();
                altList.add(AqMain.core.getRemoteFileDescFactory().createRemoteFileDesc(sha1RFD, next));
            }
            
            try {
                AqMain.core.getDownloadServices().download(rfds, altList, guid, true);
            } catch (SaveLocationException e) {
                LOG.error(e);
                
                if (e.getErrorCode() == SaveLocationException.FILE_IS_ALREADY_DOWNLOADED_TO) {
                    StringBuffer filename = new StringBuffer();
                    filename.append(QueryUtils.ripExtension(sha1RFD.getFileName()));
                    filename.append(" [");
                    filename.append(Integer.toHexString(sha1RFD.hashCode()).toUpperCase());
                    filename.append("].");
                    filename.append(FileUtils.getFileExtension(sha1RFD.getFileName()));
                    try {
                        AqMain.core.getDownloadServices().download(rfds, altList, guid, true,
                                               SharingSettings.getSaveDirectory(),
                                               filename.toString());
                    } catch (SaveLocationException e2) {
                        LOG.error(e);
                    }
                }
                
            }
        }
        
        else if (command.equals("pauseDownload")) {
            for (Iterator i = AqMain.core.getDownloadManager().getDownloads(); i.hasNext(); ) {
                Downloader d = (Downloader) i.next();
                if (d.hashCode() == intValue)
                    d.pause();
            }
        }
        
        else if (command.equals("retryDownload")) {
            for (Iterator i = AqMain.core.getDownloadManager().getDownloads(); i.hasNext(); ) {
                Downloader d = (Downloader) i.next();
                if (d.hashCode() == intValue)
                    d.resume();
            }
        }
        
        else if (command.equals("cancelDownload")) {
            for (Iterator i = AqMain.core.getDownloadManager().getDownloads(); i.hasNext(); ) {
                Downloader d = (Downloader) i.next();
                if (d.hashCode() == intValue)
                    d.cancel();
            }
        }
        
        /* Uploads */
        
        else if (command.equals("cancelUpload")) {
            for (Iterator i = AqMain.core.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 = AqMain.core.getConnectionManager().getConnections().iterator(); i.hasNext(); ) {
                RoutedConnection c = (RoutedConnection) i.next();
                if (c.getAddress().equals(strValue) && c.getPort() == port) {
                	AqMain.core.getConnectionServices().removeConnection(c);
                    break;
                }
            }
        }
        
        else if (command.equals("awakeConnection")) {
        	AqMain.core.getConnectionServices().disconnect();
        	AqMain.core.getConnectionServices().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);
                if (AqEventHandler.responses.containsKey(index))
                    rfds.add(((AqResponse)AqEventHandler.responses.get(index)).getRFD());
            }
            RemoteFileDesc[] resultArray = (RemoteFileDesc[]) rfds.toArray(new RemoteFileDesc[rfds.size()]);
            AqMain.core.getSpamManager().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);
                if (AqEventHandler.responses.containsKey(index))
                    rfds.add(((AqResponse)AqEventHandler.responses.get(index)).getRFD());
            }
            RemoteFileDesc[] resultArray = (RemoteFileDesc[]) rfds.toArray(new RemoteFileDesc[rfds.size()]);
            AqMain.core.getSpamManager().handleUserMarkedGood(resultArray);
        }
        
        else if (command.equals("clearSpamFiles")) {
        	AqMain.core.getSpamManager().clearFilterData();
        }
        
        /* Network Settings */
        
        else if (command.equals("setEnableUltrapeer")) {
            UltrapeerSettings.DISABLE_ULTRAPEER_MODE.setValue(intValue == 0);
        }
        
        else if (command.equals("setEnableDHT")) {
            DHTSettings.DISABLE_DHT_USER.setValue(intValue == 0);
        }
        
        else if (command.equals("setEnableTLS")) {
        	SSLSettings.TLS_INCOMING.setValue(intValue == 1);
        	SSLSettings.TLS_OUTGOING.setValue(intValue == 1);
        }
        
        else if (command.equals("setEnableOOB")) {
            SearchSettings.OOB_ENABLED.setValue(intValue == 1);
        }
        
        /* 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("setBannedIPs")) {
            List newIPs = new ArrayList();
            for (int i = 1; i < args.size(); i++) {
                String IP = (String) args.get(i);
                if (IP.length() != 0) newIPs.add(IP);
            }
            String[] resultArray = (String[]) newIPs.toArray(new String[newIPs.size()]);
            FilterSettings.BLACK_LISTED_IP_ADDRESSES.setValue(resultArray);
        }
        
        else if (command.equals("setBannedKeywords")) {
            List newWords = new ArrayList();
            for (int i = 1; i < args.size(); i++) {
                String keyword = (String) args.get(i);
                if (keyword.length() != 0) newWords.add(keyword);
            }
            String[] resultArray = (String[]) newWords.toArray(new String[newWords.size()]);
            FilterSettings.BANNED_WORDS.setValue(resultArray);
        }
        
        else if (command.equals("applyFilterSettings")) {
        	AqMain.core.getSpamServices().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")) {
            AqMain.core.getBandwidthManager().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("applyUploadSpeed")) {
            AqMain.core.getBandwidthManager().applyUploadRate();
        }
        
        else if (command.equals("setAllowPartialSharing")) {
        	SharingSettings.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))
                NetworkSettings.PORT.setValue(intValue);
            else
            	NetworkSettings.PORT.revertToDefault();
        }
        
        else if (command.equals("setUPnPType")) {
            if (intValue == 0) {
                // Enable UPnP
                ConnectionSettings.FORCE_IP_ADDRESS.setValue(false);
                ConnectionSettings.DISABLE_UPNP.setValue(false);
            } else if (intValue == 1) {
                // Manual port foward
                ConnectionSettings.FORCE_IP_ADDRESS.setValue(true);
                ConnectionSettings.DISABLE_UPNP.setValue(true);
            } else if (intValue == 2) {
                // Disable UPnP
                ConnectionSettings.FORCE_IP_ADDRESS.setValue(false);
                ConnectionSettings.DISABLE_UPNP.setValue(true);
            }
        }
        
        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")) {
            AqMain.core.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);
            }
        }
        
        else if (command.equals("applySaveDirectory")) {
            File[] incompleteFiles = SharingSettings.INCOMPLETE_DIRECTORY.getValue().listFiles();
            for (int i = 0; i < incompleteFiles.length; i++) {
                try {
                    AqMain.core.getDownloadServices().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")) {
            AqMain.shutdown();
        }
        
        /* Catch All */
        
        else {
            LOG.warn("Unhandled command: " + args);
        }
    }
}