package ProtocolLayer;

import Abstract.*;
import BaseLayer.*;

import java.net.*;
import java.io.*;

/**
 * ConnectorThread which takes a role to make a server socket for applet data transfer.
 * When an applet wants data transfer using FTP protocol, FTPConnectorThread will be created
 * to connect the applet and a FTP server. This connector will create one ServerSocket
 * for each FTP 'PORT' operation. From the agent perspective, it will use
 * pre-existing FTP.
 */
 /*
* Copyright (c) 1996,1997,1998 <A HREF="http://cdr.stanford.edu/">Center for Design Research</A>, Stanford University.
* All rights reserved.<p>
*
*
* 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 can obtain the GNU General Public License at
* http://www.gnu.org/copyleft/gpl.html or by writing to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* This software is bound by the terms and conditions listed in the
* attached <a href="../LICENSE">LICENSE file</A>.
*  @author <A HREF="http://cdr.stanford.edu/html/people/jeon-bio.html>Heecheol Jeon</A>
*  @version Java(tm) 1.1
*/

public class FTPConnectorThread extends ConnectorThread   {

	/**
	  * FTP Port connector thread to perform 'PORT' command. 'PORT' command will be
	  * sent from an agent, normally used to 'stor','list','retr'.
	  */
	FTPPortConnectorThread _portConnector = null;

	/**
	  * Server socket in Router machine to accept an applet connection for 'PORT' operation
	  */
	ServerSocket _portServerSocket = null;

	/**
	  * Constructor using FTP server address.
	  * @param serverAddress FTP server address
	  * @param priority Thread priority
	  * @param connectionWaitTime Maximum idle time
	  */
	public FTPConnectorThread(Address serverAddress,
													  int priority,
											 			int connectionWaitTime) throws ConnectionException  {
		super(serverAddress,priority,connectionWaitTime);
		_portConnector = null;
 }

	/**
	  * Constructor with internet service server address, priority and maximum idle time.
	  * Router will try to connect to given service address.
	  * @param toSocket Internet service server socket.
	  * @param priority Thread priority
	  * @param connectionWaitTime Maximum idle time.
	  * @exception Abstract.ConnectionException If server connection is failed, or
	  * fails to create ServerSocket for an applet.
	  */
 	public FTPConnectorThread(Socket toSocket,
												 int priority,
												 int connectionWaitTime)  throws ConnectionException {
		super(toSocket,priority,connectionWaitTime);
		_portConnector = null;
 }

/**
  * Starts to accept socket connection from an applet. Afer accepting the connection,
  * Receiver thread to receive messages from the applet will be created and started.
  * ServerSocket opened for accepting the connection will be closed. Then, receiving/sending
  * messages from both sides will be performed until "QUIT" message is received from
  * the applet side. If the applet has no chance to send 'QUIT' message to router,
  * this thread will stop after maximum idle time.
  */
 public void run()   {

		resetTimer();

    try {
			Socket client_socket = _clientServerSocket.accept();
			_clientQueue = new BMessageBuffer();
			_client = new BRecvThread(client_socket,getPriority(),null,null,_clientQueue,null);
			_client.setEndWith(_endWith);  // normally '\n'
			_client.start();
			_clientServerSocket.close();
			_server.start();
System.out.println("Connector started");
			while(true)  {
				if(!_clientQueue.isEmpty())  {
					resetTimer();
					String command = (String)_clientQueue.getMessage();
System.out.println("From FTPClient::" + command);

					String perf = command.substring(0,4);

					if(perf.equalsIgnoreCase("quit"))  {
						break;
					}
					else  {
						if(perf.equalsIgnoreCase("port"))  {
							// make a FTPPortConnector.
							try  {
								createFTPPortThread();
							} catch (ConnectionException cexception)  {
								System.out.println("PORT error");
								endConn();
								break;
							}
						}
						else  {
							_server.writeMsg(command);

							if((perf.equalsIgnoreCase("stor")) || (perf.equalsIgnoreCase("stou")))  {
								if(_portConnector == null)  {
									System.out.println("Port failed for " + command);
									endConn();
								}
								else  {
									dataTransfer(true);
								}
							}
							else
							if((perf.equalsIgnoreCase("retr")) || (perf.equalsIgnoreCase("list")) || (perf.equalsIgnoreCase("nlst")))  {
								if(_portConnector == null)  {
									System.out.println("Port failed for " + command);
									endConn();
								}
								else  {
									dataTransfer(false);
								}
							}
						}

					}
				}
				if(!_serverQueue.isEmpty())  {
					String command = ((String)_serverQueue.getMessage()).toLowerCase();
					resetTimer();

					_client.writeMsg(command);
System.out.println("From FTPServer" + command);

					// transmission success. Kill port thread
					if(command.startsWith("226"))  {
						if((_portConnector != null) && (_portConnector.isAlive()))  {
							suspend();
						}
						// wait until PortConnector wake me up
						closePortConnector();
					}
				}
				else  {
					try  {
						sleep(100);
					}  catch (InterruptedException e)  {}
				}
			}
		}  catch (Exception e)  {
			System.out.println(e.toString());
		}	finally {
			endConn();
		}
 }


