/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.ftp.ftpclient;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.SocketException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.net.SocketFactory;
import javax.net.ServerSocketFactory;

import jp.ossc.nimbus.beans.NoSuchPropertyException;
import jp.ossc.nimbus.beans.PropertyAccess;
import jp.ossc.nimbus.core.*;
import jp.ossc.nimbus.service.ftp.*;

import org.apache.commons.net.ftp.FTPClientConfig;

/**
 * FTPクライアントファクトリ。<p>
 * <a href="http://commons.apache.org/net/">Jakarta Commons Net</a>のFTPライブラリを使用した{@link FTPClientFactory}サービスである。<br>
 *
 * @author M.Takata
 */
public class FTPClientFactoryService extends ServiceBase
 implements FTPClientFactory, FTPClientFactoryServiceMBean{
    
    private static final long serialVersionUID = -3626160091182567703L;
    
    private String controlEncoding;
    private boolean isStrictMultilineParsing;
    private int sendBufferSize = -1;
    private int receiveBufferSize = -1;
    private int bufferSize = -1;
    private int connectTimeout = -1;
    private int soTimeout = -1;
    private int soLinger = -1;
    private Boolean isTcpNoDelay;
    private ServerSocketFactory serverSocketFactory;
    private SocketFactory socketFactory;
    
    private String hostName;
    private int port = -1;
    
    private String bindAddress;
    private int localPort = -1;
    
    private String userName;
    private String password = "";
    
    private File homeDir;
    
    private boolean isJavaRegexEnabled = false;
    
    private boolean isPassive = false;
    
    private FTPClientConfig ftpClientConfig;
    
    private Map<String, Object> ftpClientProps = Collections.synchronizedMap(new HashMap<String, Object>());
    
    public void setControlEncoding(String encoding){
        controlEncoding = encoding;
    }
    public String getControlEncoding(){
        return controlEncoding;
    }
    
    public void setStrictMultilineParsing(boolean isMultiline){
        isStrictMultilineParsing = isMultiline;
    }
    public boolean isStrictMultilineParsing(){
        return isStrictMultilineParsing;
    }
    
    public void setSendBufferSize(int size){
        sendBufferSize = size;
    }
    public int getSendBufferSize(){
        return sendBufferSize;
    }
    
    public void setReceiveBufferSize(int size){
        receiveBufferSize = size;
    }
    public int getReceiveBufferSize(){
        return receiveBufferSize;
    }
    
    public void setBufferSize(int size){
        bufferSize = size;
    }
    public int getBufferSize(){
        return bufferSize;
    }
    
    public void setConnectTimeout(int timeout){
        connectTimeout = timeout;
    }
    public int getConnectTimeout(){
        return connectTimeout;
    }
    
    public void setSoTimeout(int timeout){
        soTimeout = timeout;
    }
    public int getSoTimeout(){
        return soTimeout;
    }
    
    public void setTcpNoDelay(boolean noDelay){
        isTcpNoDelay = noDelay ? Boolean.TRUE : Boolean.FALSE;
    }
    public boolean isTcpNoDelay(){
        return isTcpNoDelay == null ? false : isTcpNoDelay.booleanValue();
    }
    
    public void setSoLinger(int val){
        soLinger = val;
    }
    public int getSoLinger(){
        return soLinger;
    }
    
    public void setServerSocketFactory(ServerSocketFactory factory){
        serverSocketFactory = factory;
    }
    public ServerSocketFactory getServerSocketFactory(){
        return serverSocketFactory;
    }
    
    public void setSocketFactory(SocketFactory factory){
        socketFactory = factory;
    }
    public SocketFactory getSocketFactory(){
        return socketFactory;
    }
    
    public void setHostName(String addr){
        hostName = addr;
    }
    public String getHostName(){
        return hostName;
    }
    
    public void setPort(int port){
        this.port = port;
    }
    public int getPort(){
        return port;
    }
    
    public void setBindAddress(String addr){
        bindAddress = addr;
    }
    public String getBindAddress(){
        return bindAddress;
    }
    
    public void setLocalPort(int port){
        localPort = port;
    }
    public int getLocalPort(){
        return localPort;
    }
    
    public void setUserName(String name){
        userName = name;
    }
    public String getUserName(){
        return userName;
    }
    
    public void setPassword(String password){
        this.password = password;
    }
    public String getPassword(){
        return password;
    }
    
    public void setHomeDirectory(File dir){
        homeDir = dir;
    }
    public File getHomeDirectory(){
        return homeDir;
    }
    
    public void setJavaRegexEnabled(boolean isEnabled){
        isJavaRegexEnabled = isEnabled;
    }
    public boolean isJavaRegexEnabled(){
        return isJavaRegexEnabled;
    }
    
    public void setPassive(boolean isPassive){
        this.isPassive = isPassive;
    }
    public boolean isPassive(){
        return isPassive;
    }
    
    public void setFTPClientConfig(FTPClientConfig config){
        ftpClientConfig = config;
    }
    public FTPClientConfig getFTPClientConfig(){
        return ftpClientConfig;
    }
    
    public void setFTPClientProperty(String name, Object value){
        ftpClientProps.put(name, value);
    }
    public Object getFTPClientProperty(String name){
        return ftpClientProps.get(name);
    }
    public void removeTPClientProperty(String name){
        ftpClientProps.remove(name);
    }
    public void clearFTPClientProperties(){
        ftpClientProps.clear();
    }
    public Map<String, Object> getFTPClientProperties(){
        return ftpClientProps;
    }
    
    public FTPClient createFTPClient() throws FTPException{
        org.apache.commons.net.ftp.FTPClient ftpClient
            = new org.apache.commons.net.ftp.FTPClient();
        try{
            if(ftpClientConfig != null){
                ftpClient.configure(ftpClientConfig);
            }
            if(controlEncoding != null){
                ftpClient.setControlEncoding(controlEncoding);
            }
            ftpClient.setStrictMultilineParsing(isStrictMultilineParsing);
            if(sendBufferSize >= 0){
                ftpClient.setSendBufferSize(sendBufferSize);
            }
            if(receiveBufferSize >= 0){
                ftpClient.setReceiveBufferSize(receiveBufferSize);
            }
            if(bufferSize >= 0){
                ftpClient.setBufferSize(bufferSize);
            }
            if(connectTimeout >= 0){
                ftpClient.setConnectTimeout(connectTimeout);
            }
            if(soTimeout >= 0){
                ftpClient.setSoTimeout(soTimeout);
                ftpClient.setDataTimeout(soTimeout);
            }
            if(soLinger >= 0){
                ftpClient.setSoLinger(true, soLinger);
            }
            if(isTcpNoDelay != null){
                ftpClient.setTcpNoDelay(isTcpNoDelay.booleanValue());
            }
            if(serverSocketFactory != null){
                ftpClient.setServerSocketFactory(serverSocketFactory);
            }
            if(socketFactory != null){
                ftpClient.setSocketFactory(socketFactory);
            }
            if(ftpClientProps.size() != 0){
                final PropertyAccess access = PropertyAccess.getInstance(true);
                final String[] names = (String[])ftpClientProps.keySet()
                    .toArray(new String[ftpClientProps.size()]);
                try{
                    for(int i = 0; i < names.length; i++){
                        access.set(ftpClient, names[i], ftpClientProps.get(names[i]));
                    }
                }catch(IllegalArgumentException e){
                    throw new FTPException(e);
                }catch(NoSuchPropertyException e){
                    throw new FTPException(e);
                }catch(InvocationTargetException e){
                    throw new FTPException(e.getTargetException());
                }
            }
        }catch(SocketException e){
            throw new FTPException(e);
        }
        
        FTPClientImpl client = new FTPClientImpl(ftpClient);
        if(homeDir != null){
            client.setHomeDirectory(homeDir);
        }else if(System.getProperty("user.home") != null){
            client.setHomeDirectory(new File(System.getProperty("user.home")));
        }
        client.setJavaRegexEnabled(isJavaRegexEnabled);
        if(hostName != null){
            if(port >= 0){
                if(bindAddress != null){
                    if(localPort >= 0){
                        client.connect(hostName, port, bindAddress, localPort);
                    }else{
                        client.connect(hostName, port, bindAddress, 0);
                    }
                }else{
                    if(localPort >= 0){
                        client.connect(hostName, port, "localhost", localPort);
                    }else{
                        client.connect(hostName, port);
                    }
                }
            }else{
                if(bindAddress != null){
                    if(localPort >= 0){
                        client.connect(hostName, 21, bindAddress, localPort);
                    }else{
                        client.connect(hostName, 21, bindAddress, 0);
                    }
                }else{
                    if(localPort >= 0){
                        client.connect(hostName, 21, "localhost", localPort);
                    }else{
                        client.connect(hostName);
                    }
                }
            }
            
            if(userName != null){
                client.login(userName, password);
            }
            if(isPassive){
                client.passive();
            }
        }
        
        return client;
    }
}
