//-----------------------------------------------------------------------------
/*
 * Description: This file contains the class Ax::Mail::MailBox
 *
 * See the file README in the main directory for a description of
 * this software, copyright information, and how to reach the author.
 *
 * Author: alex
 * Date:   15.11.2003
 *
 * Last modfied:
 *   $Author: alex $
 *   $Date: 2004/02/13 00:31:00 $
 *   $Revision: 1.13 $
 */
//-----------------------------------------------------------------------------

//=============================================================================
//     includes
//=============================================================================
//----- qt --------------------------------------------------------------------
//----- CORBA -----------------------------------------------------------------
//----- ICE -------------------------------------------------------------------
//----- C++ -------------------------------------------------------------------
#include <algorithm>

//----- C ---------------------------------------------------------------------
//----- AxLib -----------------------------------------------------------------
#include <Ax/Tools/Trace.h>
// #include <Ax/Tools/TraceOn.h>
#include <Ax/Tools/Globals.h>
#include <Ax/Mail/Mail.h>
#include <Ax/Mail/MailPart.h>
#include <Ax/Mail/MailBox.h>
#include <Ax/Mail/MailBoxMgr.h>

//----- vdr -------------------------------------------------------------------
//----- local plugin ----------------------------------------------------------
//----- locals ----------------------------------------------------------------

//=============================================================================
//     defines
//=============================================================================
#define DEBUG_COMM 1

//=============================================================================
//     using namespaces
//=============================================================================
using namespace std;
using namespace Ax::Mail;

//=============================================================================
//     class MailBox
//=============================================================================

//-----------------------------------------------------------------------------
//     MailBox::MailBox()
//-----------------------------------------------------------------------------
MailBox::MailBox(MailBoxSetup *theSetup)
         : mySetup       (theSetup)
         , myStream      (NIL  )
         , myHasStatus   (false)
         , myCountMails  (-1   )
         , myCountUnseen (-1   )
         , myCountRecent (-1   )
{
  wsdebug(("> MailBox::MailBox()"));

  wsdebug(("  MailBox::MailBox() registering mailbox at the manager..."));
  MailBoxMgr::instance()->registerMailBox(this);

  wsdebug(("< MailBox::MailBox()"));
} // MailBox::MailBox()

//-----------------------------------------------------------------------------
//     MailBox::~MailBox()
//-----------------------------------------------------------------------------
MailBox::~MailBox()
{
  wsdebug(("> MailBox::~MailBox()"));

  if (isOpen())
  {
    close();
  } // if

  wsdebug(("  MailBox::~MailBox() deregistering mailbox from the manager..."));
  MailBoxMgr::instance()->deregisterMailBox(this);

  wsdebug(("< MailBox::~MailBox()"));
} // MailBox::~MailBox()

//-------------------------------------------------------------------------
//     MailBox::stream()
//-------------------------------------------------------------------------
MAILSTREAM *MailBox::stream() const
{
  return myStream;
} // MailBox::stream()

//-------------------------------------------------------------------------
//     MailBox::requestStatus()
//-------------------------------------------------------------------------
bool MailBox::requestStatus()
{
  wsdebug(("> MailBox::requestStatus()"));
  wsinfo(("requesting status for '%s'...", getMailBox()));

  myHasStatus = false;

  MailBoxMgr::instance()->setActiveMailBox(this);

  wsdebug(("  MailBox::requestStatus() calling mail_status('%s')", getMailBox()));

  bool fOK = mail_status(myStream, getMailBox(), SA_MESSAGES | SA_UNSEEN | SA_RECENT) == T;
  if (!fOK)
  {
    wserror(("unable to request status for '%s': %s", getMailBox(), getLastLogMsg().c_str()));
  } // if

  MailBoxMgr::instance()->setActiveMailBox();

  wsinfo(("requesting status for '%s'...%s", getMailBox(), fOK ? "OK" : "FAILED"));
  wsdebug(("< MailBox::requestStatus() HasStatus: %d", fOK));
  return fOK;
} // MailBox::requestStatus()

//-------------------------------------------------------------------------
//     MailBox::hasStatus()
//-------------------------------------------------------------------------
bool MailBox::hasStatus() const
{
  return myHasStatus;
} // MailBox::hasStatus()

