//-----------------------------------------------------------------------------
/*
 * Description: This file contains the class AxMenuMailListView
 *
 * 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/05/31 12:52:20 $
 *   $Revision: 1.12 $
 */
//-----------------------------------------------------------------------------

//=============================================================================
//     includes
//=============================================================================

//----- multiple defs of swap() workaround ------------------------------------
#include "AxPluginGlobals.h"

//----- qt --------------------------------------------------------------------
//----- CORBA -----------------------------------------------------------------
//----- ICE -------------------------------------------------------------------
//----- C++ -------------------------------------------------------------------
//----- C ---------------------------------------------------------------------
//----- vdr -------------------------------------------------------------------
#include <vdr/tools.h>
#include <vdr/i18n.h>
#include <vdr/menuitems.h>

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

//----- local plugin ----------------------------------------------------------
#include "AxMenuMailListView.h"
#include "AxMenuMailView.h"
#include "AxVdrTools.h"
#include "AxPluginMailBox.h"
#include "AxPluginSetup.h"
#include "AxMailTools.h"

//----- locals ----------------------------------------------------------------

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

//=============================================================================
//     constants
//=============================================================================
#define ColWithFlags     5
#define ColWithSubject  28

//=============================================================================
//     class AxMailItem
//=============================================================================
class AxMailItem : public cOsdItem
{
  typedef cOsdItem PARENT;

  public:
    //-------------------------------------------------------------------------
    //     AxMailItem()
    //-------------------------------------------------------------------------
    AxMailItem(Mail::Mail *aMail, eOSState State = osUnknown);

    //-------------------------------------------------------------------------
    //     AxMailItem()
    //-------------------------------------------------------------------------
    AxMailItem(Mail::Mail *aMail, const char *Text, eOSState State = osUnknown);

    //-------------------------------------------------------------------------
    //     ~AxMailItem()
    //-------------------------------------------------------------------------
    virtual ~AxMailItem();

    //-------------------------------------------------------------------------
    //     Set()
    //-------------------------------------------------------------------------
    virtual void Set(void);

    //-------------------------------------------------------------------------
    //     getMail()
    //-------------------------------------------------------------------------
    Mail::Mail *getMail();

  private:
    //-------------------------------------------------------------------------
    //     createCacheValues()
    //-------------------------------------------------------------------------
    void createCacheValues();

    //-------------------------------------------------------------------------
    //     attributes
    //-------------------------------------------------------------------------
    Mail::Mail *myMail;

    // cache these values as they won't change
    std::string mySubject;
    std::string myFrom   ;
}; // class AxMailItem

//-----------------------------------------------------------------------------
//     AxMailItem::AxMailItem()
//-----------------------------------------------------------------------------
AxMailItem::AxMailItem(Mail::Mail *aMail, eOSState State)
          : PARENT(State)
          , myMail(aMail)
{
  createCacheValues();
} // AxMailItem::AxMailItem()

//-----------------------------------------------------------------------------
//     AxMailItem::AxMailItem()
//-----------------------------------------------------------------------------
AxMailItem::AxMailItem(Mail::Mail *aMail, const char *Text, eOSState State)
          : PARENT(Text, State)
          , myMail(aMail)
{
  createCacheValues();
} // AxMailItem::AxMailItem()

//-----------------------------------------------------------------------------
//     AxMailItem::~AxMailItem()
//-----------------------------------------------------------------------------
AxMailItem::~AxMailItem()
{
} // AxMailItem::~AxMailItem()

//-----------------------------------------------------------------------------
//     AxMailItem::createCacheValues()
//-----------------------------------------------------------------------------
void AxMailItem::createCacheValues()
{
  if (mySubject.length() == 0)
  {
    if (!AxMailTools::decodeEncodedWords(myMail->getSubject(), mySubject))
    {
      wswarn(("AxMailItem::AxMailItem() unable to decode subject line '%s'", mySubject.c_str()));
    } // if
  } // if

  if (myFrom.length() == 0)
  {
    if (!AxMailTools::decodeEncodedWords(myMail->getFrom(), myFrom))
    {
      wswarn(("AxMailItem::AxMailItem() unable to decode from line '%s'", myFrom.c_str()));
    } // if
  } // if
} // AxMailItem::createCacheValues()

//-----------------------------------------------------------------------------
//     AxMailItem::getMail()
//-----------------------------------------------------------------------------
Mail::Mail *AxMailItem::getMail()
{
  return myMail;
} // AxMailItem::getMail()

