/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/



/*======================================================================
    mailindx.c
    Implements the mail index screen
     - most code here builds the header list and displays it

 ====*/
 
#include "headers.h"


static struct key_menu index_key_menu0 = 
   {  0,{
        {"?","Help",0},      {"O","OTHER CMDS",0},   {"M","Main Menu",0},
        {"V","View Mail",0}, {"P","Prev Msg",0},     {"N","Next Msg",0},
        {"-","Prev Page",0}, {"SPACE","Next Page",0},{"F","Forward",0},
        {"R","Reply",0},     {"D","Delete",0},       {"S","Save",0}}}; 

static struct key_menu index_key_menu1 =
   {  0,{
        {"?","Help",0},       {"O","OTHER CMDS",0},{"E","Export Msg",0},
        {"Z","Sort Folder",0},{"C","Compose",0},   {"L","Print",0},
        {"U","Undelete",0},   {"X","eXpunge",0},   {"T","Take Addr",0},
        {"J","Jump",0},       {"G","Go to Fldr",0},{"W","Where is",0}}};

static struct key_menu index_key_menu2 =
   {  0,{
        {"?","Help",0},       {"O","OTHER CMDS",0},{"H","Full Headers",0},
        {"|", "Pipe to cmd",0}, {NULL,NULL,0},        {NULL,NULL,0},
        {NULL,NULL,0}     ,   {NULL,NULL,0},        {NULL,NULL,0},
        {NULL,NULL,0}     ,   {NULL,NULL,0},        {NULL,NULL,0}}};


static struct key_menu nr_index_key_menu0 = 
   {  0,{
        {"?","Help",0},      {"W", "Where is", 0},    {"V","View News",0},
        {"Q","Quit",0},      {"P","Prev Article",0},  {"N","Next Article",0},
        {"-","Prev Page",0}, {"SPACE","Next Page",0}, {"F","Forward",0},
        {"J", "Jump",0},     {"Z","Sort Articles",0}, {NULL, NULL, 0}}}; 


static int which_keys = 0;

#ifdef ANSI
static long top_ent_calc(long, long);
static void update_mess_status(MAILSTREAM *, int, int);
static char *get_sub(long);
#else
static long top_ent_calc();
static void update_mess_status();
static char *get_sub();
#endif
static void sort_blip();


/*-----------
  Saved state to redraw message index body 
  ----*/
static struct index_state {
    long   current_sorted_msgno,
           max_msgno, paint_from;
} index_body_state;




/*----------------------------------------------------------------------


  ----*/
void
do_index_border(do_header, do_footer, do_clear)
     int do_header, do_footer;
{
    if(do_clear)
      ClearScreen();
    if(do_header)
      set_titlebar(ps_global->nr_mode ? "NEWS INDEX" : "MAIL INDEX",
                   1, MessageNumber,
                   ps_global->current_sorted_msgno,0,0);
    if(do_footer) {
        format_keymenu(ps_global->nr_mode ? &nr_index_key_menu0 :
               (which_keys == 0 ? &index_key_menu0 :
                which_keys == 1 ? &index_key_menu1 : &index_key_menu2),
                           ps_global->ttyo->screen_cols);
        output_keymenu(ps_global->nr_mode ? &nr_index_key_menu0 :
               (which_keys  == 0 ? &index_key_menu0 :
                 which_keys == 1 ? &index_key_menu1 : &index_key_menu2),
                           -2, 0);
    }
}

      
    

/*----------------------------------------------------------------------
        Main loop executing commands for the mail index screen

   Args: The pine_state structure

  Result: Manifold
 ----*/

void
mail_index_screen(state)
     struct pine *state;
{
    int   ch, orig_ch, lines_per_page;
    long  old_top_ent, old_current_sorted_msgno, old_max_msgno,
          changed_sorted_msgno, tmp;

    dprint(1, (debugfile, "\n\n ---- MAIL INDEX ----\n"));
    
    ch                       = 'x';/* For display_message first time through */
    state->mangled_screen    = 1;
    old_max_msgno            = 0L;
    old_top_ent              = 0L;
    old_current_sorted_msgno = 0L;
    index_body_state.paint_from = 1;