//-------------------------------------------------------------------------
//     MailBox::processMailStatus()
//-------------------------------------------------------------------------
void MailBox::processMailStatus(MAILSTATUS *theStatus)
{
  wsdebug(("> MailBox::processMailStatus() OLD: Count: %3ld  Unseen: %3ld  Recent: %3ld  myMails.size: %3d"
         , myCountMails
         , myCountUnseen
         , myCountRecent
         , myMails.size()
         ));

  myHasStatus   = true;
  myCountMails  = theStatus->messages;
  myCountUnseen = theStatus->unseen  ;
  myCountRecent = theStatus->recent  ;

  wsdebug(("> MailBox::processMailStatus() NEW: Count: %3ld  Unseen: %3ld  Recent: %3ld  myMails.size: %3d %s"
         , myCountMails
         , myCountUnseen
         , myCountRecent
         , myMails.size()
         , (int(myMails.size()) != myCountMails) ? "DIFFERENT" : ""
         ));

  if (int(myMails.size()) != myCountMails)
  {
    int anIndex = 0;;

    // delete supernumerous mails
    for (anIndex = myCountMails; anIndex < int(myMails.size()); ++anIndex)
    {
//      wsdebug(("  MailBox::processMailStatus() deleting supernumerous mail %d", anIndex));
      Mail *aMail = myMails[anIndex];
      myMails[anIndex] = 0;
      delete aMail;
    } // for

    anIndex = myMails.size();
    myMails.resize(myCountMails);

    // create new mails
    for (; anIndex < int(myMails.size()); ++anIndex)
    {
//      wsdebug(("  MailBox::processMailStatus() setting mail %d to 0", anIndex));
      myMails[anIndex] = 0; // new Mail(this);
    } // for
  } // if

  wsdebug(("< MailBox::processMailStatus() OLD: Count: %3ld  Unseen: %3ld  Recent: %3ld  myMails.size: %3d"
         , myCountMails
         , myCountUnseen
         , myCountRecent
         , myMails.size()
         ));
} // MailBox::processMailStatus()

//-------------------------------------------------------------------------
//     MailBox::processLogMsg()
//-------------------------------------------------------------------------
void MailBox::processLogMsg(const char *theMsg, long theErrorFlag)
{
  AX_UNUSED_ARG(theErrorFlag);
  myLastLogMsg = std::string(theMsg);
} // MailBox::processLogMsg()


//-------------------------------------------------------------------------
//     MailBox::open()
//-------------------------------------------------------------------------
bool MailBox::open()
{
  wsdebug(("> MailBox::open() '%s'", getAccountName()));
  bool fOK = false;

  if (isOpen())
  {
    wswarn(("  MailBox::open() '%s'  already open", getAccountName()));
    wsinfo(("mailbox '%s' is already open", getMailBox()));
  }
  else
  {
    wsinfo(("opening mailbox '%s'...", getMailBox()));

    MailBoxMgr::instance()->setActiveMailBox(this);
    myStream = mail_open(myStream, getMailBox(), DEBUG_COMM ? OP_DEBUG : NIL);
    MailBoxMgr::instance()->setActiveMailBox();
    fOK = myStream != NIL;

    if (!fOK)
    {
      wserror(("opening mailbox '%s'...FAILED: %s", getMailBox(), getLastLogMsg().c_str()));
    }
    else
    {
      wsdebug(("  MailBox::open() registering open mailbox at the manager..."));
      MailBoxMgr::instance()->registerOpenMailBox(this);

      wsinfo(("opening mailbox '%s'...OK", getMailBox()));
    } // if
  } // if

  wsdebug(("< MailBox::open() '%s' fOK: %d", getAccountName(), fOK));
  return fOK;
} // MailBox::open()

