/*
 * ====================================================================
 * Copyright (c) 2004-2012 TMate Software Ltd.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://svnkit.com/license.html
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.core.internal.io.svn;

import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @version 1.3
 * @author  TMate Software Ltd.
 */
public class SVNTunnelConnector extends SVNAbstractTunnelConnector {

    private String myTunnelSpec;
    private String myName;

	public SVNTunnelConnector(String name, String tunnelSpec) {
        myName = name;
        myTunnelSpec = tunnelSpec;
    }

    public void open(SVNRepositoryImpl repository) throws SVNException {
        // 1. expand tunnel spec (when env. is used).
        final String[] expandedTunnel = expandTunnelSpec(myName, myTunnelSpec);

        final List<String> sshCommand = new ArrayList<String>();
        sshCommand.addAll(Arrays.asList(expandedTunnel));

        // 2. create tunnel command using repo URL.
        String host = repository.getLocation().getHost();
        if (repository.getLocation().getUserInfo() != null && !"".equals(repository.getLocation().getUserInfo())) {
            String username = repository.getLocation().getUserInfo();
            host = username + "@" + host;
        }
        sshCommand.add(host);
        final String svnServeCommand;
        // 3. get and append --tunnel-user if needed.
        final ISVNAuthenticationManager authManager = repository.getAuthenticationManager();
        if (authManager != null) {
            SVNAuthentication auth = authManager.getFirstAuthentication(ISVNAuthenticationManager.USERNAME, host, repository.getLocation());
            if (auth == null) {
                SVNErrorManager.cancel("Authentication cancelled", SVNLogType.NETWORK);
            }
            String userName = auth.getUserName();
            if (userName == null || "".equals(userName.trim())) {
                userName = System.getProperty("user.name");
            }
            auth = new SVNUserNameAuthentication(userName, auth.isStorageAllowed(), repository.getLocation(), false);
            BasicAuthenticationManager.acknowledgeAuthentication(true, ISVNAuthenticationManager.USERNAME, host, null, auth, repository.getLocation(), authManager);
            svnServeCommand = "svnserve -t --tunnel-user \"" + userName + "\"";
            repository.setExternalUserName(userName);
        } else {
            svnServeCommand = "svnserve -t";
        }
        sshCommand.add(svnServeCommand);

        SVNDebugLog.getDefaultLog().logFinest(SVNLogType.NETWORK, "tunnel command: " + sshCommand);
        open(repository, sshCommand.toArray(new String[sshCommand.size()]));
    }

    private static String[] expandTunnelSpec(String name, String tunnelSpec) throws SVNException {
        if (tunnelSpec == null || tunnelSpec.trim().length() == 0) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.EXTERNAL_PROGRAM, "No tunnel spec foound for ''{0}''", name);
            SVNErrorManager.error(err, SVNLogType.NETWORK);
        }
        tunnelSpec = tunnelSpec.trim();

        int spaceIndex = tunnelSpec.indexOf(' ');
        String firstSegment = spaceIndex > 0 ? tunnelSpec.substring(0, spaceIndex) : tunnelSpec;
        String lastSegment = spaceIndex > 0 ? tunnelSpec.substring(spaceIndex).trim() : tunnelSpec;

        if (firstSegment.charAt(0) == '%' && firstSegment.charAt(firstSegment.length() - 1) == '%') {
            firstSegment = firstSegment.substring(1);
            firstSegment = firstSegment.substring(0, firstSegment.length() - 1);
            firstSegment = SVNFileUtil.getEnvironmentVariable(firstSegment);
        } else if (firstSegment.charAt(0) == '$') {
            firstSegment = firstSegment.substring(1);
            firstSegment = SVNFileUtil.getEnvironmentVariable(firstSegment);
        } else {
            firstSegment = null;
            lastSegment = null;
        }
        if (firstSegment != null) {
            // was expanded.
            tunnelSpec = firstSegment;
        } else if (lastSegment != null) {
            // was expanded with no result.
            tunnelSpec = lastSegment;
        }
        return tunnelSpec.split("\\s");
    }

    public void handleExceptionOnOpen(SVNRepositoryImpl repository, SVNException exception) throws SVNException {
        throw exception;
    }
}