    if(state->mail_stream == NULL) {
	q_status_message(0, 1, 3, "No folder is currently open");
        state->prev_screen = mail_index_screen;
	state->next_screen = main_menu_screen;
	return;
    }

    if(state->start_in_index){
	state->start_in_index = !state->start_in_index;
	if(state->start_entry > 0)
	  state->current_sorted_msgno = min(state->start_entry,
					    state->max_msgno);
    }

    while (1) {
        lines_per_page  = max(0,state->ttyo->screen_rows - 5);
        index_body_state.paint_from = -1;

        if(state->mangled_screen) {
            state->mangled_header = 1;
            state->mangled_body   = 1;
            state->mangled_footer = 1;
            state->mangled_screen = 0;
        }
        if(state->mangled_body) {
            index_body_state.paint_from = 0;
            state->mangled_body         = 0;
        }


	/*--- Check for new mail, calculate current msgno, and redrawing ---*/
        changed_sorted_msgno = new_mail(NULL, 0, ch==NO_OP_IDLE ? 0 :
                                                   ch==NO_OP_COMMAND ? 1 : 2);
        dprint(9, (debugfile, "@@@@@ changed_sorted_msgno: %ld current:%ld\n",
                   changed_sorted_msgno,
                   state->current_sorted_msgno));
        if(changed_sorted_msgno >= 0 || state->max_msgno != old_max_msgno) {
            /* Here we have gotten some new mail, expunged or something. 
               In any case we have to figure if some painting needs doing */
            if(state->current_sorted_msgno < 0L && state->max_msgno > 0L) {
                /* Used to be no mail in folder, now there is */
                 index_body_state.paint_from = 0;                
                state->current_sorted_msgno  = 1L;
            } else if(changed_sorted_msgno >= 0L &&
                      changed_sorted_msgno < old_top_ent+(long)lines_per_page){
                /* Here we know some painting needs doing because the
                   change in the index was above the bottom of the screen.
                   (We can ignore changes in the index off the bottom of 
                    the screen -- no repainting will be required)
                 */
                dprint(9, (debugfile, "@@@@@ new: %d,  current: %d\n",
                           state->new_current_sorted_msgno,
                           state->current_sorted_msgno));
                if(state->current_sorted_msgno !=
                   state->new_current_sorted_msgno){
                    tmp = 32000; /* This code breaks for more that 32K msgs*/
                    if(state->current_sorted_msgno >= 0)
                      tmp = min(tmp, state->current_sorted_msgno);
                    if(state->new_current_sorted_msgno >= 0)
                      tmp = min(tmp, state->new_current_sorted_msgno);
                    if(changed_sorted_msgno >= 0)
                      tmp = min(tmp, changed_sorted_msgno);
                    if(index_body_state.paint_from >= 0)
                      tmp = min(tmp, index_body_state.paint_from);
                    if(tmp < 0L)
                      panic("Error calculating index position");
                    index_body_state.paint_from = tmp;
                } else  {
                    index_body_state.paint_from = 
                      index_body_state.paint_from < 0L ? changed_sorted_msgno: 
                       min(index_body_state.paint_from, changed_sorted_msgno);
                }
                if(state->new_current_sorted_msgno > 0L) {
                    if(changed_sorted_msgno >= 0)
                      state->current_sorted_msgno =
                        state->new_current_sorted_msgno;
                    state->new_current_sorted_msgno = -1L;
                }
            } else {
                state->new_current_sorted_msgno = -1L;
            }
            state->mangled_header = 1;
        }
        if(state->current_sorted_msgno > 0L) {
            if(old_top_ent !=
               top_ent_calc(state->current_sorted_msgno,(long)lines_per_page)){
                 index_body_state.paint_from = 0;

            }
        }

        if(streams_died())
          state->mangled_header = 1;

        /*------------ Update the title bar -----------*/
	if(state->mangled_header) {
            do_index_border(1,0,0);
	    state->mangled_header = 0;
	} else if(old_current_sorted_msgno != state->current_sorted_msgno) {
            MESSAGECACHE *mc;
            mc = mail_elt(state->mail_stream, state->current_sorted_msgno);
	    update_titlebar_message(state->current_sorted_msgno);
            update_del(mc == NULL ? 0 : (int)mc->deleted, 1);
	}


        /*------------- Paint/update the index screen body ---------------*/
        /* The positioning of things on the screen is controlled *only* 
           by the current message number and the number of lines on the
           screen. The total number of messages has some affect,
           as does the arrival of new mail. The mangled_body flag 
           controls when the redrawing happens. These variables along
           with the saved state in the old_.... variables are all that
           control the screen painting. Some of the calculations are done 
           in the new mail section above. */
        if(index_body_state.paint_from >= 0) {
            index_body_state.current_sorted_msgno =state->current_sorted_msgno;
            index_body_state.max_msgno            = state->max_msgno;
            if(state->current_sorted_msgno != old_current_sorted_msgno)
              index_body_state.paint_from =
                min(index_body_state.paint_from,
                   min(state->current_sorted_msgno, old_current_sorted_msgno));
            redraw_index_body();
            state->mangled_body = 0;
        } else if(state->current_sorted_msgno != old_current_sorted_msgno) {
            if(ps_global->low_speed) {
                clear_index_cache_ent(old_current_sorted_msgno);
                PutLine0(2 + (int)(old_current_sorted_msgno - old_top_ent),
                         0, "  ");
                clear_index_cache_ent(state->current_sorted_msgno);
                PutLine0(2 + (int)(state->current_sorted_msgno - old_top_ent),
                         0, "->");
            } else {
                PutLine0(2 + (int)(old_current_sorted_msgno - old_top_ent),
                         0, build_header_line(old_current_sorted_msgno, 0));
                StartInverse();

                PutLine0(2 + (int)(state->current_sorted_msgno - old_top_ent),
                         0,build_header_line(state->current_sorted_msgno, 0));
                EndInverse();
            }
            index_body_state.current_sorted_msgno =state->current_sorted_msgno;
        }
        /* Save the state to know what was drawn last */
        old_top_ent                = top_ent_calc(state->current_sorted_msgno,
                                          (long)lines_per_page);
        old_current_sorted_msgno   = state->current_sorted_msgno;
        old_max_msgno              = state->max_msgno;
        ps_global->redrawer        = redraw_index_body;


        /*------------ draw the footer/key menus ---------------*/
	if(state->mangled_footer) {
            if(!state->painted_on_startup)
              do_index_border(0, 1, 0);
	    state->mangled_footer = 0;
	}
        state->painted_on_startup = 0;

	      
          /* ------- Display any message queued up, such as new mail ----*/
        check_point(0, ch == NO_OP_IDLE ? GoodTime : ch == NO_OP_COMMAND ?
                    BadTime : VeryBadTime);
        display_message(ch);


	MoveCursor(state->ttyo->screen_rows - 3, 0);
        /* Let read_command do the fflush(stdout) */

  
        /*---------- Read command and validate it ----------------*/
	ch = read_command();
        orig_ch = ch;

	if(ch < 0x0100)
	  if(isupper(ch))
	    ch = tolower(ch);

        if(which_keys == 1)
          if(ch >= PF1 && ch <= PF12)
            ch = PF2OPF(ch);
        if(which_keys == 2)
          if(ch >= PF1 && ch <= PF12)
            ch = PF2OOPF(ch);

	ch = validatekeys(ch);


	/*----------- Execute the command ------------------*/
	switch(ch) {

            /*---------- Other commands ----------*/
          case OPF2:
          case PF2:
	  case 'o':
            if(ps_global->nr_mode)
              goto df;
            which_keys = (which_keys + 1) %
               (ps_global->feature_level == Seasoned ? 3 : 2);
	    state->mangled_footer = 1;
	    break;


            /*---------- Scroll back up ----------*/
	  case PF7 :
          case ctrl('Y'): 
	  case '-' :
	    if(old_top_ent > lines_per_page) {
                state->current_sorted_msgno = old_top_ent - lines_per_page;
#ifdef	DOS
		clear_index_cache();		/* save room on PC */
#endif
	    } else {
	        q_status_message(0, 0,1, "\007Already on first page.");
	    }
	    break;


            /*---------- Scroll forward, next page ----------*/
	  case PF8 :  /* NEXT PAGE */
          case ctrl('V'): 
	  case '+' :
	  case ' ':
	    if(lines_per_page + old_top_ent <= state->max_msgno) {
                state->current_sorted_msgno = old_top_ent + lines_per_page;
#ifdef	DOS
		clear_index_cache();		/* save room on PC */
#endif
	    } else {
	        q_status_message(0, 0,1, "\007Already on last page");
	    }
	    break;


            /*---------- Suspend Pine ----------*/
          case ctrl('Z'):
            if(!have_job_control())
              goto df;
            if(!state->can_suspend) {
                q_status_message(1, 1,3,
                            "\007Pine suspension not enabled - see help text");
                break;
            } else {
                do_suspend(state);
            }
            /*-- Fall through to redraw --*/


            /*---------- Redraw/resize ----------*/
          case KEY_RESIZE:
	  case ctrl('L') :
	    ClearScreen(); 
            state->mangled_screen  = 1;
            break;
                     

            /*---------- No op command ----------*/
          case NO_OP_IDLE:
	  case NO_OP_COMMAND :
            break;	/* no op check for new mail */

            /*---------- Default -- all other command ----------*/
          default:
          df:
#ifdef	DOS
	    clear_index_cache();		/* save room on PC */
#endif
            process_cmd(ch, 1, orig_ch);
            if(state->next_screen != SCREEN_FUN_NULL)
            {
                state->prev_screen = mail_index_screen;
      	        return;
            }
      	    if(state->status_changed)
      	      update_mess_status(state->mail_stream,
                                 ps_global->sort[old_current_sorted_msgno],
                                 old_current_sorted_msgno - old_top_ent);
	  }/* The big switch */
    } /* the BIG while loop! */
} 