//-------------------------------------------------------------------------
//     MailBox::close()
//-------------------------------------------------------------------------
bool MailBox::close()
{
  wsdebug(("> MailBox::close() '%s'", getAccountName()));
  bool fOK = false;

  if (isOpen())
  {
    wsinfo(("closing mailbox '%s'...", getMailBox()));
    wsdebug(("  MailBox::open() deregistering open mailbox from the manager..."));
    MailBoxMgr::instance()->deregisterOpenMailBox(this);

    wsdebug(("  MailBox::close() '%s' closing...", getAccountName()));
    MailBoxMgr::instance()->setActiveMailBox(this);
    mail_close_full( myStream
                   , getSetup()->fExpungeOnClose ? CL_EXPUNGE : 0
                   );
    MailBoxMgr::instance()->setActiveMailBox(0);
    wsinfo(("closing mailbox '%s'...OK", getMailBox()));
    wsdebug(("  MailBox::close() '%s' closing...OK", getAccountName()));
    myStream = NIL;
  }
  else
  {
    wsinfo(("mailbox '%s' is not open", getMailBox()));
  } // if

  myCountMails  = -1;
  myCountRecent = -1;
  myCountUnseen = -1;

  for (MailVector::size_type i = 0; i < myMails.size(); ++i)
  {
    if (myMails[i] != 0)
    {
      wsdebug(("  MailBox::close() deleting mail %d", i));
      delete myMails[i];
      myMails[i] = 0;
    } // if
  } // for

  myMails.clear();

  wsdebug(("< MailBox::close() '%s' fOK: %d", getAccountName(), fOK));
  return fOK;
} // MailBox::close()

//-------------------------------------------------------------------------
//     MailBox::getMail()
//-------------------------------------------------------------------------
Mail *MailBox::getMail(long theMailNum)
{
  wsdebug(("> MailBox::getMail() MailNum: %ld", theMailNum));

  long aMailIdx = theMailNum - 1;

  if (aMailIdx < 0 || aMailIdx >= long(myMails.size()))
  {
    wserror(("mail number %ld not in [1..%d] for mailbox '%s'...", theMailNum, myMails.size(), getMailBox()));
    return 0;
  } // if

  Mail *aMail = myMails[aMailIdx];
  if (aMail == 0)
  {
//    wsinfo(("retrieving mail %ld from mailbox '%s'...", theMailNum, getMailBox()));
    MailBoxMgr::instance()->setActiveMailBox(this);

                               mail_fetchstructure(stream(), theMailNum, NIL);
    MESSAGECACHE *aMsgCache  = mail_elt           (stream(), theMailNum);

    MailBoxMgr::instance()->setActiveMailBox();

    if (aMsgCache == 0)
    {
      wserror(("retrieving mail %ld from mailbox '%s'...FAILED (ret: 0)", theMailNum, getMailBox()));
    }
    else if (long(aMsgCache->msgno) != theMailNum)
    {
      wserror(("retrieving mail %ld from mailbox '%s'...FAILED (%ld != %ld)"
             , theMailNum
             , getMailBox()
             , long(aMsgCache->msgno)
             , theMailNum
             ));
    }
    else
    {
      aMail = new Mail(this, aMsgCache);
      myMails[aMailIdx] = aMail;
//      wsinfo(("retrieving mail %ld from mailbox '%s'...OK", theMailNum, getMailBox()));
    } // if
  }
  else
  {
//      wsinfo(("mail %ld from mailbox '%s' already there", theMailNum, getMailBox()));
  } // if

  wsdebug(("< MailBox::getMail() Mail  : 0x%08X", int(aMail)));

  return aMail;
} // MailBox::getMail()

//-------------------------------------------------------------------------
//     MailBox::getSetup()
//-------------------------------------------------------------------------
const MailBoxSetup *MailBox::getSetup() const
{
  return mySetup;
} // MailBox::getSetup()

//-------------------------------------------------------------------------
//     MailBox::getAccountName()
//-------------------------------------------------------------------------
char *MailBox::getAccountName() const
{
  return mySetup->AccountName;
} // MailBox::getAccountName()

//-------------------------------------------------------------------------
//     MailBox::getLoginName()
//-------------------------------------------------------------------------
char *MailBox::getLoginName() const
{
  return mySetup->LoginName;
} // MailBox::getLoginName()

//-------------------------------------------------------------------------
//     MailBox::getLoginPass()
//-------------------------------------------------------------------------
char *MailBox::getLoginPass() const
{
  return mySetup->LoginPass;
} // MailBox::getLoginPass()

