#ifndef FTPCLIENT_HPP
#define FTPCLIENT_HPP

#if HAVE_CONFIG_H
#include "config.h"
#endif


#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <qsocketnotifier.h>
#include <qstring.h>

class CFTPClient: public QObject
{
   Q_OBJECT
public:
   enum Commands {
     cmdNop,
     cmdLogin,
     cmdLogout,
     cmdSetType,
     cmdChangeDir,
     cmdListDir,
     cmdUpload,
     cmdDownload,
     cmdRename,
     // end of real commands

     cmdPassive,
     cmdDestroy,
   };

   enum States {
     stNOC,		///< Not connected
     stConnecting,	///< Busy connecting to server
     stConnected,	///< Server reached; awaiting response from FTP daemon
     stLogin,		///< Sending username
     stAuthenticate,	///< Sending password

     stIdle,		///< Connected OK, but not doing anything

     stSendingPort,	///< Sending PORT command to remote end
     stWaitData,	///< Waiting for data connection
     stTransfer,	///< Transfering data
     stClosingData,	///< Waiting for 226

     stFailed,		///< Failed status; can happen at every command
     stUnknown,		///< Unkown or unexpected result code
   };


private:
   QString UserName, Password;
   struct sockaddr_in MyAddr, ServerAddress;

   int CtrlFD, ListenFD, DataFD, LocalFileFD;
   char *inputbuffer, *linebuffer, *responsebuffer;
   char LastChar;
   bool MultiLine;
   int LineLen, RespLen; // Characters in linebuffer resp. response buffer
   int Response;         // FTP response code
   bool Direction; 	 // false = in, true = out
   QString RemoteFileName;

   bool m_Passive;	 // Are we in passive mode?
   unsigned int m_PassiveAddress;
   unsigned short m_PassivePort;

   QString outputbuffer;

   uchar *transferbuffer;
   int TBufLen, TBufTail, TBufHead, TBufUsed; // rotating buffer
   int TotalTransfered;

   QSocketNotifier *pControlPipe, *pListenPipe, *pDataPipe;

   int TransferMethod; // ASCII vs. BINARY
   States CurrentState;
   Commands CurrentCommand;

   void InitBuffers();
   void CloseAllFD();
   int SetupControl();
   void CloseControl();
   int SetupListen();
   void CloseListen();
   int SetupLocal(const QString &filename, bool write);
   void CloseFile();
   int SetupDataActive();
   int SetupDataPassive();
   void CloseData();
   void HookData();
   
   void SetState(States new_op, int response = -1);

   void InterpretLine();
   void InterpretResponse();

   void Send();
   void SendUser();
   void SendPass();
   void SendList();
   void SendPort(struct sockaddr_in &addr, int port);
   void SendStore(const QString &filename);

   void StartSending();
   void StartReceiving();

private slots:
   void ControlRead(int fd);
   void ListenRead(int fd);
   void DataRead(int fd);
   void DataWrite(int fd);
   void DataConnect(int fd);

public:
   static const char *CommandStr[];
   static const char *StateStr[];   

   CFTPClient();
   ~CFTPClient();

   int GetCommand() const;
   int GetState() const;
   void SetPassive();
   void SetActive();

   void Connect(const QString &user, const QString &pass, const QString &server, int port = 21);
   
   void Upload(const QString &local_file, const QString &remote_file = QString::null);
   void SetTypeAscii();
   void SetTypeBinary();
   void ChangeDir(const QString &new_dir);
   void ListDir();
   void Rename(const QString &from, const QString &to);

   void Logout();
   
   QString GetErrorString(int result) const;

signals:
   /** This signal is emitted whenever the internal state machine changes.
     The result is the numeric FTP result code, if known. There are however a 
     few extra 'result' codes defined, for situations that do not apply to 
     FTP servers:
     
     800 Local file not found (upload)
     810 Server name not resolved
     811 Failed to connect to server
     
     
    */
   void StateChange(int command, int new_state, int result, const QString &server_msg);

   /// Emitted when login was succesfull
   void LoggedIn();
   /// Emitted when login failed
   void LoginFailed();
   /// Emitted when the control connection was closed (either by us or the remote party)
   void ControlPortClosed();
   void TimeOut();
   
   void ListDirEntry(const QString &filename);

   /// Emitted every now and then to report on progress. Reports total amount of bytes transfered so far   
   void Progress(int offset);
};

#endif
