/*********************************************************************
 *
 * Module: WVAction.CPP
 *
 * Purpose: Support for action on articles and threads, including
 *          "killfile" and message selection.
 *
 *********************************************************************/

/*
 * $Id: wvaction.cpp 1.5 1996/08/31 07:06:56 dumoulin Exp $
 */

#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <stdlib.h>  // atoi()
#include "WVClass.h"

LATEST Latest;

static struct ValStrTable ActionTable[] =
{
  NO_ACTION,                    "NULL",
  MARK_SEEN_ART_LINES_LT,       "MSLT",
  MARK_SEEN_ART_LINES_EQ,       "MSEQ",
  MARK_SEEN_ART_LINES_GT,       "MSGT",
  MARK_SEEN_ART_GROUPS_LT,      "MSGL",
  MARK_SEEN_ART_GROUPS_EQ,      "MSGE",
  MARK_SEEN_ART_GROUPS_GT,      "MSGG",
  MARK_SEEN_ART_FROM_IS,        "MSFI",
  MARK_SEEN_ART_FROM_CONTAINS,  "MSFC",
  MARK_SEEN_ART_EMAIL_IS,       "MSEI",
  MARK_SEEN_ART_EMAIL_CONTAINS, "MSEC",
  MARK_SEEN_ART_SUBJ_IS,        "MSSI",
  MARK_SEEN_ART_SUBJ_CONTAINS,  "MSSC",
  MARK_SEEN_ART_MSGID_IS,       "MSMI",
  MARK_SEEN_ART_MSGID_CONTAINS, "MSMC",
  MARK_SEEN_ART_REF_CONTAINS,   "MSRC",
  MARK_SEEN_NGROUP_CONTAINS,    "MSNC",
  SELECT_ART_LINES_LT,          "SLLT",
  SELECT_ART_LINES_EQ,          "SLEQ",
  SELECT_ART_LINES_GT,          "SLGT",
  SELECT_ART_GROUPS_LT,         "SLGL",
  SELECT_ART_GROUPS_EQ,         "SLGE",
  SELECT_ART_GROUPS_GT,         "SLGG",
  SELECT_ART_FROM_IS,           "SLFI",
  SELECT_ART_FROM_CONTAINS,     "SLFC",
  SELECT_ART_EMAIL_IS,          "SLEI",
  SELECT_ART_EMAIL_CONTAINS,    "SLEC",
  SELECT_ART_SUBJ_IS,           "SLSI",
  SELECT_ART_SUBJ_CONTAINS,     "SLSC",
  SELECT_ART_MSGID_IS,          "SLMI",
  SELECT_ART_MSGID_CONTAINS,    "SLMC",
  SELECT_ART_REF_CONTAINS,      "SLRC",
  SELECT_NGROUP_CONTAINS,       "SLNC"
  };

#define ACTIONTABLESIZE (sizeof(ActionTable) / sizeof(ActionTable[0]))

#define WM_DATA_TO_DLG          WM_USER + 250
#define WM_DLG_TO_DATA          WM_USER + 251
#define WM_SETFILTER_OPTIONS    WM_USER + 260
#define WM_PROCESS_TEXT         WM_USER + 261

#define STR_HEADER_LINES   "Number of Lines"
#define STR_HEADER_GROUPS  "Number of Groups (*)"
#define STR_HEADER_FROM    "From"
#define STR_HEADER_EMAIL   "email"
#define STR_HEADER_SUBJ    "Subject"
#define STR_HEADER_MSGID   "Message Id"
#define STR_HEADER_REF     "References"
#define STR_HEADER_NGROUP  "Newsgroup (*)"

#define STR_HDFILT_LT    "< (non-zero)"
#define STR_HDFILT_EQ    "="
#define STR_HDFILT_GT    ">"
#define STR_HDFILT_EQCS  "Equals" // "Is exactly (incl case)"
//#define STR_HDFILT_EQNS  "Is exactly (ignore case)"
#define STR_HDFILT_CONT  "Contains" // "Contains (incl case)"

//#define STR_ACTION_NOOP   "(no action)"
#define STR_ACTION_MARK   "Mark article as 'seen'"
#define STR_ACTION_SELECT "Select article"


WMemPool WVArticleAction::m_ClassPool(sizeof(WVArticleAction), 4096/sizeof(WVArticleAction)); // 335.3
WVArticleAction *g_pAction = NULL;  // global variable for 'all groups'