/*----------------------------------------------------------------------
     Calculate the message number that should be at the top of the display

  Args: current - the current message number
        lines_per_page - the number of lines for the body of the index only

  Returns: -1 if the current message is -1 
           the message entry for the first message at the top of the screen.

When paging in the index it is always on even page boundies, and the
current message is always on the page thus the top of the page is
completely determined by the current message and the number of lines
on the page. 
 ----*/
static long
top_ent_calc(current, lines_per_page)
  long current, lines_per_page;
{
    if(current < 0L)
      return(-1);
    if(lines_per_page == 0L)
      return(current);
    return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
}



/*----------------------------------------------------------------------
    This redraws the body of the index screen possibly taking into
account and changes in the size of the screen. All the state needed to
repaint is in the static variables so this can be called from
anywhere.
 ----*/
void
redraw_index_body()
{
    long ent, scr, lines, top_ent;

    lines   = (long)(ps_global->ttyo->screen_rows - 5);
    top_ent = top_ent_calc(index_body_state.current_sorted_msgno, lines);
    ent     = max(top_ent, index_body_state.paint_from);

    dprint(9, (debugfile, "Repaint starting with %ld at %ld\n",
               ent, ent- top_ent));
    
    for(scr = ent - top_ent; scr < lines; scr++) {
        MoveCursor(2 + scr, 0);
        CleartoEOLN();
        if(ent > 0 && ent <= index_body_state.max_msgno){
            if(ent == index_body_state.current_sorted_msgno) {
                if(ps_global->low_speed)
                  clear_index_cache_ent(ent);
                else
                  StartInverse();
            }
            PutLine0(2 + (int)scr, 0, build_header_line(ent,
                      ps_global->low_speed &&
                        ent == index_body_state.current_sorted_msgno));
            if(ent == index_body_state.current_sorted_msgno &&
               !ps_global->low_speed)
              EndInverse();
            ent++;
        }
    }
}



