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


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

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


static struct ValStrTable ActionTable[] =
{
  NO_ACTION,                    "NULL",
  MARK_SEEN_ART_LINES_LT,       "MSLT",
  MARK_SEEN_ART_LINES_LT,       "SALT",
  MARK_SEEN_ART_LINES_EQ,       "MSEQ",
  MARK_SEEN_ART_LINES_EQ,       "SAEQ",
  MARK_SEEN_ART_LINES_GT,       "MSGT",
  MARK_SEEN_ART_LINES_GT,       "SAGT",
  MARK_SEEN_ART_FROM_IS,        "MSFI",
  MARK_SEEN_ART_FROM_IS,        "SAFI",
  MARK_SEEN_ART_FROM_CONTAINS,  "MSFC",
  MARK_SEEN_ART_FROM_CONTAINS,  "SAFC",
  MARK_SEEN_ART_EMAIL_IS,       "MSEI",
  MARK_SEEN_ART_EMAIL_CONTAINS, "MSEC",
  MARK_SEEN_ART_SUBJ_IS,        "MSSI",
  MARK_SEEN_ART_SUBJ_IS,        "SASI",
  MARK_SEEN_ART_SUBJ_CONTAINS,  "MSSC",
  MARK_SEEN_ART_SUBJ_CONTAINS,  "SASC",
  MARK_SEEN_ART_MSGID_IS,       "MSMI",
  MARK_SEEN_ART_MSGID_IS,       "SAMI",
  MARK_SEEN_ART_MSGID_CONTAINS, "MSMC",
  MARK_SEEN_ART_MSGID_CONTAINS, "SAMC",
  MARK_SEEN_ART_REF_CONTAINS,   "MSRC",
  MARK_SEEN_ART_REF_CONTAINS,   "SARC",
  MARK_SEEN_NGROUP_CONTAINS,    "MSNC",
  SELECT_ART_LINES_LT,          "SLLT",
  SELECT_ART_LINES_EQ,          "SLEQ",
  SELECT_ART_LINES_GT,          "SLGT",
  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   "Lines"
#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"

WVArticleAction g_action;  // 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->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_LINES_EQ:
    if(header->lines == (unsigned int) ad->iParam)
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_LINES_GT:
    if(header->lines > (unsigned int) ad->iParam)
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_FROM_IS:
    if(!_stricmp(header->from, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_FROM_CONTAINS:
    if(strstr(header->from, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_EMAIL_IS:
    if(!_stricmp(header->email, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_EMAIL_CONTAINS:
    if(strstr(header->email, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_SUBJ_IS:
    if(!_stricmp(header->subject, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_SUBJ_CONTAINS:
    if(strstr(header->subject, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_MSGID_IS:
    if(!strcmp(header->message_id, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_MSGID_CONTAINS:
    if(strstr(header->message_id, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_ART_REF_CONTAINS:
    if(strstr(header->frob_ref_list, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case MARK_SEEN_NGROUP_CONTAINS:
    if(strstr(header->newsgroups, ad->sParam))
      header->Seen = ART_KILLED;
    break;
  case SELECT_ART_LINES_LT:
    if(header->lines && (header->lines < (unsigned int) ad->iParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_LINES_EQ:
    if(header->lines == (unsigned int) ad->iParam)
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_LINES_GT:
    if(header->lines > (unsigned int) ad->iParam)
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_FROM_IS:
    if(!_stricmp(header->from, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_FROM_CONTAINS:
    if(strstr(header->from, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_EMAIL_IS:
    if(!_stricmp(header->email, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_EMAIL_CONTAINS:
    if(strstr(header->email, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_SUBJ_IS:
    if(!_stricmp(header->subject, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_SUBJ_CONTAINS:
    if(strstr(header->subject, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_MSGID_IS:
    if(!strcmp(header->message_id, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_MSGID_CONTAINS:
    if(strstr(header->message_id, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_ART_REF_CONTAINS:
    if(strstr(header->frob_ref_list, ad->sParam))
      header->Selected = ART_SELECTED;
    break;
  case SELECT_NGROUP_CONTAINS:
    if(strstr(header->newsgroups, ad->sParam))
      header->Selected = 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()
{
  Node* cur;
  g_action.ResetCurrent();
      while(cur = g_action.GetCurrent())
      {
        if(cur->ad.iAction == MARK_SEEN_NGROUP_CONTAINS) return (TRUE);
        g_action.GoToNext();
      }

  ResetCurrent();  
  while(cur = GetCurrent())
	{
	  if(cur->ad.iAction == MARK_SEEN_NGROUP_CONTAINS) return (TRUE);
	  GoToNext();
	}
  return (FALSE);
}

WVArticleAction::WVArticleAction()
{
  m_Head = NULL;
  m_Tail = NULL;
  m_Current = NULL;
  m_ProfSect[0] = 0;
}


WVArticleAction::WVArticleAction(WVArticleAction*wv)
{
  m_Head = NULL;
  m_Tail = NULL;
  m_Current = NULL;
  if(wv->m_ProfSect[0])
    strcpy(m_ProfSect, wv->m_ProfSect);
  else
    m_ProfSect[0] = 0;
  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()
{
  DeleteAll();
}


// Append a new entry to the end of the list
void WVArticleAction::Append(int iA, long int iP, 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)
  {
    TRACE0("SetIParam() error: NULL pointer\n");
    DEBUG_BREAK;
  }
  else
  {
    m_Current->ad.iParam = i;
  }
}



// Set the sParam text, adjusting string size if necessary
void WVArticleAction::SetParamText(char*sP)
{
  if(!m_Current)
  {
    TRACE0("SetParamText() error: NULL pointer\n");
    DEBUG_BREAK;
  }
  else
  {
    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);
    }
  }
}



// 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(char*sGroup)
{
  long int iTmp;
  char ProfEntry[32];
  char ProfValue[MAXINTERNALLINE];
  char *pParam;
  
  if(sGroup)
    sprintf(m_ProfSect, "G: %s", sGroup);
  else
    sprintf(m_ProfSect, "N: %s", NNTPHost);

#ifdef _DEBUG
  if(strlen(m_ProfSect) >= sizeof(m_ProfSect))
    DEBUG_BREAK;
#endif

  for(int ind = 1; ind < 1000; ind++)
  {
    sprintf(ProfEntry, "Action%02d", ind);
    
    if(!GetPrivateProfileString(m_ProfSect, 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_ProfSect, ProfEntry,
            ProfValue, szAppProFile);
          break;
        }
      }
    }
    GoToNext();
  }
  sprintf(ProfEntry, "Action%02d", ++ind);
  // TODO: if WIN32, use the Registry...
  WritePrivateProfileString(m_ProfSect, ProfEntry, NULL, szAppProFile);
}


// Do the requested action for each article
void WVArticleAction::ActOnArticles(TypGroup far *Group, header_p headers)
{
  int artindex;
  header_p header;
  
  for(artindex = 0; artindex < Group->total_headers; artindex++)
  {
    header = header_elt(headers, artindex);
    if((ART_UNSEEN == header->Seen) &&
       (ART_UNSELECTED == header->Selected))
    {
      g_action.ResetCurrent();
      while((NULL != g_action.GetCurrent()) &&
            (ART_UNSEEN == header->Seen) &&
            (ART_UNSELECTED == header->Selected))
      {
        DoAction(header, &g_action.GetCurrent()->ad);
        g_action.GoToNext();
      }
      if((ART_UNSEEN == header->Seen) &&
         (ART_UNSELECTED == header->Selected))
      {
        ResetCurrent();
        while((NULL != GetCurrent()) &&
              (ART_UNSEEN == header->Seen) &&
              (ART_UNSELECTED == header->Selected))
        {
          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 *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();
    pGrp->ReadActions(CurrentGroup);
    pGlob = new WVArticleAction(&g_action);
    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_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_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 lines", 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))
      {
        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);
      }
      else
      {
//        if(strcmp(sTmp, STR_HEADER_REF))
        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 lines", Latest.lines);
              SetDlgItemText(hDlg, IDC_AA_TEXT, sTmp);
            }
            else if(!strcmp(sTmp, STR_HEADER_FROM))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, Latest.from);
            }
            else if(!strcmp(sTmp, STR_HEADER_EMAIL))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, Latest.email);
            }
            else if(!strcmp(sTmp, STR_HEADER_SUBJ))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, Latest.subject);
            }
            else if(!strcmp(sTmp, STR_HEADER_MSGID))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, Latest.message_id);
            }
            else if(!strcmp(sTmp, STR_HEADER_REF))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, Latest.message_id);
            }
            else if(!strcmp(sTmp, STR_HEADER_NGROUP))
            {
              SetDlgItemText(hDlg, IDC_AA_TEXT, Latest.newsgroups);
            }
          }
        }
        return 0;
      }
      break;
    
    case IDOK:
      SendMessage(hDlg, WM_DLG_TO_DATA, 0, 0);
      pGrp->WriteActions();
      pGlob->WriteActions();
      g_action.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, Latest.subject);
      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);
}