//-------------------------------------------------------------------------
//      MailBox::getMailBox()
//-------------------------------------------------------------------------
char *MailBox::getMailBox() const
{
  return mySetup->MailBox;
} // MailBox::getMailBox()

//-------------------------------------------------------------------------
//     MailBox::getCountMails()
//-------------------------------------------------------------------------
long MailBox::getCountMails() const
{
  return myCountMails;
} // MailBox::getCountMails()

//-------------------------------------------------------------------------
//     MailBox::getCountUnseen()
//-------------------------------------------------------------------------
long MailBox::getCountUnseen() const
{
  return myCountUnseen;
} // MailBox::getCountUnseen()

//-------------------------------------------------------------------------
//     MailBox::getCountRecent()
//-------------------------------------------------------------------------
long MailBox::getCountRecent() const
{
  return myCountRecent;
} // MailBox::getCountRecent()

//-------------------------------------------------------------------------
//     MailBox::getLastLogMsg()
//-------------------------------------------------------------------------
std::string MailBox::getLastLogMsg() const
{
  return myLastLogMsg;
} // MailBox::getLastLogMsg()

//-------------------------------------------------------------------------
//     MailBox::getNext()
//-------------------------------------------------------------------------
Mail *MailBox::getNext(Mail *theMail)
{
  Mail *aMail = 0;

  if (getCountMails() == 0)
  {
    // nothing to do: return 0;
  }
  else if (getCountMails() == 1)
  {
    if (theMail == 0)
    {
      aMail = *(myMails.begin());
    }
    else
    {
      // nothing to do: return 0;
    } // if
  }
  else
  {
    MailVector::iterator anIter = std::find(myMails.begin(), myMails.end(), theMail);
    if (anIter != myMails.end())
    {
      anIter++;
      if (anIter != myMails.end())
      {
        aMail = *anIter;
      }
      else
      {
        // nothing to do: return 0;
      } // if
    }
    else
    {
      aMail = *(myMails.begin());
    } // if
  } // if

  return aMail;
} // MailBox::getNext()

//-------------------------------------------------------------------------
//     MailBox::getPrev()
//-------------------------------------------------------------------------
Mail *MailBox::getPrev(Mail *theMail)
{
  Mail *aMail = 0;

  if (getCountMails() == 0)
  {
    // nothing to do: return 0;
  }
  else if (getCountMails() == 1)
  {
    if (theMail == 0)
    {
      aMail = *(myMails.begin());
    }
    else
    {
      // nothing to do: return 0;
    } // if
  }
  else
  {
    MailVector::reverse_iterator anIter = std::find(myMails.rbegin(), myMails.rend(), theMail);
    if (anIter != myMails.rend())
    {
      anIter++;
      if (anIter != myMails.rend())
      {
        aMail = *anIter;
      }
      else
      {
        // nothing to do: return 0;
      } // if
    }
    else
    {
      aMail = *(myMails.rbegin());
    } // if
  } // if

  return aMail;
} // MailBox::getPrev()

//-------------------------------------------------------------------------
//     MailBox::getNextNew()
//-------------------------------------------------------------------------
Mail *MailBox::getNextNew(Mail *theMail)
{
  Mail *aMail = 0;

  if (getCountUnseen() == 0)
  {
    // nothing to do: return 0;
  }
  else if (getCountMails() == 0)
  {
    // nothing to do: return 0;
  }
  else if (getCountUnseen() == 1 && theMail && !theMail->getIsSeen())
  {
    // nothing to do: return 0;
  }
  else
  {
    //----- get an iterator for the given mail -----
    MailVector::iterator anIter = theMail
                                  ? std::find(myMails.begin(), myMails.end(), theMail)
                                  : myMails.begin()
                                  ;

    //----- if we found the given mal -> iterate one pos -----
    if (anIter != myMails.end())
    {
      anIter++;
    } // if

    //----- find the next mail which is not seen  -----
    for (; anIter != myMails.end() && *anIter && (*anIter)->getIsSeen(); ++anIter)
    {
      ;
    } // for

    if (anIter != myMails.end())
    {
      aMail = *anIter;
    } // if
  } // if
  return aMail;
} // MailBox::getNextNew()

