/**
 * Copyright 2019 Huawei Technologies Co.,Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License.  You may obtain a copy of the
 * License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.obs.services;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;

import com.obs.services.internal.ObsConstraint;
import com.obs.services.model.AuthTypeEnum;
import com.obs.services.model.HttpProtocolTypeEnum;

import okhttp3.Dispatcher;

/**
 * Configuration parameters of ObsClient
 */
public class ObsConfiguration implements Cloneable {

    private int connectionTimeout;

    private int idleConnectionTime;

    private int maxIdleConnections;

    private int maxConnections;

    private int maxErrorRetry;

    private int socketTimeout;

    private String endPoint;

    private int endpointHttpPort;

    private int endpointHttpsPort;

    private boolean httpsOnly;

    private boolean pathStyle;

    private HttpProxyConfiguration httpProxy;

    private int uploadStreamRetryBufferSize;

    private boolean validateCertificate;

    private boolean verifyResponseContentType;

    private int readBufferSize;

    private int writeBufferSize;

    private KeyManagerFactory keyManagerFactory;

    private TrustManagerFactory trustManagerFactory;

    private boolean isStrictHostnameVerification;

    private AuthTypeEnum authType;

    private String signatString;
    private String defaultBucketLocation;
    private int bufferSize;
    private int socketWriteBufferSize;
    private int socketReadBufferSize;
    private boolean isNio;
    private boolean useReaper;
    private boolean keepAlive;
    private int connectionRequestTimeout;
    private boolean authTypeNegotiation;

    private boolean cname;

    private String delimiter;

    private String sslProvider;

    private HttpProtocolTypeEnum httpProtocolType;

    private Dispatcher httpDispatcher;

    /**
     * Constructor
     */
    public ObsConfiguration() {
        this.connectionTimeout = ObsConstraint.HTTP_CONNECT_TIMEOUT_VALUE;
        this.maxConnections = ObsConstraint.HTTP_MAX_CONNECT_VALUE;
        this.maxErrorRetry = ObsConstraint.HTTP_RETRY_MAX_VALUE;
        this.socketTimeout = ObsConstraint.HTTP_SOCKET_TIMEOUT_VALUE;
        this.endpointHttpPort = ObsConstraint.HTTP_PORT_VALUE;
        this.endpointHttpsPort = ObsConstraint.HTTPS_PORT_VALUE;
        this.httpsOnly = true;
        this.endPoint = "";
        this.pathStyle = false;
        this.validateCertificate = false;
        this.verifyResponseContentType = true;
        this.isStrictHostnameVerification = false;
        this.uploadStreamRetryBufferSize = -1;
        this.socketWriteBufferSize = -1;
        this.socketReadBufferSize = -1;
        this.readBufferSize = -1;
        this.writeBufferSize = -1;
        this.idleConnectionTime = ObsConstraint.DEFAULT_IDLE_CONNECTION_TIME;
        this.maxIdleConnections = ObsConstraint.DEFAULT_MAX_IDLE_CONNECTIONS;
        this.authType = AuthTypeEnum.OBS;
        this.keepAlive = true;
        this.signatString = "";
        this.defaultBucketLocation = "";
        this.authTypeNegotiation = true;
        this.cname = false;
        this.delimiter = "/";
        this.httpProtocolType = HttpProtocolTypeEnum.HTTP1_1;
    }

    public String getDelimiter() {
        return delimiter;
    }

    /**
     * Set folder isolators to slashes.
     * 
     * @param delimiter
     *            Folder isolator
     */
    public void setDelimiter(String delimiter) {
        this.delimiter = delimiter;
    }

    @Deprecated
    public String getSignatString() {
        return signatString;
    }

    @Deprecated
    public void setSignatString(String signatString) {
        this.signatString = signatString;
    }

    @Deprecated
    public String getDefaultBucketLocation() {
        return defaultBucketLocation;
    }

    @Deprecated
    public void setDefaultBucketLocation(String defaultBucketLocation) {
        this.defaultBucketLocation = defaultBucketLocation;
    }

    @Deprecated
    public int getBufferSize() {
        return bufferSize;
    }

