/*
 * Copyright 1999-2006 University of Chicago
 *
 * 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 org.globus.ftp.dc;

import org.globus.ftp.Buffer;
import org.globus.ftp.DataSource;
import org.globus.ftp.vanilla.FTPServerFacade;
import org.globus.ftp.vanilla.BasicServerControlChannel;

import java.io.IOException;

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

/**
   Implements outgoing transfer.
   While the transfer is in progress, replies are sent to the
   local control channel. Also any failure messages go there
   in the form of a negative reply.
**/
public class TransferSourceThread extends TransferThread {

    protected static Log logger =
	LogFactory.getLog(TransferSourceThread.class.getName());

    protected DataChannelWriter writer;
    protected DataSource source;
    protected BasicServerControlChannel localControlChannel;
    protected TransferContext context;
    protected SocketBox socketBox = null;

    public TransferSourceThread(AbstractDataChannel dataChannel,
				SocketBox socketBox,
				DataSource source,
				BasicServerControlChannel localControlChannel,
				TransferContext context)
	throws Exception {
	this.socketBox = socketBox;
	this.source = source;
	this.localControlChannel = localControlChannel;
	this.context = context;
	this.writer = dataChannel.getDataChannelSink(context);
	logger.debug("using socket " + socketBox.getSocket().toString());
	writer.setDataStream(socketBox.getSocket().getOutputStream());
    }

    public void run() {
	Buffer buf;
	long transferred = 0;
	boolean error = false;
	logger.debug("TransferSourceThread executing");

	try {
	    startup();

	    try {
		while ((buf = source.read()) != null) {
		    transferred += buf.getLength();
		    writer.write(buf);
		}

		logger.debug("finished sending data; sent " +
			     transferred + " bytes");

	    } catch (Exception e) {
		// this happens also if thread gets interrupted
		error = true;
		FTPServerFacade.exceptionToControlChannel(
				    e,
				    "exception during TransferSourceThread",
				    localControlChannel);
	    }

	    Object quitToken = shutdown();

	    if (!error && (quitToken != null)) {
		//226 Transfer complete
		localControlChannel.write(new LocalReply(226));
	    }

	} catch (Exception e) {
	    FTPServerFacade.cannotPropagateError(e);
	}
    }

    protected void startup() {
	//send initial reply only if nothing has yet been sent
	synchronized(localControlChannel) {
	    if (localControlChannel.getReplyCount() == 0) {
		// 125 Data connection already open; transfer starting
		localControlChannel.write(new LocalReply(125));
	    }
	}
    }

    // called after the transfer completes, before 226
    protected Object shutdown() throws IOException {
	logger.debug("shutdown");

	// close the socket
	writer.close();

	// garbage collect the socket
	socketBox.setSocket(null);

	// attempt to obtain permission to close data source
	Object quitToken = context.getQuitToken();

	// data source is shared by all data channels,
	// so should be closed by the last one exiting
	if (quitToken != null) {
	    source.close();
	}

	return quitToken;
    }


}