// Perform the article action requested
void DoAction(header_p header, LPACTIONDATA ad)
{
  switch(ad->iAction)
  {
  case MARK_SEEN_ART_LINES_LT:
    if(header->lines && (header->lines < (unsigned int) ad->iParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_LINES_EQ:
    if(header->lines == (unsigned int) ad->iParam)
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_LINES_GT:
    if(header->lines > (unsigned int) ad->iParam)
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_GROUPS_LT:
    if(header->pHD->iGroups < (unsigned int) ad->iParam)
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_GROUPS_EQ:
    if(header->pHD->iGroups == (unsigned int) ad->iParam)
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_GROUPS_GT:
    if(header->pHD->iGroups > (unsigned int) ad->iParam)
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_FROM_IS:
    if(!_stricmp(header->pHD->sFrom, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_FROM_CONTAINS:
    if(strstr(header->pHD->sFrom, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_EMAIL_IS:
    if(!_stricmp(header->pHD->sEmail, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_EMAIL_CONTAINS:
    if(strstr(header->pHD->sEmail, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_SUBJ_IS:
    if(!_stricmp(header->pHD->sSubject, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_SUBJ_CONTAINS:
    if(strstr(header->pHD->sSubject, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_MSGID_IS:
    if(!strcmp(header->pHD->sMessage_id, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_MSGID_CONTAINS:
    if(strstr(header->pHD->sMessage_id, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_ART_REF_CONTAINS:
    if(strstr(header->pHD->sFrob_ref_list, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case MARK_SEEN_NGROUP_CONTAINS:
    if(strstr(header->pHD->sNewsgroups, ad->sParam))
      header->SeenSelected = ART_KILLED;
    break;
  case SELECT_ART_LINES_LT:
    if(header->lines && (header->lines < (unsigned int) ad->iParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_LINES_EQ:
    if(header->lines == (unsigned int) ad->iParam)
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_LINES_GT:
    if(header->lines > (unsigned int) ad->iParam)
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_GROUPS_LT:
    if(header->pHD->iGroups < (unsigned int) ad->iParam)
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_GROUPS_EQ:
    if(header->pHD->iGroups == (unsigned int) ad->iParam)
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_GROUPS_GT:
    if(header->pHD->iGroups > (unsigned int) ad->iParam)
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_FROM_IS:
    if(!_stricmp(header->pHD->sFrom, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_FROM_CONTAINS:
    if(strstr(header->pHD->sFrom, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_EMAIL_IS:
    if(!_stricmp(header->pHD->sEmail, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_EMAIL_CONTAINS:
    if(strstr(header->pHD->sEmail, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_SUBJ_IS:
    if(!_stricmp(header->pHD->sSubject, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_SUBJ_CONTAINS:
    if(strstr(header->pHD->sSubject, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_MSGID_IS:
    if(!strcmp(header->pHD->sMessage_id, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_MSGID_CONTAINS:
    if(strstr(header->pHD->sMessage_id, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_ART_REF_CONTAINS:
    if(strstr(header->pHD->sFrob_ref_list, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case SELECT_NGROUP_CONTAINS:
    if(strstr(header->pHD->sNewsgroups, ad->sParam))
      header->SeenSelected = ART_SELECTED;
    break;
  case NO_ACTION:
    break;
  default:
    TRACE1("Unknown action handler: %u\n", ad->iAction);
    DEBUG_BREAK;
    break;
  }
}


// Determine if we need to fetch the Newsgroups line of the header.  We
// only need to do this if the user has selected a Killfile options
// that depends on having data from the newsgroups header line.  Since
// this isn't typically in the XOVER database on the server, it is an 
// expensive operation to perform. (JD 8/1/96)

BOOL WVArticleAction::NeedNgroupHeader()
{
  if(!m_bNeedGroup)
    m_bNeedGroup = new WVPersBOOL(m_sProfSect, "NeedGroupHeader", FALSE);
  return (BOOL) *m_bNeedGroup;
}


WVArticleAction::WVArticleAction(const char*sGrp)
: m_Head(NULL), m_Tail(NULL), m_Current(NULL), m_bNeedGroup(NULL)
{
  char sProfSect[128];
  if(sGrp)
    sprintf(sProfSect, "G: %s", sGrp);
  else
    sprintf(sProfSect, "N: %s", NNTPHost);
  m_sProfSect = sProfSect;
}



WVArticleAction::WVArticleAction(WVArticleAction*wv)
: m_Head(NULL), m_Tail(NULL), m_Current(NULL), m_bNeedGroup(NULL),
  m_sProfSect(wv->m_sProfSect)
{
  //if(wv.m_sProfSect[0])
  //  m_sProfSect = wv.m_sProfSect;
  //else
  //  m_sProfSect = "";
  if(wv->m_Head == NULL)
    return;
  Node*n = wv->m_Head;
  do
  {
    Append(n->ad.iAction, n->ad.iParam, n->ad.sParam);
    n = n->Flink;
  } while(n != NULL);
}


WVArticleAction::WVArticleAction(const WVArticleAction&wv)
: m_Head(NULL), m_Tail(NULL), m_Current(NULL), m_bNeedGroup(NULL),
  m_sProfSect(wv.m_sProfSect)
{
  //if(wv.m_sProfSect[0])
  //  m_sProfSect = wv.m_sProfSect;
  //else
  //  m_sProfSect = "";
  if(wv.m_Head == NULL)
    return;
  Node*n = wv.m_Head;
  do
  {
    Append(n->ad.iAction, n->ad.iParam, n->ad.sParam);
    n = n->Flink;
  } while(n != NULL);
}


WVArticleAction::~WVArticleAction()
{
  uint32 iMod;
  Node* cur;
  if(!m_bNeedGroup)
    m_bNeedGroup = new WVPersBOOL(m_sProfSect, "NeedGroupHeader", FALSE);
  
  ResetCurrent();
  
  *m_bNeedGroup = FALSE;
  while((cur = GetCurrent()) && !*m_bNeedGroup)
  {
    if(cur->ad.sParam)
    {
      for(int i = 0; i < ACTIONTABLESIZE; i++)
      {
        if(cur->ad.iAction == ActionTable[i].value)
        {
          iMod = cur->ad.iAction & 0xFFFFFFF0;
          if(iMod == MARK_SEEN_ART_GROUPS_BASE ||
            iMod == SELECT_ART_GROUPS_BASE ||
            iMod == MARK_SEEN_NGROUP_BASE ||
            iMod == SELECT_NGROUP_BASE)
            *m_bNeedGroup = TRUE;
          break;
        }
      }
    }
    GoToNext();
  }
  delete m_bNeedGroup;
  DeleteAll();
}

// Append a new entry to the end of the list
void WVArticleAction::Append(int iA, long int iP, const char*sP)
{
  Node * n = new Node;
  if(n)
  {
    if(m_Tail == NULL)
    {
      m_Head = n;
      n->Blink = NULL;
    }
    else
    {
      m_Tail->Flink = n;
      n->Blink = m_Tail;
    }
    m_Tail = n;
    m_Current = n;
    n->Flink = NULL;
    n->ad.iAction = iA;
    n->ad.iParam  = iP;
    n->ad.sParam = NULL;
    SetParamText(sP);
  }
}


void WVArticleAction::DeleteNode(Node* pN)
{
  if(pN == NULL)
    return;
  Node*pF = pN->Flink;
  Node*pB = pN->Blink;

  if(pN->Blink && pN->Flink) // any node in middle of list
  {
    pF->Blink = pB;
    pB->Flink = pF;
  }
  else
  {
    if(pN->Blink == NULL) // first node in list
    {
      m_Head = pN->Flink;
      if(pN->Flink != NULL)
      {
        pF->Blink = NULL;
      }
    }
    if(pN->Flink == NULL) // last node in list
    {
      m_Tail = pN->Blink;
      if(pN->Blink != NULL)
      {
        pB->Flink = NULL;
      }
    }
  }
  delete pN;
  m_Current = pB ? pB : pF;
}


// Set the iParam value
void WVArticleAction::SetIParam(int i)
{
  if(m_Current)
  {
    m_Current->ad.iParam = i;
  }
#ifdef _DEBUG
  else
  {
    TRACE0("SetIParam() error: NULL pointer\n");
    DEBUG_BREAK;
  }
#endif
}



// Set the sParam text, adjusting string size if necessary
void WVArticleAction::SetParamText(const char*sP)
{
  if(m_Current)
  {
    if((NULL == sP) || !*sP)
    {
      if(NULL != m_Current->ad.sParam)
      {
        delete m_Current->ad.sParam;
        m_Current->ad.sParam = NULL;
      }
    }
    else
    {
      if(NULL != m_Current->ad.sParam)
      {
        if(strlen(m_Current->ad.sParam) < strlen(sP))
        {
          delete m_Current->ad.sParam;
          m_Current->ad.sParam = NULL;
        }
      }
      if(NULL == m_Current->ad.sParam)
      {
        m_Current->ad.sParam = new char[strlen(sP) + 1];
      }
      strcpy(m_Current->ad.sParam, sP);
    }
  }
#ifdef _DEBUG
  else
  {
    TRACE0("SetParamText() error: NULL pointer\n");
    DEBUG_BREAK;
  }
#endif
}



// Delete all entries on the list
void WVArticleAction::DeleteAll()
{
  m_Current = m_Head;
  while(m_Current != NULL)
  {
    m_Head = m_Current->Flink;
    delete m_Current->ad.sParam;
    m_Current->ad.sParam = NULL;
    m_Current->Flink = NULL;
    m_Current->Blink = NULL;
    delete m_Current;
    m_Current = m_Head;
  }
  m_Head = NULL;
  m_Tail = NULL;
  m_Current = NULL;
}


// Go to the next node in the linked list
void WVArticleAction::GoToNext()
{
  if(m_Current != NULL)
  {
    m_Current = m_Current->Flink;
  }
}


// Go to the previous node in the linked list
void WVArticleAction::GoToPrev()
{
  if(m_Current != NULL)
  {
    m_Current = m_Current->Blink;
  }
}


// Switch two nodes in the linked list
void WVArticleAction::Switch(Node*nA, Node*nB)
{
  long int iTmp;
  char*sTmp;

  if(!nA)
    return;
  if(!nB)
    return;
  iTmp = nA->ad.iAction;
  nA->ad.iAction = nB->ad.iAction;
  nB->ad.iAction = (int) iTmp;
  iTmp = nA->ad.iParam;
  nA->ad.iParam = nB->ad.iParam;
  nB->ad.iParam = iTmp;
  sTmp = nA->ad.sParam;
  nA->ad.sParam = nB->ad.sParam;
  nB->ad.sParam = sTmp;
}


// Switch two complete lists
void WVArticleAction::Switchwith(WVArticleAction*pA)
{
  Node*pWTmp;

  if(!pA)
    return;
  pWTmp = this->m_Head;
  this->m_Head = pA->m_Head;
  pA->m_Head = pWTmp;
  pWTmp = this->m_Tail;
  this->m_Tail = pA->m_Tail;
  pA->m_Tail = pWTmp;
  pWTmp = this->m_Current;
  this->m_Current = pA->m_Current;
  pA->m_Current = pWTmp;
}


// Read the list of actions from the .ini file
void WVArticleAction::ReadActions(/* const char*sGroup */)
{
  long int iTmp;
  char ProfEntry[32];
  char ProfValue[MAXINTERNALLINE];
  char *pParam;
  
  //if(sGroup)  // moved to ctor
  //  sprintf(m_ProfSect, "G: %s", sGroup);
  //else
  //  sprintf(m_ProfSect, "N: %s", NNTPHost);
  
  for(int ind = 1; ind < 1000; ind++)
  {
    sprintf(ProfEntry, "Action%02d", ind);
    
    if(!GetPrivateProfileString(m_sProfSect, ProfEntry, "", 
      ProfValue, sizeof(ProfValue), szAppProFile))
      return;
    
    pParam = strchr(ProfValue, ' ');
    if(pParam)
    {
      *pParam++ = '\0';
      for(int i = 1; i < ACTIONTABLESIZE; i++) // skip 'no action' item
      {
        if(!strcmp(ProfValue, ActionTable[i].text))
        {
          iTmp = atoi(pParam);
          Append(ActionTable[i].value, iTmp, pParam);
          break;
        }
      }
    }
  }
}


// Save the action list back to the .ini file
void WVArticleAction::WriteActions()
{
  int ind = 0;
  Node* cur;
  char ProfEntry[32];
  char ProfValue[MAXINTERNALLINE];
  
  ResetCurrent();

  while(cur = GetCurrent())
  {
    if(cur->ad.sParam)
    {
      for(int i = 0; i < ACTIONTABLESIZE; i++)
      {
        if(cur->ad.iAction == ActionTable[i].value)
        {
          sprintf(ProfEntry, "Action%02d", ++ind);
          sprintf(ProfValue, "%s %s", ActionTable[i].text, cur->ad.sParam);
#ifdef _DEBUG
          if(strlen(ProfValue) >= sizeof(ProfValue)) // bozo programmer filter
            DEBUG_BREAK;
#endif
          ProfValue[sizeof(ProfValue)-1] = 0;
          WritePrivateProfileString(m_sProfSect, ProfEntry,
            ProfValue, szAppProFile);
          break;
        }
      }
    }
    GoToNext();
  }
  sprintf(ProfEntry, "Action%02d", ++ind);
  // TODO: if WIN32, use the Registry...
  WritePrivateProfileString(m_sProfSect, ProfEntry, NULL, szAppProFile);
}


// Do the requested action for each article
void WVArticleAction::ActOnArticles(TypGroup far *Group, header_p headers)
{
  int artindex;
  header_p header;
  
  ResetCurrent();
  if(NULL != GetCurrent())
  {
    for(artindex = 0; artindex < Group->total_headers; artindex++)
    {
      header = header_elt(headers, artindex);
      if((ART_UNSEEN_UNSELECTED == header->SeenSelected))
      {
        ResetCurrent();
        while((NULL != GetCurrent()) &&
          (ART_UNSEEN_UNSELECTED == header->SeenSelected))
        {
          DoAction(header, &GetCurrent()->ad);
          GoToNext();
        }
      }
    }
  }
}


BOOL AddEQCSChoice(const char*s)
{
  if(!strcmp(s, STR_HEADER_REF))
    return FALSE;
  if(!strcmp(s, STR_HEADER_NGROUP))
    return FALSE;
  return TRUE;
}


LRESULT FAR PASCAL
WinVnArticleActionDlg(HWND hDlg, unsigned iMessage, WPARAM wParam, LPARAM lParam)
{
  int iSelect = 0;
  int cOffset;
  int cBase;
  WORD wID;
  WORD wCode;
  static int iSaveGroupRadio = 0;
  static WVArticleAction *pSaveGrp = NULL;
  static WVArticleAction *pGrp = NULL;
  static WVArticleAction *pGlob = NULL;
  static WVArticleAction *pAA = NULL;
  static Node *pAD = NULL;
  char sTmp[512];

  wID = LOWORD(wParam);
#ifdef WIN32
  HWND hCtl = (HWND)(UINT) lParam;
  wCode = HIWORD(wParam);
#else
  int hCtl = LOWORD(lParam);
  wCode = HIWORD(lParam);
#endif
  switch(iMessage)
  {
  case WM_INITDIALOG:
    //pGrp  = new WVArticleAction(CurrentGroup);
    //pGrp->ReadActions();
    pSaveGrp = (WVArticleAction*)lParam;
    pGrp = new WVArticleAction(pSaveGrp);
    pGlob = new WVArticleAction(*g_pAction);
    pAA = pGrp;
    pAA->ResetCurrent();
    pAD = pAA->GetCurrent();
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_FROM);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_EMAIL);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_LINES);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_GROUPS);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_SUBJ);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_MSGID);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_REF);
    SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_HEADER_NGROUP);

//    SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_ADDSTRING, 0,
//      (LPARAM) (LPSTR) STR_ACTION_NOOP);
    SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_ACTION_MARK);
    SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_ADDSTRING, 0,
      (LPARAM) (LPSTR) STR_ACTION_SELECT);

    iSaveGroupRadio = IDC_AA_RADIO_GROUP;
    SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
    return TRUE;

  case WM_DESTROY:
    delete pGrp;
    pGrp = NULL;
    delete pGlob;
    pGlob = NULL;
    break;

  case WM_DLG_TO_DATA: // Update data from dialog
    pAD = pAA->GetCurrent();
    if(pAD != NULL)
    {
      pAD->ad.iAction = NO_ACTION;
      GetDlgItemText(hDlg, IDC_AA_TEXT, sTmp, sizeof(sTmp));
      pAA->SetParamText(sTmp);
      pAD->ad.iParam = atoi(sTmp);
//      SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_GETLBTEXT,
//          (WPARAM) iSelect, (LPARAM) (LPSTR) sTmp);
      iSelect = (int) SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_GETCURSEL, 0, 0);
      if(iSelect == CB_ERR)
      {
        DEBUG_BREAK;
      }
      else
      {
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_GETLBTEXT,
            (WPARAM) iSelect, (LPARAM) (LPSTR) sTmp);
        if(!strcmp(sTmp, STR_HEADER_LINES))
          pAD->ad.iAction = MARK_SEEN_ART_LINES_BASE;
        else if(!strcmp(sTmp, STR_HEADER_GROUPS))
          pAD->ad.iAction = MARK_SEEN_ART_GROUPS_BASE;
        else if(!strcmp(sTmp, STR_HEADER_FROM))
          pAD->ad.iAction = MARK_SEEN_ART_FROM_BASE;
        else if(!strcmp(sTmp, STR_HEADER_EMAIL))
          pAD->ad.iAction = MARK_SEEN_ART_EMAIL_BASE;
        else if(!strcmp(sTmp, STR_HEADER_SUBJ))
          pAD->ad.iAction = MARK_SEEN_ART_SUBJ_BASE;
        else if(!strcmp(sTmp, STR_HEADER_MSGID))
          pAD->ad.iAction = MARK_SEEN_ART_MSGID_BASE;
        else if(!strcmp(sTmp, STR_HEADER_REF))
          pAD->ad.iAction = MARK_SEEN_ART_REF_BASE;
        else if(!strcmp(sTmp, STR_HEADER_NGROUP))
          pAD->ad.iAction = MARK_SEEN_NGROUP_BASE;
        else
        {
          TRACE0("No header item selected\n");
          DEBUG_BREAK;
          pAD->ad.iAction = NO_ACTION;
        }

        iSelect = (int) SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_GETCURSEL, 0, 0);
        if(iSelect == CB_ERR)
        {
          DEBUG_BREAK;
        }
        else
        {
          SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_GETLBTEXT,
            (WPARAM) iSelect, (LPARAM) (LPSTR) sTmp);
          if(!strcmp(sTmp, STR_ACTION_SELECT))
          {
            pAD->ad.iAction |= AA_SELECT_MASK;
          }

          iSelect = (int) SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_GETCURSEL, 0, 0);
          if(iSelect == CB_ERR)
          {
            DEBUG_BREAK;
          }
          else
          {
            SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_GETLBTEXT,
                (WPARAM) iSelect, (LPARAM) (LPSTR) sTmp);
            if(!strcmp(sTmp, STR_HDFILT_LT))
            {
              pAD->ad.iAction |= AA_LT_MASK;
              sscanf(pAD->ad.sParam, "%u", &pAD->ad.iParam);
            }
            else if(!strcmp(sTmp, STR_HDFILT_EQ))
            {
              pAD->ad.iAction |= AA_EQ_MASK;
              sscanf(pAD->ad.sParam, "%u", &pAD->ad.iParam);
            }
            else if(!strcmp(sTmp, STR_HDFILT_GT))
            {
              pAD->ad.iAction |= AA_GT_MASK;
              sscanf(pAD->ad.sParam, "%u", &pAD->ad.iParam);
            }
            else if(!strcmp(sTmp, STR_HDFILT_EQCS))
            {
              pAD->ad.iAction |= AA_IS_MASK;
            }
            else if(!strcmp(sTmp, STR_HDFILT_CONT))
            {
              pAD->ad.iAction |= AA_CONTAINS_MASK;
            }
          }
        }
      }
//    TRACE3("WM_DLG_TO_DATA iAction = %lu, iParam = %lu, sParam = <%s>\n",
//      pAD->ad.iAction, pAD->ad.iParam, pAD->ad.sParam ? pAD->ad.sParam : "");
    }
    return 0; // WM_DLG_TO_DATA

  case WM_DATA_TO_DLG:  // Update dialog from current data
    pAD = pAA->GetCurrent();
    SetDlgItemText(hDlg, IDC_AA_RADIO_GROUP, CurrentGroup);
    CheckRadioButton (hDlg, IDC_AA_RADIO_GLOBAL, IDC_AA_RADIO_GROUP,
      iSaveGroupRadio);
    SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_RESETCONTENT, 0, 0);
    EnableWindow(GetDlgItem(hDlg, IDC_AA_EDIT_PREV), FALSE);
    EnableWindow(GetDlgItem(hDlg, IDC_AA_EDIT_NEXT), FALSE);
    EnableWindow(GetDlgItem(hDlg, IDC_AA_MOVE_PREV), FALSE);
    EnableWindow(GetDlgItem(hDlg, IDC_AA_MOVE_NEXT), FALSE);

    if(pAD == NULL)
    {
      SetDlgItemText(hDlg, IDC_AA_TEXT, "");
      EnableWindow(GetDlgItem(hDlg, IDC_AA_HEADER),    FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_HDRFILTER), FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_TEXT),      FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_ACTION),    FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_DELETE),    FALSE);
    }
    else
    {
//      TRACE3("WM_DATA_TO_DLG iAction = %lu, iParam = %lu, sParam = <%s>\n",
//        pAD->ad.iAction, pAD->ad.iParam, pAD->ad.sParam ? pAD->ad.sParam : "");
      cOffset = pAD->ad.iAction % AA_MODULO;
      cBase = (pAD->ad.iAction / AA_MODULO) * AA_MODULO;
      if(cBase >= AA_SELECT_MASK)
        cBase &= ~AA_SELECT_MASK;

      EnableWindow(GetDlgItem(hDlg, IDC_AA_HEADER),    TRUE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_HDRFILTER), TRUE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_TEXT),      TRUE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_ACTION),    TRUE);

      if(pAD->ad.iAction & AA_SELECT_MASK)
      {
        SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_ACTION_SELECT);
      }
      else
      {
        SendDlgItemMessage(hDlg, IDC_AA_ACTION, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_ACTION_MARK);
      }
      switch(cBase)
      {
      case MARK_SEEN_ART_LINES_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_LINES);
        sprintf(sTmp, "%lu line(s)", pAD->ad.iParam);
        pAA->SetParamText(sTmp);
        break;
      case MARK_SEEN_ART_GROUPS_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_GROUPS);
        sprintf(sTmp, "%lu group(s)", pAD->ad.iParam);
        pAA->SetParamText(sTmp);
        break;
      case MARK_SEEN_ART_FROM_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_FROM);
        break;
      case MARK_SEEN_ART_EMAIL_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_EMAIL);
        break;
      case MARK_SEEN_ART_SUBJ_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_SUBJ);
        break;
      case MARK_SEEN_ART_MSGID_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_MSGID);
        break;
      case MARK_SEEN_ART_REF_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_REF);
        break;
      case MARK_SEEN_NGROUP_BASE:
        SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HEADER_NGROUP);
        break;
      default:
        TRACE1("**** Unknown cBase value: %lu\n", cBase);
        DEBUG_BREAK;
        break;
      }
      SendMessage(hDlg, WM_SETFILTER_OPTIONS, 0, 0);
      SetDlgItemText(hDlg, IDC_AA_TEXT, pAD->ad.sParam);
      switch(cOffset)
      {
      case AA_LT_MASK:
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HDFILT_LT);
        break;
      case AA_EQ_MASK:
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HDFILT_EQ);
        break;
      case AA_GT_MASK:
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HDFILT_GT);
        break;
      case AA_IS_MASK:
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HDFILT_EQCS);
        break;
      case AA_CONTAINS_MASK:
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SELECTSTRING,
                  (WPARAM) -1, (LPARAM)(LPSTR) STR_HDFILT_CONT);
        break;

      default:
        TRACE1("**** Unknown cOffset value: %lu\n", cOffset);
        DEBUG_BREAK;
        break;
      }
      EnableWindow(GetDlgItem(hDlg, IDC_AA_EDIT_PREV),
                   pAA->GetCurrent()->Blink ? TRUE : FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_EDIT_NEXT),
                   pAA->GetCurrent()->Flink ? TRUE : FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_MOVE_PREV),
                   pAA->GetCurrent()->Blink ? TRUE : FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_MOVE_NEXT),
                   pAA->GetCurrent()->Flink ? TRUE : FALSE);
      EnableWindow(GetDlgItem(hDlg, IDC_AA_DELETE), TRUE);
    }
    return 0;  // WM_DLG_TO_DATA

  case WM_SETFILTER_OPTIONS: // Set filter listbox options based on header selection
    SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_RESETCONTENT, 0, 0);
    iSelect = (int) SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_GETCURSEL, 0, 0);
    if(iSelect != CB_ERR)
    {
      SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_GETLBTEXT,
        (WPARAM) iSelect, (LPARAM) (LPSTR) sTmp);
      
      if((!strcmp(sTmp, STR_HEADER_LINES)) || !strcmp(sTmp, STR_HEADER_GROUPS))
      {
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER,
          CB_ADDSTRING, 0, (LPARAM)(LPSTR) STR_HDFILT_LT);
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER,
          CB_ADDSTRING, 0, (LPARAM)(LPSTR) STR_HDFILT_EQ);
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER,
          CB_ADDSTRING, 0, (LPARAM)(LPSTR) STR_HDFILT_GT);
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SETCURSEL, 1, 0);
      }
      else
      {
        if(AddEQCSChoice(sTmp))
        {
          SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER,
            CB_ADDSTRING, 0, (LPARAM)(LPSTR) STR_HDFILT_EQCS);
        }
