#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <qftp.h>
#include <qtimer.h>
#include <qurloperator.h>

#include "ftpput.h"

CFTPPut::CFTPPut(int argc, char **argv)
	: QApplication(argc, argv, FALSE) // Non-GUI
{
   pClient = new CFTPClient();
   connect(pClient, SIGNAL(StateChange(int, int)), this, SLOT(Action(int, int)));
   CommandList.setAutoDelete(TRUE);
   Done = FALSE;
   m_CurrentCommand = CFTPClient::cmdNop;
}


CFTPPut::~CFTPPut()
{
   if (CommandList.count() > 1) // The last command can be Quit
     qDebug("CFTPPut::~CFTPPut: Command list not empty!");
   delete pClient;
}

// private

void CFTPPut::Usage()
{
   qDebug("\
Not enough/wrong arguments. Syntax:\
\
   ftpput file [options] [file ...] user:password@server[/path]\
\
The file is uploaded to the server using the FTP protocol. Path can be\
relative to the user's directory or absolute (use a double /).\
\
Options:\
  --passive         Use passive FTP to upload data\
\
");
}

/** 
  \brief Parse user:pass@server[:path] string and put the strings into the variables.
 */
bool CFTPPut::ParseUPSP(char *s)
{
   char *t, *colon, *at, *slash;
   
   t = strdup(s);
   if (t == NULL)
     return FALSE;
   colon = strchr(t, ':');
   at = strchr(t, '@');
   if (at == NULL)
     return FALSE;
   slash = strchr(at, '/');
   if (colon == NULL || at == NULL || at < colon)
     return FALSE;
   *colon++ = '\0'; // trim strings
   *at++ = '\0';
   if (slash != NULL) {
     *slash = '\0';
     slash++;
   }
//qDebug("parsed: %s %s %s %s", t, colon, at, slash);
   User = t;
   Pass = colon;
   Server = at;
   Path = slash;

   memset(s, 'X', strlen(s)); // Hide parameters
   free(t);

   return TRUE;
}

// private

void CFTPPut::TriggerNext()
{
   QTimer::singleShot(0, this, SLOT(ProcessNextCommand()));
}

// private slots

void CFTPPut::ProcessNextCommand()
{
   struct CommandStruct *pStruct;

   m_CurrentCommand = CFTPClient::cmdNop;
   pStruct = CommandList.first(); // Take first item
   if (pStruct == NULL) {
     qDebug("Command queue is empty. Exiting.");
     exit();
   }

qDebug("ProcessNextCommand: cmd = %d (\"%s\"), arg1 = '%s'", pStruct->Command, CFTPClient::CommandStr[pStruct->Command], (const char *)pStruct->Param[0]);
   m_CurrentCommand = pStruct->Command;
   switch (m_CurrentCommand) {
     case CFTPClient::cmdLogin:
       pClient->Connect(pStruct->Param[1], pStruct->Param[2], pStruct->Param[0]);
       break;
     
     case CFTPClient::cmdSetType:
       if (pStruct->Param[0] == "A")
         pClient->SetTypeAscii();
       else
         pClient->SetTypeBinary();
       break;
     
     case CFTPClient::cmdChangeDir:
       pClient->ChangeDir(pStruct->Param[0]);
       break;
       
     case CFTPClient::cmdUpload:
       pClient->Upload(pStruct->Param[0]);
       break;
       
     case CFTPClient::cmdLogout:
       pClient->Logout();
       break;
       
     case CFTPClient::cmdListDir:
       pClient->ListDir();
       break;

     case CFTPClient::cmdPassive:
       pClient->SetPassive();
       TriggerNext();
       break;
       
     case CFTPClient::cmdRename:
       pClient->Rename(pStruct->Param[0], pStruct->Param[1]);
       break;

     default: 
       qDebug("Unknown command.... bleh");
       exit(1);
       break;
   }
   CommandList.remove(); //clean up
}



// private slots