/*----------------------------------------------------------------------
      Update the status char on the left, usually D or N

   Args:  stream      -- MAILSTREAM of current folder
          msgno       -- message number of update
          screen_line -- line on the screen the message is at

  Result: The status char is updated on the screen

   only works with current message which is high lit 
 ----*/

static void
update_mess_status(stream, msgno, screen_line)
     int msgno, screen_line;
     MAILSTREAM *stream;
{
    MESSAGECACHE *m;
    char         *string, sequence[10];

    sprintf(sequence, "%ld", msgno);
    mail_fetchflags(stream, sequence);
    m = mail_elt(stream, (long)msgno);
    string = status_string(m);
    if(!ps_global->low_speed)
      StartInverse();
    MoveCursor(2 + screen_line, 2);
    Write_to_screen(string);
    if(!ps_global->low_speed)
      EndInverse();
}
    

/*----------------------------------------------------------------------
    The index cache is just the formatted strings for the header lines
 of the messages in the current folder
  ----*/    
static char **index_cache = NULL;
static int    index_cache_size;
static int    index_cache_screen_width = -1;


/*----------------------------------------------------------------------
      Create a string summarizing the message header for index on screen

   Args:  message_number  -- Message number to create line for
          highlight       -- flag indicating the message is to be high-lit

  Result: returns a malloced string
          saves string in a cache for next call for same header

The highlight flag is unused now. It might be used for a low baud rate
implementation that outputs an arrow instead of painting the whole line
in inverse video.
 ----*/