//        SendDlgItemMessage (hDlg, IDC_AA_HDRFILTER,
//                    CB_ADDSTRING, 0, (LPARAM)(LPSTR) STR_HDFILT_EQNS);
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER,
          CB_ADDSTRING, 0, (LPARAM)(LPSTR) STR_HDFILT_CONT);
        SendDlgItemMessage(hDlg, IDC_AA_HDRFILTER, CB_SETCURSEL, 0, 0);
      }
    }
    return 0;  // WM_SETFILTER_OPTIONS

  case WM_COMMAND:
    pAD = pAA->GetCurrent();
    switch(wID)
    {
    case IDC_AA_HEADER:
      if(pAD && (CBN_SELCHANGE == wCode))
      {
        iSelect = (int) SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_GETCURSEL, 0, 0);
        if(iSelect != CB_ERR)
        {
          SendMessage(hDlg, WM_SETFILTER_OPTIONS, 0, 0);
          if(!pAD->ad.sParam)
          {
            SendDlgItemMessage(hDlg, IDC_AA_HEADER, CB_GETLBTEXT,
                (WPARAM) iSelect, (LPARAM) (LPSTR) sTmp);
            if(!strcmp(sTmp, STR_HEADER_LINES))
            {
              sprintf(sTmp, "%lu line(s)", Latest.lines);
              SetDlgItemText(hDlg, IDC_AA_TEXT, sTmp);
            }
            else if(!strcmp(sTmp, STR_HEADER_GROUPS))
            {
              sprintf(sTmp, "%lu group(s)", Latest.iGroups);
              SetDlgItemText(hDlg, IDC_AA_TEXT, sTmp);
              if(!(pGlob->NeedNgroupHeader() || pGrp->NeedNgroupHeader()))
              {
                MessageBox(hDlg,
                  "You are requesting a filter requiring Newsgroup header\n"
                  "data to be loaded from your news server.  This has not\n"
                  "yet been done for this newsgroup.  If you complete this\n"
                  "request by clicking on \"OK\", the header information\n"
                  "will be collected the next time you enter this newsgroup.",
                  "No newsgroup header info collected yet", MB_OK);
              }
            }
            else if(!strcmp(sTmp, STR_HEADER_FROM))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sFrom);
            }
            else if(!strcmp(sTmp, STR_HEADER_EMAIL))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sEmail);
            }
            else if(!strcmp(sTmp, STR_HEADER_SUBJ))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sSubject);
            }
            else if(!strcmp(sTmp, STR_HEADER_MSGID))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sMessage_id);
            }
            else if(!strcmp(sTmp, STR_HEADER_REF))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sMessage_id);
            }
            else if(!strcmp(sTmp, STR_HEADER_NGROUP))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sNewsgroups);
              if(!(pGlob->NeedNgroupHeader() || pGrp->NeedNgroupHeader()))
              {
                MessageBox(hDlg,
                  "You are requesting a filter requiring Newsgroup header\n"
                  "data to be loaded from your news server.  This has not\n"
                  "yet been done for this newsgroup.  If you complete this\n"
                  "request by clicking on \"OK\", the header information\n"
                  "will be collected the next time you enter this newsgroup.",
                  "No newsgroup header info collected yet", MB_OK);
              }
            }
          }
        }
        return 0;
      }
      break;
    
    case IDOK:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pGrp->WriteActions();
      pGlob->WriteActions();
      pSaveGrp->Switchwith(pGrp);
      g_pAction->Switchwith(pGlob);
      EndDialog(hDlg, TRUE);
      pAA = NULL;
      return 0;

    case IDCANCEL:
      EndDialog (hDlg, FALSE);
      pAA = NULL;
      return 0;

    case IDC_AA_NEW:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pAA->Append(MARK_SEEN_ART_SUBJ_CONTAINS, 0, "");
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      SetDlgItemText(hDlg, IDC_AA_TEXT, (const char*) Latest.sSubject);
      return 0;

    case IDC_AA_DELETE:
      pAA->DeleteNode(pAD);
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;
    
    case IDC_AA_RADIO_GLOBAL:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      iSaveGroupRadio = IDC_AA_RADIO_GLOBAL;
      CheckRadioButton(hDlg, IDC_AA_RADIO_GLOBAL, IDC_AA_RADIO_GROUP,
          iSaveGroupRadio);
      pAA = pGlob;
      pAA->ResetCurrent();
      pAD = pAA->GetCurrent();
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;

    case IDC_AA_RADIO_GROUP:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      iSaveGroupRadio = IDC_AA_RADIO_GROUP;
      CheckRadioButton(hDlg, IDC_AA_RADIO_GLOBAL, IDC_AA_RADIO_GROUP,
          iSaveGroupRadio);
      pAA = pGrp;
      pAA->ResetCurrent();
      pAD = pAA->GetCurrent();
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;

    case IDC_AA_EDIT_NEXT:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pAA->GoToNext();
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;

    case IDC_AA_EDIT_PREV:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pAA->GoToPrev();
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;

    case IDC_AA_MOVE_NEXT:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pAA->Switch(pAA->GetCurrent(), pAA->GetCurrent()->Flink);
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;

    case IDC_AA_MOVE_PREV:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pAA->Switch(pAA->GetCurrent(), pAA->GetCurrent()->Blink);
      SendMessage(hDlg, WM_DATA_TO_DLG, 0, 0);
      return 0;

    default:
      break;
    }  // switch(wID)
    break;  // WM_COMMAND
  
  default:
    return FALSE;
    break;
  }  // switch(iMessage)
  return DefWindowProc (hDlg, iMessage, wParam, lParam);
}

/* 
 * Local Variables:
 * tab-width: 2
 * end:
 */