void CFTPPut::Action(int status, int result)
{
   if (m_CurrentCommand == CFTPClient::cmdNop) {
     qWarning("Action was called, but there was no command in queue. Duh?");
     Done = TRUE;
     return;
   }

qDebug("CFTPPut::Action() New state: status = %d (\"%s\"), cmd = %d (\"%s\"), result = %d", status, CFTPClient::StateStr[status], m_CurrentCommand, CFTPClient::CommandStr[m_CurrentCommand], result);

   switch(status) {
     case CFTPClient::stNOC:
       if (m_CurrentCommand == CFTPClient::cmdLogout)
         qDebug("Logout completed.");
       else
         qDebug("Connection closed by foreign host.");
       Done = TRUE;
       break;
     
     case CFTPClient::stFailed:
       Done = TRUE; // Default action is to stop
       switch(m_CurrentCommand) {
         case CFTPClient::cmdLogin:
           qDebug("Login failed. Giving up.");
           break;

         case CFTPClient::cmdChangeDir:
           qDebug("Directory not found. Aborting.");
           break;
         
         case CFTPClient::cmdUpload:
           qDebug("Upload failed. Skipping to next.");
           Done = FALSE;
           TriggerNext();
           break;

         default:
           qDebug("Command %d failed.", m_CurrentCommand);
       }
       break;
   
     case CFTPClient::stIdle:
       switch(m_CurrentCommand) {
         case CFTPClient::cmdLogin:
           qDebug("Logged in.");
           break;

         case CFTPClient::cmdUpload:
         case CFTPClient::cmdDownload:
           qDebug("Transfer completed.");
           break;
            
         case CFTPClient::cmdListDir:
           qDebug("Directory listing completed.");
           break;
       }
       TriggerNext();
       break;
       
     case CFTPClient::stUnknown:
       qDebug("Command resulted in unkown or invalid response");
       break;
   }
}


void CFTPPut::Progress(int size)
{
   qDebug("Uploaded %d bytes", size);
}

// public

void CFTPPut::Run()
{
   struct CommandStruct *pStruct;
   int i;

   qDebug("CFTPPut::Run() starting.");

   if (argc() < 3) {
     Usage();
     exit(1);
     return;
   }
   
   if (!ParseUPSP(argv()[argc() - 1])) {
     Usage();
     exit(1);
     return;
   }
   
   pStruct = new CommandStruct;
   pStruct->Command = CFTPClient::cmdLogin;
   pStruct->Param[0] = Server;
   pStruct->Param[1] = User;
   pStruct->Param[2] = Pass;
   CommandList.append(pStruct);

   if (!Path.isEmpty()) {   
     pStruct = new CommandStruct;
     pStruct->Command = CFTPClient::cmdChangeDir;    
     pStruct->Param[0] = Path;
     CommandList.append(pStruct);
   }

#if 0   
   pStruct = new CommandStruct;
   pStruct->Command = CFTPClient::cmdSetType;
   pStruct->Param[0] = "A";
   CommandList.append(pStruct);

   pStruct = new CommandStruct;
   pStruct->Command = CFTPClient::cmdListDir;
   CommandList.append(pStruct);
#endif

   /* Later, this syntax might get introduced: 
      file [file ...] user:pass@server[:path] [file [file ... ] user2:pass2@server2[:path]]*
    */
   pStruct = new CommandStruct;
   pStruct->Command = CFTPClient::cmdSetType;
   pStruct->Param[0] = "B";
   CommandList.append(pStruct);

   i = 1;
   if (!strcmp(argv()[i], "--passive")) {
     i++;
     pStruct = new CommandStruct;
     pStruct->Command = CFTPClient::cmdPassive;
     CommandList.append(pStruct);
   }
   
   for (; i < argc() - 1; i++) {
      pStruct = new CommandStruct;
      pStruct->Command = CFTPClient::cmdUpload;
      pStruct->Param[0] = argv()[i];
      CommandList.append(pStruct);   
   }
   
   pStruct = new CommandStruct;
   pStruct->Command = CFTPClient::cmdLogout;
   CommandList.append(pStruct);
   TriggerNext();

   do {
     processOneEvent();
   } while (!Done);
   qDebug("CFTPPut::Run() done.");
   exit();
}

// main

int main(int argc, char *argv[])
{
   CFTPPut Put(argc, argv);
   QTimer::singleShot(0, &Put, SLOT(Run()));
   Put.exec();
}