//-----------------------------------------------------------------------------
//     AxMailItem::Set()
//-----------------------------------------------------------------------------
void AxMailItem::Set(void)
{
  char *aLine = 0;

  // the %80s avoids that parts of the subject are displayed behind
  // the sender, if the subject is very long and the sender is
  // very short.
  asprintf( &aLine
          , "%s\t %s\t  %-80s"
          , myMail->getFlagsStr().c_str()
          , mySubject.c_str()
          , myFrom   .c_str()
          );

  wsdebug(("- AxMailItem::Set() '%s'", aLine));
  SetText(aLine, false);
} // AxMailItem::Set()

//=============================================================================
//     class AxMenuMailListView
//=============================================================================

//-----------------------------------------------------------------------------
//     AxMenuMailListView::AxMenuMailListView()
//-----------------------------------------------------------------------------
AxMenuMailListView::AxMenuMailListView(AxPluginMailBox *thePlugin, Mail::MailBox *theMailBox, bool fShowAll)
               : PARENT(       "Mails", ColWithFlags, ColWithSubject)
               , myPlugin      (thePlugin )
               , myMailBox     (theMailBox)
               , myShowAll     (fShowAll  )
               , myButtonPage  (PAGE_JUMP )
               , myMail        (0)
               , myPrevNew     (0)
               , myNextNew     (0)
{
  wsdebug(("> AxMenuMailListView::AxMenuMailListView()"));

  bool fOK = true;

  //----- update title bar -----
  updateTitle();

  //----- show "checking mailbox <mailboxname>..." -----
  Display();

  char *szMsg = 0;
  asprintf(&szMsg, tr("Getting mails..."));
  Skins.Message(mtStatus, szMsg);
  free(szMsg); szMsg = 0;

  //----- open the mailbox -----
  if (fOK)
  {
    fOK = myMailBox->open();
    if (!fOK)
    {
      Skins.Message(mtStatus, tr("Unable to open mail account!"));
    } // if
  } // if

  //----- request the current status -----
  if (fOK)
  {
    fOK = myMailBox->requestStatus();
    if (!fOK)
    {
      Skins.Message(mtStatus, tr("Unable to request status!"), 2);
      Skins.Message(mtStatus, szMsg, 2);
    } // if
  } // if

  //----- show the mails -----
  if (fOK)
  {
    showMails();
  } // if

  Display();

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

//-----------------------------------------------------------------------------
//     AxMenuMailListView::~AxMenuMailListView()
//-----------------------------------------------------------------------------
AxMenuMailListView::~AxMenuMailListView()
{
  wsdebug(("> AxMenuMailListView::~AxMenuMailListView()"));
  if (Mail::MailBoxMgr::instance()->hasMailBoxPtr(myMailBox))
  {
    myMailBox->close();
  }
  else
  {
    wsinfo(("AxMenuMailListView::~AxMenuMailListView() myMailBox vanished, assuming it was destroyed before this!"));
  } // if
  wsdebug(("< AxMenuMailListView::~AxMenuMailListView()"));
} // AxMenuMailListView::~AxMenuMailListView()

#define DEBUG_PROCESS_KEY 0

//-------------------------------------------------------------------------
//     AxMenuMailListView::ProcessKey()
//-------------------------------------------------------------------------
eOSState AxMenuMailListView::ProcessKey(eKeys theKey)
{
#if DEBUG_PROCESS_KEY
  if (theKey != kNone)
  {
    wsdebug(("> AxMenuMailListView::ProcessKey()     Key: %-10s  Cur: %3d  HasSub: %d  parent processing..."
          , cKey::ToString(theKey)
          , Current()
          , HasSubMenu()
          ));
  } // if
#endif

  bool fHasSubMenu = HasSubMenu();

  //----- if this has no subment, the key
  // k4 should behave like kUp
  // k6 should behave like kDown
  if (!fHasSubMenu)
  {
    if (theKey == k4) theKey = kUp;
    if (theKey == k6) theKey = kDown;
  } // if

  eOSState aState = PARENT::ProcessKey(theKey);

#if DEBUG_PROCESS_KEY
  if (theKey != kNone)
  {
    wsdebug(("  AxMenuMailListView::ProcessKey()     Key: %-10s  Cur: %3d  HasSub: %d  State: %-10s  "
          , cKey::ToString(theKey)
          , Current()
          , HasSubMenu()
          , AxVdrTools::getOSStateStr(aState)
          ));
  } // if
#endif

  if ( theKey == kBack
    && fHasSubMenu
    && !HasSubMenu()
     )
  {
    // submenu has disappeared, the current may have changed through the sub-menu
    updateTitle();
    setButtons(myButtonPage);
  } // if

#if 1
  if (aState != osUnknown && fHasSubMenu)
  {
#if DEBUG_PROCESS_KEY
    if (theKey != kNone)
    {
      wsdebug(("< AxMenuMailListView::ProcessKey()     Key: %-10s  Cur: %3d  HasSub: %d  State: %-10s  already handled"
            , cKey::ToString(theKey)
            , Current()
            , HasSubMenu()
            , AxVdrTools::getOSStateStr(aState)
            ));
    } // if
#endif
    return aState;
  } // if
#endif

  //----- select action... ----------------------------------------------------
  aState = osContinue;
  switch (theKey)
  {
    case kBack:
    {
      aState = osBack;
      break;
    }
    case kUp    :
    case kUp    | k_Repeat:
    case kDown  :
    case kDown  | k_Repeat:
    case kLeft  :
    case kLeft  | k_Repeat:
    case kRight :
    case kRight | k_Repeat:
    {
      updateTitle();
      setButtons(myButtonPage);
      break;
    }

    //----- show mail -----
    case kOk    :
    {
      myMail = getCurrentMail();
      if (myMail)
      {
        cOsdMenu *anOsdMenu = new AxMenuMailView(myPlugin, myMailBox, myMail, this);
        aState = AddSubMenu(anOsdMenu);
      } // if
      break;
    }

    //----- RED key ---------
    case kRed:
    {
      switch (myButtonPage)
      {
        case PAGE_JUMP:  // ----- unused ----------------
        {
          break;
        }
        case PAGE_MARK:  // ----- toggle flag: SEEN -----
        {
          myMail = getCurrentMail();
          if (myMail && theKey == kRed)
          {
            if (myMail->getIsSeen()) myMail->clearFlag(Mail::MF_SEEN);
            else                     myMail->setFlag  (Mail::MF_SEEN);

            // return to PAGE_JUMP and display new flags in mail
            if (Current() >= 0) Get(Current())->Set();
            DisplayCurrent(true);
            setButtons(STAY_ON_MARK_PAGE ? myButtonPage : PAGE_JUMP);
          } // if
        }
      } // switch
      break;
    }

    //----- GREEN key ---------
    case k7     :
    case k7     | k_Repeat:
    case kGreen :
    case kGreen | k_Repeat:
    {
      switch (myButtonPage)
      {
        case PAGE_JUMP: //----- jump to previous new -----
        {
          if (myPrevNew)
          {
            selectMail(myPrevNew);
          } // if
          break;
        }

        case PAGE_MARK:  // ----- toggle flag: FLAGGED -----
        {
          myMail = getCurrentMail();
          if (myMail && theKey == kGreen)
          {
            if (myMail->getIsFlagged()) myMail->clearFlag(Mail::MF_FLAGGED);
            else                        myMail->setFlag  (Mail::MF_FLAGGED);

            // return to PAGE_JUMP and display new flags in mail
            if (Current() >= 0) Get(Current())->Set();
            DisplayCurrent(true);
            setButtons(STAY_ON_MARK_PAGE ? myButtonPage : PAGE_JUMP);
          } // if
        }
      } // switch
      break;
    }

    //----- YELLOW key ---------
    case k9      :
    case k9      | k_Repeat:
    case kYellow :
    case kYellow | k_Repeat:
    {
      switch (myButtonPage)
      {
        case PAGE_JUMP: //----- jump to next new -----
        {
          if (myNextNew)
          {
            selectMail(myNextNew);
          } // if
          break;
        }

        case PAGE_MARK:  // ----- toggle flag: DELETED -----
        {
          myMail = getCurrentMail();
          if (myMail && theKey == kYellow)
          {
            if (myMail->getIsDeleted()) myMail->clearFlag(Mail::MF_DELETED);
            else                        myMail->setFlag  (Mail::MF_DELETED);

            // return to PAGE_JUMP and display new flags in mail
            if (Current() >= 0) Get(Current())->Set();
            DisplayCurrent(true);
            setButtons(STAY_ON_MARK_PAGE ? myButtonPage : PAGE_JUMP);
          } // if
        }
      } // switch
      break;
    }

    //----- BLUE key ---------
    case kBlue:
    {
      // switch pages
      switch (myButtonPage)
      {
        case PAGE_JUMP: setButtons(PAGE_MARK); break;
        case PAGE_MARK: setButtons(PAGE_JUMP); break;
      } // switch
      break;
    }

    default     :
    {
//      aState = osUnknown;
      break;
    }
  } // switch

#if DEBUG_PROCESS_KEY
  if (theKey != kNone)
  {
    wsdebug(("< AxMenuMailListView::ProcessKey()     Key: %-10s  Cur: %3d  HasSub: %d  State: %-10s  "
          , cKey::ToString(theKey)
          , Current()
          , HasSubMenu()
          , AxVdrTools::getOSStateStr(aState)
          ));
  } // if
#endif
  return aState;
} // AxMenuMailListView::ProcessKey()

//-------------------------------------------------------------------------
//     AxMenuMailListView::setButtons()
//-------------------------------------------------------------------------
void AxMenuMailListView::setButtons(AxPage theButtonPageNum)
{
#if DEBUG_PROCESS_KEY
  wsdebug(("> AxMenuMailListView::setButtons() ButtonPage: %d", myButtonPage));
#endif
  myButtonPage = theButtonPageNum;

  myMail    = getCurrentMail();
  myPrevNew = 0;
  myNextNew = 0;

  if (myMail)
  {
    myMailBox->getSurroundings(myMail, 0, 0, &myPrevNew, &myNextNew);
  } // if

  const char *aButton1Text = 0;
  const char *aButton2Text = 0;
  const char *aButton3Text = 0;
  const char *aButton4Text = 0;

  switch (myButtonPage)
  {
    //----- page jump ------------------------------------------------
    case PAGE_JUMP: // first page
    {
      if (myPrevNew) aButton2Text = tr("<< New");
      if (myNextNew) aButton3Text = tr("New >>");
      if (myMail)    aButton4Text = tr("Flags...");
      break;
    }

    //----- page mark ------------------------------------------------
    case PAGE_MARK:
    {
      if (myMail)
      {
        if (myMail->getIsSeen())    aButton1Text   = tr("New");
        else                        aButton1Text   = tr("Seen");

        if (myMail->getIsFlagged()) aButton2Text   = tr("Not Flagged");
        else                        aButton2Text   = tr("Flagged");

        if (myMail->getIsDeleted()) aButton3Text   = tr("Undelete");
        else                        aButton3Text   = tr("Delete"  );
      } // if
      aButton4Text   = tr("Back...");
    }
  } // switch

  SetHelp(aButton1Text, aButton2Text, aButton3Text, aButton4Text);

#if DEBUG_PROCESS_KEY
  wsdebug(("< AxMenuMailListView::setButtons() '%10s' '%10s' '%10s' '%10s'"
         , aButton1Text, aButton2Text, aButton3Text, aButton4Text
         ));
#endif
} // AxMenuMailListView::setButtons()

//-------------------------------------------------------------------------
//     AxMenuMailListView::updateTitle()
//-------------------------------------------------------------------------
void AxMenuMailListView::updateTitle()
{
  Mail::Mail *aMail = getCurrentMail();
  char *szMsg = 0;
  asprintf( &szMsg
          , "%s [%ld/%ld]: %s: %ld\t%s"
          , myMailBox->getAccountName()
          , aMail ? aMail->getMailNum() : 0l
          , myMailBox->getCountMails()
          , tr("New")
          , myMailBox->getCountUnseen()
#if VDRVERSNUM >= 10318
          , *DayDateTime(time(NULL))
#else
          , DayDateTime(time(NULL))
#endif
          );

  // set the title in the osd (which will not be updated immediately
  SetTitle(szMsg);

  // draw the title immediately
  Display();

  free(szMsg); szMsg = 0;
} // AxMenuMailListView::updateTitle()

//-------------------------------------------------------------------------
//     AxMenuMailListView::showMails()
//-------------------------------------------------------------------------
void AxMenuMailListView::showMails()
{
  if (myMailBox->getCountMails() == 0)
  {
    wsdebug(("  AxMenuMailListView::showMails() %ld Mails", myMailBox->getCountMails()));
    return;
  } // if
  wsdebug(("> AxMenuMailListView::showMails() %ld Mails", myMailBox->getCountMails()));

  bool fOK    = true;
  long aStart = 0;
  long aEnd   = 0;
  long aInc   = 0;

  if (myPlugin->getSetup()->PluginSettings.MLV_SortOrder == AxVdrTools::SO_ASC)
  {
    aStart = myPlugin->getSetup()->PluginSettings.MLV_MaxMails == 0
           ? 1
           : max(1, myMailBox->getCountMails() - myPlugin->getSetup()->PluginSettings.MLV_MaxMails);
    aEnd   = myMailBox->getCountMails();
    aInc   = 1;
  }
  else
  {
    aStart = myMailBox->getCountMails();
    aEnd   = myPlugin->getSetup()->PluginSettings.MLV_MaxMails == 0
           ? 1
           : max(1, myMailBox->getCountMails() - myPlugin->getSetup()->PluginSettings.MLV_MaxMails);
    aInc   = -1;
  } // if

  wsdebug(("  AxMenuMailListView::showMails() Start: %3ld  End: %3ld  Inc: %3ld", aStart, aEnd, aInc));

  Mail::Mail *aFirstMail = 0;

  const int aDelta = 1;
  time_t    myMsgTime = time(0) + aDelta;

  for (long i = aStart; i != aEnd + aInc && fOK; i += aInc)
  {
    Mail::Mail *aMail = myMailBox->getMail(i);
    if (aMail == 0)
    {
      wserror(("  AxMenuMailListView::showMails() i: %3ld  unable to get AxMail!", i));
    }
    else
    {
      if (time(0) >= myMsgTime)
      {
        myMsgTime = time(0) + aDelta;

        char *szMsg;
        asprintf( &szMsg
                , "%s %ld / %ld"
                , tr("Getting mails...")
                , (aStart < aEnd) ? i : (aStart - i + aEnd)
                , myMailBox->getCountMails()
                );
        Skins.Message(mtStatus, szMsg);
        free(szMsg);
        Display();
      } // if

      AxMailItem *anItem = new AxMailItem(aMail, "");
      anItem->Set();
      Add(anItem);

      if (aFirstMail == 0)
      {
        aFirstMail = aMail;
      } // if
    } // if
  } // for

//  Interface->Status(0);

  if (aFirstMail)
  {
    selectMail(aFirstMail);
  } // if

  wsdebug(("< AxMenuMailListView::showMails()"));
} // AxMenuMailListView::showMails()

//-------------------------------------------------------------------------
//     AxMenuMailListView::selectMail()
//-------------------------------------------------------------------------
void AxMenuMailListView::selectMail(Mail::Mail *theMail)
{
  if (theMail == 0)
  {
    return;
  } // if

  wsdebug(("> AxMenuMailListView::selectMail()"));
  setCurrentMail(theMail);

  wsdebug(("  AxMenuMailListView::selectMail() updateTitle"));
  updateTitle();

  myMail = theMail;

  wsdebug(("  AxMenuMailListView::selectMail() setButtons"));
  setButtons(myButtonPage);

  Display();

  wsdebug(("< AxMenuMailListView::selectMail()"));
} // AxMenuMailListView::selectMail()

//-------------------------------------------------------------------------
//     AxMenuMailListView::setCurrentMail()
//-------------------------------------------------------------------------
void AxMenuMailListView::setCurrentMail(Mail::Mail *theMail)
{
  if (theMail == 0)
  {
    return;
  } // if

  wsdebug(("> AxMenuMailListView::setCurrentMail()"));
  myMail = theMail;

  for (int i = 0; i < Count(); ++i)
  {
    AxMailItem *anItem = dynamic_cast<AxMailItem *>(Get(i));
    Mail::Mail *aMail  = anItem->getMail();

    if (aMail == theMail)
    {
      SetCurrent(anItem);
      break;
    } // if
  } // for
  wsdebug(("< AxMenuMailListView::setCurrentMail()"));
} // AxMenuMailListView::setCurrentMail()

//-------------------------------------------------------------------------
//     AxMenuMailListView::getCurrentMail()
//-------------------------------------------------------------------------
Ax::Mail::Mail *AxMenuMailListView::getCurrentMail()
{
  wsdebug(("> AxMenuMailListView::getCurrentMail() Current: %d", Current()));
  Ax::Mail::Mail *aMail =(Current() >= 0) ? dynamic_cast<AxMailItem *>(Get(Current()))->getMail() : 0;
  wsdebug(("< AxMenuMailListView::getCurrentMail() aMail  : %p", aMail));
  return aMail;
} // AxMenuMailListView::getCurrentMail()