char *
build_header_line(message_number, highlight)
     long message_number;
     int  highlight;
{
    ENVELOPE     *envelope;
    MESSAGECACHE *cache;
    int           i, subject_length, from_length;
    char          status[20], from[MAX_SCREEN_COLS/2+1], size[12],
                  subject[MAX_SCREEN_COLS/2+1];
    char         *s, *s_tmp, * buffer;
    struct date   d;


    dprint(8, (debugfile, "=== build_header_line (%ld %d) called ===\n",
               message_number, highlight));

    if(index_cache_screen_width != ps_global->ttyo->screen_cols)
      clear_index_cache(); /* Have to do it over if width changed */

    index_cache_screen_width = ps_global->ttyo->screen_cols;

    /*----- recreate cache of header lines if it has been cleared ---*/
    if(index_cache == NULL) {
	index_cache = (char **)fs_get(ps_global->max_msgno * sizeof(char *));
	index_cache_size = ps_global->max_msgno;
	for(i = 0 ; i < ps_global->max_msgno; i++)
	  index_cache[i] = NULL;
    } else if(index_cache_size < ps_global->max_msgno) {
        fs_resize((void **)&index_cache, ps_global->max_msgno* sizeof(char *));
	for(i = index_cache_size; i < ps_global->max_msgno; i++)
	  index_cache[i] = NULL;
	index_cache_size = ps_global->max_msgno;
    }
	

    /*--- Return cache entry if there is one ----*/
    if(index_cache[message_number - 1] != NULL)
      return(index_cache[message_number - 1]);

    /*---- Nothing in cache for us, create entry ---*/
    buffer = fs_get(max(ps_global->ttyo->screen_cols, 80) + 1);
    index_cache[message_number - 1] = buffer;

    envelope = mail_fetchenvelope(ps_global->mail_stream,
                                  ps_global->sort[message_number]);
    cache    = mail_elt(ps_global->mail_stream,
                        ps_global->sort[message_number]);
    if(envelope == NULL || cache == NULL) {
        sprintf(buffer,"    %-3d", message_number);
        return(buffer);
    }

    /*------------------------------------------------------
       Four parts:
          status: arrow cursor, date, msgno, status, always 15 chars
          from:   may show To:, always at least 18 chars
          size:   currently in bytes up to 99K, always 10 chars
                   when line counts become available, always 8 chars
          subject:minimum 37 chars
       over all minimum: 80 chars 
       ----------------------------------------------------------------------*/

    /*-- status --*/
    parse_date(envelope->date, &d);
    sprintf(status, "%s%s %-3ld %s %2d ",
	    (highlight && 1)? "->" : "  ", /* for when arrow cursor is back */
	     status_string(cache),
	     message_number, month_abbrev(d.month), d.day);


    /*--- from ---*/
    if(!ps_global->nr_mode) {
        from_length = 18 + max(0, ps_global->ttyo->screen_cols - 80 + 1)/2;
        if(envelope->from==NULL || address_is_us(envelope->from, ps_global)) {
            ADDRESS *addr;
            char    *a_string;
            addr = envelope->to != NULL ? envelope->to :
                     envelope->cc != NULL ? envelope->cc : NULL;
            if(addr == NULL) {
                sprintf(from, "%*.*s", from_length, from_length,
                      "                                        ");
            } else {
                a_string =  addr_list_string(addr);
                sprintf(from, "To: %-*.*s", from_length - 4, from_length - 4,
                    a_string);
                fs_give((void **)&a_string);
            }
        } else {
            mail_fetchfrom(from, ps_global->mail_stream,
                           ps_global->sort[message_number], (long)from_length);
        }
    } else {
        from[0] = '\0';
    }

    
    /*--- size ---*/
    if(!ps_global->nr_mode){
        if(cache->rfc822_size < 100000) {
            s_tmp = comatose(cache->rfc822_size);
            sprintf(size, " %-*.*s(%s) ", 6 - strlen(s_tmp),
                6 - strlen(s_tmp), "       ", s_tmp);
        } else if(cache->rfc822_size < 10000000) {
            s_tmp = comatose(cache->rfc822_size/1000);
            sprintf(size, " %-*.*s(%sK) ", 5 - strlen(s_tmp),
                5 - strlen(s_tmp), "       ", s_tmp);
        } else {
            strcpy(size, "  (****)");
        }
    } else {
        size[0] = ' ';
        size[1] = '\0';
    }

        
    /*-- subject --*/
    subject_length = ps_global->nr_mode ? 66 : 37 +
                          max(ps_global->ttyo->screen_cols - 80,0)/2;
    mail_fetchsubject(subject, ps_global->mail_stream,
                      ps_global->sort[message_number], (long)subject_length);
    for(s = subject + strlen(subject); s < subject + subject_length; s++)
      *s = ' ';
    *s = '\0';

    /*--- Put them all together ---*/
    sprintf(buffer, "%s%s%s%s", status, from, size, subject);
    buffer[index_cache_screen_width] = '\0'; /* Truncate it to proper size */
    dprint(9, (debugfile, "{%d}{%s}\n", strlen(buffer), buffer));
    return(buffer);
}



