/*
 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "mutt.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
#include "sort.h"
#include "mx.h"

void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_flags)
{
  int changed = h->changed;
  int deleted = ctx->deleted;
  int tagged = ctx->tagged;
  int flagged = ctx->flagged;
  int upd_ctx = (upd_flags & MUTT_SET_FLAG_UPDATE_CONTEXT);
  int update = 0;

  if (ctx->readonly && flag != MUTT_TAG)
    return; /* don't modify anything if we are read-only */

  switch (flag)
  {
    case MUTT_DELETE:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_DELETE))
	return;

      if (bf)
      {
	if (!h->deleted && !ctx->readonly
	    && (!h->flagged || !option(OPTFLAGSAFE)))
	{
	  h->deleted = 1;
          update = 1;
	  if (upd_ctx) ctx->deleted++;
#ifdef USE_IMAP
          /* deleted messages aren't treated as changed elsewhere so that the
           * purge-on-sync option works correctly. This isn't applicable here */
          if (ctx && ctx->magic == MUTT_IMAP)
          {
            h->changed = 1;
	    if (upd_ctx) ctx->changed = 1;
          }
#endif
	}
      }
      else if (h->deleted)
      {
	h->deleted = 0;
        update = 1;
	if (upd_ctx) ctx->deleted--;
#ifdef USE_IMAP
        /* see my comment above */
	if (ctx->magic == MUTT_IMAP)
	{
	  h->changed = 1;
	  if (upd_ctx) ctx->changed = 1;
	}
#endif
	/*
	 * If the user undeletes a message which is marked as
	 * "trash" in the maildir folder on disk, the folder has
	 * been changed, and is marked accordingly.  However, we do
	 * _not_ mark the message itself changed, because trashing
	 * is checked in specific code in the maildir folder
	 * driver.
	 */
	if (ctx->magic == MUTT_MAILDIR && upd_ctx && h->trash)
	  ctx->changed = 1;
      }
      break;

    case MUTT_PURGE:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_DELETE))
        return;

      if (bf)
      {
        if (!h->purge && !ctx->readonly)
          h->purge = 1;
      }
      else if (h->purge)
        h->purge = 0;
      break;

    case MUTT_NEW:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_SEEN))
	return;

      if (bf)
      {
	if (h->read || h->old)
	{
          update = 1;
	  h->old = 0;
	  if (upd_ctx) ctx->new++;
	  if (h->read)
	  {
	    h->read = 0;
	    if (upd_ctx) ctx->unread++;
	  }
	  h->changed = 1;
	  if (upd_ctx) ctx->changed = 1;
	}
      }
      else if (!h->read)
      {
        update = 1;
	if (!h->old)
	  if (upd_ctx) ctx->new--;
	h->read = 1;
	if (upd_ctx) ctx->unread--;
	h->changed = 1;
	if (upd_ctx) ctx->changed = 1;
      }
      break;

    case MUTT_OLD:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_SEEN))
	return;

      if (bf)
      {
	if (!h->old)
	{
          update = 1;
	  h->old = 1;
	  if (!h->read)
	    if (upd_ctx) ctx->new--;
	  h->changed = 1;
	  if (upd_ctx) ctx->changed = 1;
	}
      }
      else if (h->old)
      {
        update = 1;
	h->old = 0;
	if (!h->read)
	  if (upd_ctx) ctx->new++;
	h->changed = 1;
	if (upd_ctx) ctx->changed = 1;
      }
      break;

    case MUTT_READ:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_SEEN))
	return;

      if (bf)
      {
	if (!h->read)
	{
          update = 1;
	  h->read = 1;
	  if (upd_ctx) ctx->unread--;
	  if (!h->old)
	    if (upd_ctx) ctx->new--;
	  h->changed = 1;
	  if (upd_ctx) ctx->changed = 1;
	}
      }
      else if (h->read)
      {
        update = 1;
	h->read = 0;
	if (upd_ctx) ctx->unread++;
	if (!h->old)
	  if (upd_ctx) ctx->new++;
	h->changed = 1;
	if (upd_ctx) ctx->changed = 1;
      }
      break;

    case MUTT_REPLIED:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_WRITE))
	return;

      if (bf)
      {
	if (!h->replied)
	{
          update = 1;
	  h->replied = 1;
	  if (!h->read)
	  {
	    h->read = 1;
	    if (upd_ctx) ctx->unread--;
	    if (!h->old)
	      if (upd_ctx) ctx->new--;
	  }
	  h->changed = 1;
	  if (upd_ctx) ctx->changed = 1;
	}
      }
      else if (h->replied)
      {
        update = 1;
	h->replied = 0;
	h->changed = 1;
	if (upd_ctx) ctx->changed = 1;
      }
      break;

    case MUTT_FLAG:

      if (!mutt_bit_isset(ctx->rights,MUTT_ACL_WRITE))
	return;

      if (bf)
      {
	if (!h->flagged)
	{
          update = 1;
	  h->flagged = bf;
	  if (upd_ctx) ctx->flagged++;
	  h->changed = 1;
	  if (upd_ctx) ctx->changed = 1;
	}
      }
      else if (h->flagged)
      {
        update = 1;
	h->flagged = 0;
	if (upd_ctx) ctx->flagged--;
	h->changed = 1;
	if (upd_ctx) ctx->changed = 1;
      }
      break;

    case MUTT_TAG:
      if (bf)
      {
	if (!h->tagged)
	{
          update = 1;
	  h->tagged = 1;
	  if (upd_ctx) ctx->tagged++;
	}
      }
      else if (h->tagged)
      {
        update = 1;
	h->tagged = 0;
	if (upd_ctx) ctx->tagged--;
      }
      break;
  }

  if (update)
  {
    h->color.pair = 0;
    h->color.attrs = 0;
#ifdef USE_SIDEBAR
    mutt_set_current_menu_redraw (REDRAW_SIDEBAR);
#endif
  }

  /* if the message status has changed, we need to invalidate the cached
   * search results so that any future search will match the current status
   * of this message and not what it was at the time it was last searched.
   */
  if (h->searched && (changed != h->changed || deleted != ctx->deleted || tagged != ctx->tagged || flagged != ctx->flagged))
    h->searched = 0;
}