    @Deprecated
    public void setBufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
    }

    /**
     * Check whether path-style access to OBS is enabled. "true" indicates that
     * path-style access is enabled while "false" (default) indicates virtual
     * hosted-style access is enabled. Note: If the path-style access is
     * enabled, new bucket features of OBS 3.0 are not supported.
     * 
     * @return Whether to enable path-style access to OBS.
     */
    @Deprecated
    public boolean isDisableDnsBucket() {
        return this.isPathStyle();
    }

    /**
     * Specify whether to enable path-style access to OBS. "true" indicates that
     * path-style access is enabled while "false" (default) indicates that
     * virtual hosted-style access is enabled. Note: If the path-style access is
     * enabled, new bucket features of OBS 3.0 are not supported.
     * 
     * @param disableDns
     *            Whether to enable path-style access to OBS.
     */
    @Deprecated
    public void setDisableDnsBucket(boolean disableDns) {
        this.setPathStyle(disableDns);
    }

    /**
     * Obtain the size (bytes) of the socket receive buffer, corresponding to
     * the "java.net.SocketOptions.SO_RVCBUF" parameter. The default value is
     * "-1", indicating that the size is not set.
     * 
     * @return Socket receive buffer size
     */
    public int getSocketReadBufferSize() {
        return socketReadBufferSize;
    }

    /**
     * Set the size (bytes) of the socket receive buffer, corresponding to the
     * "java.net.SocketOptions.SO_RVCBUF" parameter. The default value is "-1",
     * indicating that the size is not set.
     * 
     * @param socketReadBufferSize
     *            Socket receive buffer size
     */
    public void setSocketReadBufferSize(int socketReadBufferSize) {
        this.socketReadBufferSize = socketReadBufferSize;
    }

    /**
     * Obtain the size (bytes) of the socket send buffer, corresponding to the
     * "java.net.SocketOptions.SO_SNDBUF" parameter. The default value is "-1",
     * indicating that the size is not set.
     * 
     * @return socketSocket send buffer size
     */
    public int getSocketWriteBufferSize() {
        return socketWriteBufferSize;
    }

    /**
     * Set the size (bytes) of the socket send buffer (in bytes), corresponding
     * to the "java.net.SocketOptions.SO_SNDBUF" parameter. The default value is
     * "-1", indicating that the size is not set.
     * 
     * @param socketWriteBufferSize
     *            socket Socket send buffer size
     */
    public void setSocketWriteBufferSize(int socketWriteBufferSize) {
        this.socketWriteBufferSize = socketWriteBufferSize;
    }

    @Deprecated
    public int getConnectionRequestTimeout() {
        return connectionRequestTimeout;
    }

    @Deprecated
    public void setConnectionRequestTimeout(int connectionRequestTimeout) {
        this.connectionRequestTimeout = connectionRequestTimeout;
    }

    @Deprecated
    public void disableNio() {
        this.isNio = false;
    }

    @Deprecated
    public void enableNio() {
        this.isNio = true;
    }

    @Deprecated
    public boolean isNio() {
        return this.isNio;
    }

    @Deprecated
    public boolean isUseReaper() {
        return useReaper;
    }

    @Deprecated
    public void setUseReaper(boolean useReaper) {
        this.useReaper = useReaper;
    }

    /**
     * Obtain the factory for generating the "KeyManager" array.
     * 
     * @return Factory for generating the "KeyManager" array
     */
    public KeyManagerFactory getKeyManagerFactory() {
        return keyManagerFactory;
    }

    /**
     * Set the factory for generating the "KeyManager" array.
     * 
     * @param keyManagerFactory
     *            Factory for generating the "KeyManager" array
     */
    public void setKeyManagerFactory(KeyManagerFactory keyManagerFactory) {
        this.keyManagerFactory = keyManagerFactory;
    }

    /**
     * Obtain the factory for generating the "TrustManager" array.
     * 
     * @return Factory for generating the "TrustManager" array
     */
    public TrustManagerFactory getTrustManagerFactory() {
        return trustManagerFactory;
    }

    /**
     * Set the factory for generating the "TrustManager" array.
     * 
     * @param trustManagerFactory
     *            Factory for generating the "TrustManager" array
     */
    public void setTrustManagerFactory(TrustManagerFactory trustManagerFactory) {
        this.trustManagerFactory = trustManagerFactory;
    }

    /**
     * Obtain the identifier specifying whether to verify the domain name
     * ("false" by default).
     * 
     * @return Identifier specifying whether to verify the domain name
     */
    public boolean isStrictHostnameVerification() {
        return isStrictHostnameVerification;
    }

    /**
     * Set the identifier specifying whether to verify the domain name.
     * 
     * @param isStrictHostnameVerification
     *            Identifier specifying whether to verify the domain name
     */
    public void setIsStrictHostnameVerification(boolean isStrictHostnameVerification) {
        this.isStrictHostnameVerification = isStrictHostnameVerification;
    }

    /**
     * Check whether path-style access to OBS is enabled. "true" indicates that
     * path-style access is enabled while "false" (default) indicates virtual
     * hosted-style access is enabled. Note: If the path-style access is
     * enabled, new bucket features of OBS 3.0 are not supported.
     * 
     * @return Whether to enable path-style access to OBS.
     */
    @Deprecated
    public boolean isPathStyle() {
        return pathStyle;
    }

    /**
     * Specify whether to enable path-style access to OBS. "true" indicates that
     * path-style access is enabled while "false" (default) indicates that
     * virtual hosted-style access is enabled. Note: If the path-style access is
     * enabled, new bucket features of OBS 3.0 are not supported.
     * 
     * @param pathStyle
     *            Whether to enable path-style access to OBS.
     */
    @Deprecated
    public void setPathStyle(boolean pathStyle) {
        this.pathStyle = pathStyle;
    }

    /**
     * Obtain the timeout interval for establishing HTTP/HTTPS connections (in
     * milliseconds, 60,000 milliseconds by default).
     * 
     * @return Timeout interval for establishing HTTP/HTTPS connections
     */
    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    /**
     * Set the timeout interval for establishing HTTP/HTTPS connections (in
     * milliseconds, 60,000 milliseconds by default).
     * 
     * @param connectionTimeout
     *            Timeout interval for establishing HTTP/HTTPS connections
     */
    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    /**
     * Obtain the maximum number of HTTP connections (1000 by default) that can
     * be opened.
     * 
     * @return Maximum number of concurrently opened HTTP connections
     */
    public int getMaxConnections() {
        return maxConnections;
    }

    /**
     * Set the maximum number of HTTP connections (1000 by default) that can be
     * opened.
     * 
     * @param maxConnections
     *            Maximum number of concurrently opened HTTP connections
     */
    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    /**
     * Obtain the maximum number of retry attempts (5 by default) upon a request
     * failure (request exception, or error 500 or 503 on the server).
     * 
     * @return Maximum number of retry attempts upon a request failure
     */
    public int getMaxErrorRetry() {
        return maxErrorRetry;
    }

    /**
     * Set the maximum number of retry attempts (5 by default) upon a request
     * failure (request exception, or error 500 or 503 on the server).
     * 
     * @param maxErrorRetry
     *            Maximum number of retry attempts upon a request failure
     */
    public void setMaxErrorRetry(int maxErrorRetry) {
        this.maxErrorRetry = maxErrorRetry;
    }

    /**
     * Obtain the timeout interval for data transmission at the socket layer (in
     * milliseconds, 60,000 milliseconds by default).
     * 
     * @return Timeout interval for data transmission at the socket layer
     */
    public int getSocketTimeout() {
        return socketTimeout;
    }

    /**
     * Set the timeout interval for data transmission at the socket layer (in
     * milliseconds, 60,000 milliseconds by default).
     * 
     * @param socketTimeout
     *            Timeout interval for data transmission at the socket layer
     */
    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    /**
     * Obtain the endpoint for accessing OBS.
     * 
     * @return OBS endpoint
     */
    public String getEndPoint() {
        if (endPoint == null || endPoint.trim().equals("")) {
            throw new IllegalArgumentException("EndPoint is not set");
        }
        return endPoint.trim();
    }

    /**
     * Set the endpoint for accessing OBS.
     * 
     * @param endPoint
     *            OBS endpoint
     */
    public void setEndPoint(String endPoint) {
        this.endPoint = endPoint;
    }

    /**
     * Obtain the port number of the HTTP request (80 by default).
     * 
     * @return Port number of the HTTP request
     */
    @Deprecated
    public int getEndpointHttpPort() {
        return endpointHttpPort;
    }

    /**
     * Set the port number of the HTTP request (80 by default).
     * 
     * @param endpointHttpPort
     *            Port number of the HTTP request
     */
    @Deprecated
    public void setEndpointHttpPort(int endpointHttpPort) {
        this.endpointHttpPort = endpointHttpPort;
    }

    /**
     * Obtain the port number of the HTTPS request (443 by default).
     * 
     * @return Port number of the HTTPS request
     */
    @Deprecated
    public int getEndpointHttpsPort() {
        return endpointHttpsPort;
    }

    /**
     * Set the port number of the HTTPS request (443 by default).
     * 
     * @param endpointHttpsPort
     *            Port number of the HTTPS request
     */
    @Deprecated
    public void setEndpointHttpsPort(int endpointHttpsPort) {
        this.endpointHttpsPort = endpointHttpsPort;
    }

    /**
     * Specify whether to use HTTPS to connect OBS ("true" by default).
     * 
     * @param httpsOnly
     *            Identifier specifying whether to use HTTPS to connect OBS
     */
    @Deprecated
    public void setHttpsOnly(boolean httpsOnly) {
        this.httpsOnly = httpsOnly;
    }

    /**
     * Check whether HTTPS is used to connect OBS ("true" by default).
     * 
     * @return Identifier specifying whether to use HTTPS to connect OBS
     */
    @Deprecated
    public boolean isHttpsOnly() {
        return httpsOnly;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * Obtain the proxy configuration.
     * 
     * @return Proxy configuration
     */
    public HttpProxyConfiguration getHttpProxy() {
        return httpProxy;
    }

    /**
     * Configure the proxy.
     * 
     * @param httpProxy
     *            HTTP proxy configuration
     */
    public void setHttpProxy(HttpProxyConfiguration httpProxy) {
        this.httpProxy = httpProxy;
    }

    /**
     * Configure the proxy server.
     * 
     * @param proxyAddr
     *            Proxy server address
     * @param proxyPort
     *            Proxy server port
     * @param userName
     *            Proxy username
     * @param password
     *            Proxy password
     * @param domain
     *            Proxy domain
     */
    @Deprecated
    public void setHttpProxy(String proxyAddr, int proxyPort, String userName, String password, String domain) {
        this.httpProxy = new HttpProxyConfiguration(proxyAddr, proxyPort, userName, password, domain);
    }

    /**
     * Configure the proxy server.
     * 
     * @param proxyAddr
     *            Proxy server address
     * @param proxyPort
     *            Proxy server port
     * @param userName
     *            Proxy username
     * @param password
     *            Proxy password
     */
    public void setHttpProxy(String proxyAddr, int proxyPort, String userName, String password) {
        this.httpProxy = new HttpProxyConfiguration(proxyAddr, proxyPort, userName, password, null);
    }

    /**
     * Set the buffer size used for uploading stream objects (in bytes). The
     * default value is 512 KB.
     * 
     * @param uploadStreamRetryBufferSize
     *            Buffer size used for uploading stream objects
     */
    @Deprecated
    public void setUploadStreamRetryBufferSize(int uploadStreamRetryBufferSize) {
        this.uploadStreamRetryBufferSize = uploadStreamRetryBufferSize;
    }

    /**
     * Obtain the buffer size used for uploading stream objects (in bytes). The
     * default value is 512 KB.
     * 
     * @return Buffer size used for uploading stream objects
     */
    @Deprecated
    public int getUploadStreamRetryBufferSize() {
        return this.uploadStreamRetryBufferSize;
    }

    /**
     * Check whether server-side verification is enabled. The default value is
     * "false".
     * 
     * @return Identifier specifying whether to enable server-side verification
     */
    public boolean isValidateCertificate() {
        return validateCertificate;
    }

    /**
     * Specify whether to enable server-side certificate verification. The
     * default value is "false".
     ** 
     * @param validateCertificate
     *            Identifier specifying whether to enable server-side
     *            verification
     */
    public void setValidateCertificate(boolean validateCertificate) {
        this.validateCertificate = validateCertificate;
    }

    /**
     * Check whether "ContentType" in the response is verified. The default
     * value is "true".
     * 
     * @return Identifier specifying whether to verify "ContentType" in the
     *         response
     */
    public boolean isVerifyResponseContentType() {
        return verifyResponseContentType;
    }

    /**
     * Specify whether to verify "ContentType" in the response. The default
     * value is "true".
     * 
     * @param verifyResponseContentType
     *            Identifier specifying whether to verify "ContentType" in the
     *            response
     */
    public void setVerifyResponseContentType(boolean verifyResponseContentType) {
        this.verifyResponseContentType = verifyResponseContentType;
    }

    /**
     * Obtain the read cache size used for uploading objects to socket streams
     * (in bytes). The default value is 8192.
     * 
     * @return Read cache size used for uploading objects to socket streams
     */
    public int getReadBufferSize() {
        return readBufferSize;
    }

    /**
     * Set the read cache size used for uploading objects to socket streams (in
     * bytes). The default value is 8192.
     * 
     * @param readBufferSize
     *            Read cache size used for uploading objects to socket streams
     */
    public void setReadBufferSize(int readBufferSize) {
        this.readBufferSize = readBufferSize;
    }

    /**
     * Obtain the write cache size used for uploading objects to socket streams
     * (in bytes). The default value is 8192.
     * 
     * @return Write cache size used for uploading objects to socket streams
     */
    public int getWriteBufferSize() {
        return writeBufferSize;
    }

    /**
     * Set the write cache size used for uploading objects to socket streams (in
     * bytes). The default value is 8192.
     * 
     * @param writeBufferSize
     *            Write cache size used for uploading objects to socket streams
     */
    public void setWriteBufferSize(int writeBufferSize) {
        this.writeBufferSize = writeBufferSize;
    }

    /**
     * Obtain the idle time for obtaining connections from the connection pool.
     * The unit is millisecond and the default value is 30000.
     * 
     * @return Maximum idle time for obtaining connections from the connection
     *         pool
     */
    public int getIdleConnectionTime() {
        return idleConnectionTime;
    }

    /**
     * Set the idle time for obtaining connections from the connection pool. The
     * unit is millisecond and the default value is 30000.
     * 
     * @param idleConnectionTime
     *            Maximum idle time for obtaining connections from the
     *            connection pool
     */
    public void setIdleConnectionTime(int idleConnectionTime) {
        this.idleConnectionTime = idleConnectionTime;
    }

    /**
     * Obtain the maximum number of idle connections in the connection pool. The
     * default value is 1000.
     * 
     * @return Maximum number of idle connections in the connection pool
     */
    public int getMaxIdleConnections() {
        return maxIdleConnections;
    }

    /**
     * Set the maximum number of idle connections in the connection pool. The
     * default value is 1000.
     * 
     * @param maxIdleConnections
     *            Maximum number of idle connections in the connection pool
     */
    public void setMaxIdleConnections(int maxIdleConnections) {
        this.maxIdleConnections = maxIdleConnections;
    }

    /**
     * Obtain the authentication type.
     * 
     * @return Authentication type
     */
    public AuthTypeEnum getAuthType() {
        return authType;
    }

    /**
     * Set the authentication type.
     * 
     * @param authType
     *            Authentication type
     */
    public void setAuthType(AuthTypeEnum authType) {
        this.authType = authType;
    }

    /**
     * Specify whether to use persistent connections.
     * 
     * @return Identifier specifying whether to use the persistent connections
     */
    public boolean isKeepAlive() {
        return keepAlive;
    }

    /**
     * Check whether persistent connections are used.
     * 
     * @param keepAlive
     *            Identifier specifying whether to use the persistent
     *            connections
     */
    public void setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    /**
     * Check whether protocol negotiation is used.
     * 
     * @return Identifier specifying whether to use protocol negotiation
     */
    public boolean isAuthTypeNegotiation() {
        return authTypeNegotiation;
    }

    /**
     * Specify whether to use protocol negotiation.
     * 
     * @param authTypeNegotiation
     *            Identifier specifying whether to use protocol negotiation
     */
    public void setAuthTypeNegotiation(boolean authTypeNegotiation) {
        this.authTypeNegotiation = authTypeNegotiation;
    }

    /**
     * Check whether user-defined domain name is used.
     * 
     * @return Identifier specifying whether to use user-defined domain name
     */
    public boolean isCname() {
        return cname;
    }

    /**
     * Specify whether to use user-defined domain name.
     * 
     * @param cname
     *            Identifier specifying whether to use user-defined domain name
     */
    public void setCname(boolean cname) {
        this.cname = cname;
    }

    /**
     * Set the provider of SSLContext.
     * 
     * @return SSLContext provider
     */
    public String getSslProvider() {
        return sslProvider;
    }

    /**
     * Obtain the provider of SSLContext.
     * 
     * @param sslProvider
     *            SSLContext provider
     */
    public void setSslProvider(String sslProvider) {
        this.sslProvider = sslProvider;
    }

    /**
     * Set the HTTP type used for accessing OBS servers.
     * 
     * @return HTTP type
     */
    public HttpProtocolTypeEnum getHttpProtocolType() {
        return httpProtocolType;
    }

    /**
     * Obtain the HTTP type used for accessing OBS servers.
     * 
     * @param httpProtocolType
     *            HTTP type
     */
    public void setHttpProtocolType(HttpProtocolTypeEnum httpProtocolType) {
        this.httpProtocolType = httpProtocolType;
    }

    /**
     * Set the customized dispatcher.
     * 
     * @return Customized dispatcher
     */
    public Dispatcher getHttpDispatcher() {
        return httpDispatcher;
    }

    /**
     * Obtain the customized dispatcher.
     * 
     * @param httpDispatcher
     *            Customized dispatcher
     */
    public void setHttpDispatcher(Dispatcher httpDispatcher) {
        this.httpDispatcher = httpDispatcher;
    }

}