/*----------------------------------------------------------------------
     Clear a single line from the index cache for given message number

   Args: The single line to clear from index cache

  Result: entry is cleared storage is freed
 ----*/
void
clear_index_cache_ent(msgno)
     int msgno;
{
    if(index_cache != NULL && msgno <= index_cache_size && 
       index_cache[msgno - 1] != NULL) {
        fs_give((void **)&(index_cache[msgno - 1]));
        index_cache[msgno - 1] = NULL;
    }
}



/*----------------------------------------------------------------------
      Clear the cache of index headers lines from build_header...

   Args:  none

  Result: Cache is cleared

 Call this when changing folders or expunging messages, or screen resize
 ---*/
void
clear_index_cache()
{
    int i; 
    if(index_cache != NULL) {
	for(i = 0; i < index_cache_size; i++)
	  if(index_cache[i] != NULL)
	    fs_give((void **)&(index_cache[i]));
        fs_give((void **)&index_cache);

    }
    index_cache = NULL;
}

	

/*-----
   Every DISPLAY_COUNT compares, call display_message to keep
 the user informed that we're still grinding away on the sort. 
 The cost of the sort is fetching all the headers which happens in the
 first itterations of the sort
  ----*/
#define    DISPLAY_COUNT  200
static long compare_count = 0;
static long last_mess;
static int  mess_count;


/*----------------------------------------------------------------------
  Compare function for sorting on subjects. Ignores case, space and "re:"
  ----*/
int
compare_subjects(a, b)
    QSType *a, *b;
{
    char *suba, *subb;
    long *mess_a = (long *)a, *mess_b = (long *)b;
    int   res;

    sort_blip();

    suba = get_sub(*mess_a);
    subb = get_sub(*mess_b);

    res = strucmp(suba, subb);

    fs_give((void **)&suba);
    fs_give((void **)&subb);

    return(res != 0 ?  res : *mess_a - *mess_b);
}

static char *
get_sub(mess)
     long mess;
{
    ENVELOPE *e;
    char *s, *s2;    

    e = mail_fetchenvelope(ps_global->mail_stream, mess);
    if(e == NULL || e->subject == NULL) {
        s2 = cpystr("");
    } else {
        for(s = e->subject; *s && (isspace(*s) || *s == '['); s++);
        if(struncmp(s, "re:", 3) == 0)
          s += 3;
        while(*s && (isspace(*s) || *s == '['))
          s++;
        s2 = cpystr(s);
    }
    return(s2);
}