void mutt_tag_set_flag (int flag, int bf)
{
  int j;

  for (j = 0; j < Context->vcount; j++)
    if (Context->hdrs[Context->v2r[j]]->tagged)
      mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], flag, bf);
}

int mutt_thread_set_flag (HEADER *hdr, int flag, int bf, int subthread)
{
  THREAD *start, *cur = hdr->thread;

  if ((Sort & SORT_MASK) != SORT_THREADS)
  {
    mutt_error _("Threading is not enabled.");
    return (-1);
  }

  if (!subthread)
    while (cur->parent)
      cur = cur->parent;
  start = cur;

  /* For thread flag setting, there are some corner cases that make it
   * better to set the color values afterwards.  For example coloring
   * collapsed threads with a deleted message in them '~v ~(~D)', if
   * we <undelete-thread> it's better to determine the color lazily in the
   * index after all the flag settings for the thread is done.
   */
  if (cur->message)
  {
    mutt_set_flag (Context, cur->message, flag, bf);
    cur->message->color.pair = cur->message->color.attrs = 0;
  }

  if ((cur = cur->child) == NULL)
    return (0);

  FOREVER
  {
    if (cur->message)
    {
      mutt_set_flag (Context, cur->message, flag, bf);
      cur->message->color.pair = cur->message->color.attrs = 0;
    }

    if (cur->child)
      cur = cur->child;
    else if (cur->next)
      cur = cur->next;
    else
    {
      while (!cur->next)
      {
	cur = cur->parent;
	if (cur == start)
	  return (0);
      }
      cur = cur->next;
    }
  }
  /* not reached */
}

int mutt_change_flag (HEADER *h, int bf)
{
  int i, flag;
  event_t event;

  mutt_window_mvprintw (MuttMessageWindow, 0, 0,
                        "%s? (D/N/O/r/*/!): ", bf ? _("Set flag") : _("Clear flag"));
  mutt_window_clrtoeol (MuttMessageWindow);
  mutt_refresh ();

  do
  {
    event = mutt_getch();
  } while (event.ch == -2);
  i = event.ch;
  if (i < 0)
  {
    mutt_window_clearline (MuttMessageWindow, 0);
    return (-1);
  }

  mutt_window_clearline (MuttMessageWindow, 0);

  switch (i)
  {
    case 'd':
    case 'D':
      if (!bf)
      {
        if (h)
          mutt_set_flag (Context, h, MUTT_PURGE, bf);
        else
          mutt_tag_set_flag (MUTT_PURGE, bf);
      }
      flag = MUTT_DELETE;
      break;

    case 'N':
    case 'n':
      flag = MUTT_NEW;
      break;

    case 'o':
    case 'O':
      if (h)
	mutt_set_flag (Context, h, MUTT_READ, !bf);
      else
	mutt_tag_set_flag (MUTT_READ, !bf);
      flag = MUTT_OLD;
      break;

    case 'r':
    case 'R':
      flag = MUTT_REPLIED;
      break;

    case '*':
      flag = MUTT_TAG;
      break;

    case '!':
      flag = MUTT_FLAG;
      break;

    default:
      BEEP ();
      return (-1);
  }

  if (h)
    mutt_set_flag (Context, h, flag, bf);
  else
    mutt_tag_set_flag (flag, bf);

  return 0;
}
