/***************************************************************************
                          application.cpp  -  description
                             -------------------
    begin                : Thu Mar 20 2003
    copyright            : (C) 2003 by Mike K. Bennett
    email                : mkb137b@hotmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "application.h"

#include <stdlib.h>

#include <kdebug.h>
#include <klocale.h>

#include "../../currentaccount.h"
#include "../../kmessdebug.h"
#include "../mimemessage.h"

// The constructor
Application::Application(const QString& localIp)
 : QObject(0, "*Application"),
   localIp_(localIp),
   mode_(APP_MODE_NORMAL),
   userAccepted_(false),
   userStartedApp_(true),
   waitingForUser_(false)
{
}



// The destructor
Application::~Application()
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "DESTROYED Application" << endl;
#endif
}



// The contact aborted the session
void Application::contactAborted(const QString &message)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - contactAborted" << endl;
#endif

  if( message == 0 )
  {
    endApplication( i18n("The contact cancelled the session.") );
  }
  else
  {
    endApplication( message );
  }
}



// The contact rejected the invitation
void Application::contactRejected(const QString &message)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - contactRejected" << endl;
#endif

  if( message == 0 )
  {
    endApplication( i18n("The contact rejected the invitation.") );
  }
  else
  {
    endApplication( message );
  }
}



// Step one of a contact-started chat: the contact invites the user
// By default the invitation will be declined unless you override this method.
void Application::contactStarted1_ContactInvitesUser(const MimeMessage& /*message*/)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - contactStarted1_ContactInvitesUser (rejects invitation)" << endl;
#endif

  // Cancel the invitation
  sendCancelMessage( CANCEL_NOT_INSTALLED );

  // We can assume the switchboard already displayed a better error message
#ifdef KMESTEST
  kdWarning() << "The Application subclass didn't implement contactStarted1_ContactInvitesUser!" << endl;
#endif
  endApplication();
}



// Step two of a contact-started chat: the user accepts
void Application::contactStarted2_UserAccepts()
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - contactStarted2_UserAccepts" << endl;
#endif
}



// Step three of a contact-started chat: the contact confirms the accept
void Application::contactStarted3_ContactConfirmsAccept(const MimeMessage& /*message*/)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - contactStarted3_ContactConfirmsAccept" << endl;
#endif
}



// Close the appliation (asks switchboard to delete this object)
void Application::endApplication(const QString &reason)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - Requesting removal" << endl;
#endif

  // Display a message if provided.
  if(reason != 0)
  {
    emit appInitMessage(reason);
  }

  // Signal that this instance should be deleted.
  emit deleteMe(this);
}



// Free a used port
void Application::freePort(const int& port)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - Freeing port " << port << "." << endl;
#endif

  // TODO: Should mark a port from getAvailablePort() as free again.
}



// Generate a random file or authorization cookie.
QString Application::generateCookie() const
{
  int     number, maxNumber;
  QString cookie;

  // The maximum size for a cookie is supposedly 2^32 - 1, though I've
  //  never seen one go higher than 100,000.
  maxNumber = 1048575;
  // Get a random number in the given range.
  number = rand()%maxNumber;
  // Convert the number to a QCString
  cookie.sprintf("%d", number);
  // Return the cookie
  return cookie;
}


// Return a cancel message to the user
QString Application::getCancelMessage() const
{
  return i18n("You have cancelled the session.");
}


// Return the application's identifying cookie
const QString& Application::getCookie() const
{
  return cookie_;
}



// Return the external IP address
const QString& Application::getExternalIp() const
{
  // A wrapper for now, makes the Application API easier to use.
  return CurrentAccount::instance()->getExternalIP();
}



// Return the local IP address
const QString& Application::getLocalIp() const
{
  return localIp_;
}



// Return the current mode of the application
int Application::getMode() const
{
  return mode_;
}



// A command for the application was received (i.e. "Accept" or "Reject")
void Application::gotCommand(QString command)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - gotCommand(" << command << ")" << endl;
#endif

  waitingForUser_ = false;

  if ( command == "accept" )
  {
    if(userAccepted_)
    {
#ifdef KMESSDEBUG_APPLICATION
      kdDebug() << "Application - User already accepted before, ignoring." << endl;
#endif
      return;
    }
    userAccepted_ = true;
    contactStarted2_UserAccepts();
  }
  else if ( command == "cancel" )
  {
    userAborted();
  }
  else if ( command == "reject" )
  {
    userRejected();
  }
  else
  {
    kdDebug() << "Application received unhandled command " << command << "." << endl;
  }
}



// Return the "user started this app" state
bool Application::isUserStartedApp() const
{
  return userStartedApp_;
}



// Return true if we're waiting for the user to accept.
bool Application::isWaitingForUser() const
{
  return waitingForUser_;
}



// Let the user accept or reject the application
void Application::offerAcceptOrReject(const QString& appHtml)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application: Displaying accept/cancel message in ChatWindow" << endl;
#endif

  QString html;

  // Create the message, starting with the application's particulars
  html += "<font color=purple>";
  html += appHtml;
  html += "  ";
  html += i18n( "Do you want to <a href=%1>accept</a> or <a href=%2>cancel</a>?" )
          .arg( "\"kmess://accept?"  + getCookie() + "\"" )
          .arg( "\"kmess://reject?"  + getCookie() + "\"" );
  html += "</font>";

  // Send the message to the chat window.
  waitingForUser_ = true;
  userAccepted_   = false;
  emit appInitMessage( html );

#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application: Waiting for user to accept.." << endl;
#endif
}



// Let the user cancel the application
void Application::offerCancel(const QString& appHtml)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application: Displaying cancel link in ChatWindow" << endl;
#endif

  QString html;

  // Create the message, starting with the application's particulars
  html += appHtml + "  ";
  html += i18n("Click to <a href=%1>cancel</a>.")
          .arg( "\"kmess://cancel?"  + getCookie() + "\"" );

  // Send the message to the chat window.
  emit appInitMessage( html );
}



// Change the current mode
void Application::setMode(ApplicationMode mode)
{
  mode_ = mode;
}



// Start the application
void Application::start()
{
  userStartedApp_ = true;
  cookie_         = generateCookie();

  // Start the application
  userStarted1_UserInvitesContact();
}



// Start the application internally (from an invite message)
void Application::startByInvite(const QString &invitationCookie)
{
  userStartedApp_ = false;
  cookie_         = invitationCookie;
}



// You have cancelled the session
void Application::userAborted()
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - userAborted" << endl;
#endif

  sendCancelMessage( CANCEL_SESSION );
  endApplication( getCancelMessage() );
}



// You have rejected the invitation
void Application::userRejected()
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - userRejected" << endl;
#endif

  sendCancelMessage( CANCEL_INVITATION );
  endApplication( i18n("You have rejected the invitation.") );
}



// Step one of a user-started chat: the user invites the contact
void Application::userStarted1_UserInvitesContact()
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - userStarted1_UserInvitesContact" << endl;
#endif
}



// Step two of a user-started chat: the contact accepts
void Application::userStarted2_ContactAccepts(const MimeMessage& /*message*/)
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - userStarted2_ContactAccepts" << endl;
#endif
}



// Step three of a user-started chat: the user prepares for the session
void Application::userStarted3_UserPrepares()
{
#ifdef KMESSDEBUG_APPLICATION
  kdDebug() << "Application - userStarted3_UserPrepares" << endl;
#endif
}

#include "application.moc"