/*----------------------------------------------------------------------
   Compare the To: fields for sorting. Ignore case
   ----*/
int 
compare_to(a, b)
     QSType *a, *b;
{
    long     *mess_a = (long *)a, *mess_b = (long *)b;
    ENVELOPE *e;
    char     *b_addr, *a_addr;
    int       res;

    sort_blip();

    e = mail_fetchenvelope(ps_global->mail_stream, *mess_a);
    if(e == NULL || e->to == NULL)
      a_addr = cpystr("");
    else
      a_addr = cpystr(addr_string(e->to));

    e = mail_fetchenvelope(ps_global->mail_stream, *mess_b);
    if(e == NULL || e->to == NULL)
      b_addr = cpystr("");
    else
      b_addr = cpystr(addr_string(e->to));

    res = strcmp(a_addr, b_addr);
    fs_give((void **)&a_addr);
    fs_give((void **)&b_addr);
    return(res != 0 ? res : *mess_b - *mess_a);
}


/*----------------------------------------------------------------------
   Compares the From: field for sorting. Ignores case.
  BUG should do something when the sender is us
  ----*/
int 
compare_from(a, b)
     QSType *a, *b;
{
    long     *mess_a = (long *)a, *mess_b = (long *)b;
    ENVELOPE *e;
    char  froma[200];
    int   res;

    sort_blip();

    e = mail_fetchenvelope(ps_global->mail_stream, *mess_a);
    if(e == NULL || e->from == NULL || e->from->mailbox == NULL)  
      froma[0] = '\0';
    else
      strncpy(froma, e->from->mailbox, sizeof(froma) - 1);
    froma[sizeof(froma) - 1] = '\0';

    e = mail_fetchenvelope(ps_global->mail_stream, *mess_b);
    if(e != NULL && e->from != NULL && e->from->mailbox != NULL) {
        res = strucmp(froma, e->from->mailbox);
        return(res != 0 ? res : *mess_b - *mess_a);
    }
    return(*mess_b - *mess_a);
}


/*----------------------------------------------------------------------
   Compare the Cc: fields for sorting. Ignore case.
   ----*/
int 
compare_cc(a, b)
     QSType *a, *b;
{
    long     *mess_a = (long *)a, *mess_b = (long *)b;
    ENVELOPE *e;
    char     *b_addr, *a_addr;
    int       res;

    sort_blip();

    e = mail_fetchenvelope(ps_global->mail_stream, *mess_a);
    if(e == NULL || e->cc == NULL)
      a_addr = cpystr("");
    else
      a_addr = cpystr(addr_string(e->to));

    e = mail_fetchenvelope(ps_global->mail_stream, *mess_b);
    if(e == NULL || e->cc == NULL)
      b_addr = cpystr("");
    else
      b_addr = cpystr(addr_string(e->to));

    res = strcmp(a_addr, b_addr);
    fs_give((void **)&a_addr);
    fs_give((void **)&b_addr);
    return(res != 0 ? res : *mess_b - *mess_a);

}


/*----------------------------------------------------------------------
   Compare dates
  ----*/
int compare_message_dates(a, b)
     QSType *a, *b;
{
    long       *mess_a = (long *)a, *mess_b = (long *)b;
    int         res;
    ENVELOPE   *e_a, *e_b;
    struct date d_a, d_b;

    sort_blip();

    e_a = mail_fetchenvelope(ps_global->mail_stream, *mess_a);
    e_b = mail_fetchenvelope(ps_global->mail_stream, *mess_b);
    if(e_a == NULL && e_b == NULL)
      return(0);
    if(e_a == NULL || e_b == NULL)
      return(e_a == NULL ? -1 : 1);
    if(e_a->date == NULL && e_b->date == NULL)
      return(0);
    if(e_a->date == NULL || e_b->date == NULL)
      return(e_a->date == NULL ? -1 : 1);

    parse_date(e_a->date, &d_a);
    parse_date(e_b->date, &d_b);

    dprint(9,(debugfile,"before GMT: wkday:%d  month:%d  year:%d  day:%d  hour:%d  min:%d  sec:%d  off_gmt:%d\n",
            d_a.wkday, d_a.month, d_a.year, d_a.day, d_a.hour, d_a.minute,
              d_a.sec,d_a.hours_off_gmt));

    convert_to_gmt(&d_a);

    dprint(9,(debugfile,"after GMT: wkday:%d  month:%d  year:%d  day:%d  hour:%d  min:%d  sec:%d\n",
            d_a.wkday, d_a.month, d_a.year, d_a.day, d_a.hour, d_a.minute,
              d_a.sec));

    convert_to_gmt(&d_b);
    
    res = compare_dates(&d_a, &d_b);
    
    return(res == 0 ? *mess_a - *mess_b : res);
}

    