//-------------------------------------------------------------------------
//     MailBox::getPrevNew()
//-------------------------------------------------------------------------
Mail *MailBox::getPrevNew(Mail *theMail)
{
  Mail *aMail = 0;

  if (getCountUnseen() == 0)
  {
    // nothing to do: return 0;
  }
  else if (getCountMails() == 0)
  {
    // nothing to do: return 0;
  }
  else if (getCountUnseen() == 1 && theMail && !theMail->getIsSeen())
  {
    // nothing to do: return 0;
  }
  else
  {
    //----- get an iterator for the given mail -----
    MailVector::reverse_iterator anIter = theMail
                                          ? std::find(myMails.rbegin(), myMails.rend(), theMail)
                                          : myMails.rbegin()
                                          ;

    //----- if we found the given mal -> iterate one pos -----
    if (anIter != myMails.rend())
    {
      anIter++;
    } // if

    //----- find the next mail which is not seen  -----
    for (; anIter != myMails.rend() && *anIter && (*anIter)->getIsSeen(); ++anIter)
    {
      ;
    } // for

    if (anIter != myMails.rend())
    {
      aMail = *anIter;
    } // if
  } // if
  return aMail;
} // MailBox::getPrevNew()

//-------------------------------------------------------------------------
//     MailBox::getSurroundings()
//-------------------------------------------------------------------------
void MailBox::getSurroundings( Mail * theMail
                             , Mail **thePrev
                             , Mail **theNext
                             , Mail **thePrevNew
                             , Mail **theNextNew
                             )
{
  wsdebug(("> MailBox::getSurroundings() Mail: %4ld", theMail ? theMail->getMailNum() : -1));

  if (thePrev   ) *thePrev    = 0;
  if (theNext   ) *theNext    = 0;
  if (thePrevNew) *thePrevNew = 0;
  if (theNextNew) *theNextNew = 0;

  if (getCountMails() == 0)
  {
    return;
  } // if

  //----- get an iterator for the given mail -----
  MailVector::iterator aStartIter = theMail
                                  ? std::find(myMails.begin(), myMails.end(), theMail)
                                  : myMails.begin()
                                  ;

  //----- find the prev -----
  if (thePrev != 0)
  {
    if (aStartIter != myMails.begin()) *thePrev = *(aStartIter - 1);
  } // if

  //----- find the next -----
  if (theNext != 0)
  {
    MailVector::iterator aNextIter = aStartIter + 1;
    if (aNextIter != myMails.end()) *theNext = *aNextIter;
  } // if

  //----- find the prev new -----
  if (thePrevNew != 0 && getCountUnseen() != 0 && aStartIter != myMails.begin())
  {
    MailVector::iterator aPrevIter;

    for ( aPrevIter  = aStartIter - 1;
          aPrevIter != myMails.begin() && *aPrevIter && (*aPrevIter)->getIsSeen();
        --aPrevIter)
    {
    } // for

    // not aPrevIter points to a new mail or to the first mail
    if      (aPrevIter != myMails.begin()) *thePrevNew = *aPrevIter;
    else if (!(*aPrevIter)->getIsSeen())   *thePrevNew = *aPrevIter;
  } // if

  //----- find the next new -----
  if (theNextNew != 0 && getCountUnseen() != 0 && aStartIter != myMails.end())
  {
    MailVector::iterator aNextIter;

    for ( aNextIter  = aStartIter + 1;
          aNextIter != myMails.end() && *aNextIter && (*aNextIter)->getIsSeen();
        ++aNextIter)
    {
    } // for

    if (aNextIter != myMails.end()) *theNextNew = *aNextIter;
  } // if

  wsdebug(("< MailBox::getSurroundings() Mail: %4ld  Prev: %4ld  Next: %4ld  PrevNew: %4ld  NextNew: %4ld"
         , theMail                  ? theMail      ->getMailNum() : -1
         , thePrev    ? *thePrev    ? (*thePrev   )->getMailNum() : -2 : -1
         , theNext    ? *theNext    ? (*theNext   )->getMailNum() : -2 : -1
         , thePrevNew ? *thePrevNew ? (*thePrevNew)->getMailNum() : -2 : -1
         , theNextNew ? *theNextNew ? (*theNextNew)->getMailNum() : -2 : -1
         ));
} // MailBox::getSurroundings()