 /**
   * Data transfer transaction. Currently synchronous transfer is used.
   * @param getOrPut Get or Put operation. PUT is true.
   * @exception Abstract.ConnectionException
   */
 protected void dataTransfer(boolean getOrPut) throws ConnectionException {
 	try  {
		_portConnector.setGetOrPut(getOrPut);
System.out.println("FTPPort thread started");
		_portConnector.start();
//		_portConnector.transfer();
//		closePortConnector();
 	} catch (Exception e)  {
 		throw new ConnectionException(e.toString());
 	}
}

 /**
   * Close port connector
   */
 protected void closePortConnector()  {
 		if(_portConnector != null)  {
 			_portConnector.endConn();
 			_portConnector = null;
  	}
 }

 /**
   * Send a message to server side
   * @param msg FTP message
   * @exception Abstract.ConnctionException if writing fails
   */
 protected void sendMessageToServer(String msg)
 throws ConnectionException  {
 	_server.writeMsg(msg);
 }

 /**
   * Send a message to applet side
   * @param msg FTP message
   * @exception Abstract.ConnctionException if writing fails
   */
 protected void sendMessageToClient(String msg)
 throws ConnectionException  {
 	_client.writeMsg(msg);
 }

 /**
  * Create FTP PORT thread to process data
  * FTPPortThread will create two server socket, one for server side and one for applet side.
  * Port number will be passed according to protocol format.
  * @exception Abstract.ConnectionException
  */
 protected void createFTPPortThread()
 		throws ConnectionException  {

		resetTimer();

 		try  {

 			if((_portConnector != null) && (_portConnector.isAlive())) {
 				_portConnector.stop();
 				_portConnector = null;
 			}

 			// make a new server socket
 			ServerSocket ssidess = new ServerSocket(0);

 			// send server socket information to FTP server
			System.out.println("******************");
                        int localPort = ssidess.getLocalPort();
                        System.out.println("getLocalPort: "+ssidess.getLocalPort());
			InetAddress localIp = InetAddress.getLocalHost();

                        System.out.println("getLocalHost: "+InetAddress.getLocalHost());
			byte[] addrbytes = localIp.getAddress();
                        System.out.println("getAdress: "+localIp.getAddress());
			short addrshorts[] = new short[4];

			for(int i=0; i<=3; i++){
				addrshorts[i] = addrbytes[i];
				if(addrshorts[i] < 0) addrshorts[i] += 256;
			}

			String portstr = "port ";
			portstr = portstr + addrshorts[0] + "," + addrshorts[1] + "," +
			addrshorts[2] + "," + /*addrshorts[3]*/19 + "," + ((localPort & 0xff00) >> 8) + "," + (localPort & 0x00ff);

			sendMessageToServer(portstr);
                        System.out.println("FTPTeste: "+portstr);
                        System.out.println("******************");
			// create applet side server socket
			ServerSocket ss = new ServerSocket(0);

			// send socket information to applet side. Applet should be prepared to new '299' code
			String portinfo = "299 " + ss.getLocalPort();
			sendMessageToClient(portinfo);


			Socket csocket = ss.accept();
			_portConnector = new FTPPortConnectorThread(this,ssidess,csocket,getPriority(),_maxWaitTime);
			ss.close();

	 	}  catch (Exception e)  {
	 		throw new ConnectionException(e.toString());
	 	}
 }

 /**
   * Will be invoked by timer, if time out reached. Stop FTPConPortThread if still alive
   */
 public void disconnectCon()  {
System.out.println("FTPConnectorThread disconnected " + getName());
 		if((_portConnector != null) && (_portConnector.isAlive()))  {
 			_portConnector.endConn();
 			_portConnector = null;
 		}

		super.disconnectCon();
 }

}