/*----------------------------------------------------------------------
  Compare size of messages for sorting
 ----*/
compare_size(a, b)
     QSType *a, *b;
{
    long *mess_a = (long *)a, *mess_b = (long *)b;
    long size_a, size_b, diff;
    MESSAGECACHE *mc;
    int  res;

    sort_blip();

    mail_fetchenvelope(ps_global->mail_stream, *mess_a);
    mc = mail_elt(ps_global->mail_stream, *mess_a);
    size_a = mc != NULL ? mc->rfc822_size : -1;

    mail_fetchenvelope(ps_global->mail_stream, *mess_b);
    mc = mail_elt(ps_global->mail_stream, *mess_b);
    size_b = mc != NULL ? mc->rfc822_size : -1;

    diff = size_a - size_b;
    res  = diff == 0L ? 0 : (diff > 0 ? 1 : -1); /* Convert from long to int */
    return(res =! 0 ? res : *mess_b - *mess_a); /* BUG, should
                                                   convert to int from long */
}



static void
sort_blip()
{
    compare_count++;
    if((compare_count % DISPLAY_COUNT) == 0) {
        display_message(NO_OP_COMMAND);
        if(time(0) - last_mess > 15) {
            q_status_message1(0, 2, 5, "Still sorting..%s",
                              repeat_char(mess_count, '.'));
            mess_count++;
            last_mess = time(0);
        }
    }
}

    



/*----------------------------------------------------------------------
    Sort the current folder into the order set in the sort_order
 variable
  ----*/
void
sort_current_folder()
{
    long       mess_no, *sort, tmp;
    SortOrder  so;

    sort = ps_global->sort;
    so   = ps_global->current_sort_order;

    dprint(2, (debugfile, "Sorting by %s%s\n", sort_name(so),
               ps_global->current_reverse_sort ? "/reverse" : ""));

    for(mess_no = 1; mess_no <= ps_global->max_msgno; mess_no++)
          sort[mess_no] = ps_global->current_reverse_sort ? 
                               1+ ps_global->max_msgno - mess_no: mess_no ;

    if(so != SortArrival){
        if(ps_global->max_msgno > 500) 
            q_status_message1(0, 2, 4, "Sorting folder by %s", sort_name(so));
                              
        for(mess_no = 1; mess_no <= ps_global->max_msgno; mess_no++)
          sort[mess_no] = ps_global->current_reverse_sort ? 
                               1+ ps_global->max_msgno - mess_no: mess_no ;
        compare_count = 0;
        mess_count    = 0;
        last_mess     = time(0);
        qsort(sort+1, ps_global->max_msgno, sizeof(long),
              so == SortSubject ? compare_subjects :
               so == SortFrom    ? compare_from :
                so == SortTo      ? compare_to :
                 so == SortCc      ? compare_cc :
                  so == SortDate    ? compare_message_dates :
                                       compare_size);
        if(ps_global->current_reverse_sort) 
          for(mess_no = 1; mess_no <= ps_global->max_msgno/2; mess_no++){
              tmp = sort[mess_no];
              sort[mess_no] = sort[ps_global->max_msgno - mess_no + 1];
              sort[ps_global->max_msgno - mess_no + 1] = tmp;
          }
    }
}


char *    
sort_name(so)
  SortOrder so;
{
    static char *sort_names[] = {"arrival", "date", "subject", "cc",
                                 "from",    "to",   "size",    NULL};
    static SortOrder sort_types[] = { SortArrival, SortDate, SortSubject,
                                  SortCc,      SortFrom, SortTo, SortSize};
    int x;
    for(x = 0; so != sort_types[x]; x++);
    return(sort_names[x]);
}


        
