#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: addrbook.c 90 2006-07-19 22:30:36Z hubert@u.washington.edu $";
#endif

/*
 * ========================================================================
 * Copyright 2013-2020 Eduardo Chappa
 * Copyright 2006-2007 University of Washington
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * ========================================================================
 */

/*====================================================================== 
    addrbook.c
    display format/support routines
 ====*/

#include "../c-client/c-client.h"

#include <system.h>
#include <general.h>

#include "addrbook.h"
#include "state.h"
#include "adrbklib.h"
#include "abdlc.h"
#include "conf.h"
#include "conftype.h"
#include "status.h"
#include "debug.h"
#include "osdep/collate.h"


/* internal prototypes */
void           parse_format(char *, COL_S *);




/*
 * We have to call this to set up the format of the columns. There is a
 * separate format for each addrbook, so we need to call this for each
 * addrbook. We call it when the pab's are built. It also depends on
 * whether or not as.checkboxes is set, so if we go into a Select mode
 * from the address book maintenance screen we need to re-call this. Since
 * we can't go back out of ListMode we don't have that problem. Restore_state
 * has to call it because of the as.checkboxes possibly being different in
 * the two states.
 */
void
addrbook_new_disp_form(PerAddrBook *pab, char **list, int addrbook_num,
		       int (*prefix_f)(PerAddrBook *, int *))
{
    char *last_one;
    int   column = 0;

    dprint((9, "- init_disp_form(%s) -\n",
	   (pab && pab->abnick) ? pab->abnick : "?"));

    memset((void *)pab->disp_form, 0, NFIELDS*sizeof(COL_S));
    pab->disp_form[1].wtype = WeCalculate; /* so we don't get false AllAuto */

    if(prefix_f)
      as.do_bold = (*prefix_f)(pab, &column);

    /* if custom format is specified */
    if(list && list[0] && list[0][0]){
	/* find the one for addrbook_num */
	for(last_one = *list;
	    *list != NULL && addrbook_num;
	    addrbook_num--,list++)
	  last_one = *list;

	/* If not enough to go around, last one repeats */
	if(*list == NULL)
	  parse_format(last_one, &(pab->disp_form[column]));
	else
	  parse_format(*list, &(pab->disp_form[column]));
    }
    else{  /* default */
	/* If 2nd wtype is AllAuto, the widths are calculated old way */
	pab->disp_form[1].wtype   = AllAuto;

	pab->disp_form[column++].type  = Nickname;
	pab->disp_form[column++].type  = Fullname;
	pab->disp_form[column++].type  = Addr;
	/* Fill in rest */
	while(column < NFIELDS)
	  pab->disp_form[column++].type = Notused;
    }
}


struct parse_tokens {
    char *name;
    ColumnType ctype;
};

struct parse_tokens ptokens[] = {
    {"NICKNAME", Nickname},
    {"FULLNAME", Fullname},
    {"ADDRESS",  Addr},
    {"FCC",      Filecopy},
    {"COMMENT",  Comment},
    {"DEFAULT",  Def},
    {NULL,       Notused}
};

/*
 * Parse format_str and fill in disp_form structure based on what's there.
 *
 * Args: format_str -- The format string from pinerc.
 *        disp_form -- This is where we fill in the answer.
 *
 * The format string consists of special tokens which give the order of
 * the columns to be displayed.  The possible tokens are NICKNAME,
 * FULLNAME, ADDRESS, FCC, COMMENT.  If a token is followed by
 * parens with an integer inside (FULLNAME(16)) then that means we
 * make that variable that many characters wide.  If it is a percentage, we
 * allocate that percentage of the columns to that variable.  If no
 * parens, that means we calculate it for the user.  The tokens are
 * delimited by white space.  A token of DEFAULT means to calculate the
 * whole thing as we would if no spec was given.  This makes it possible
 * to specify default for one addrbook and something special for another.
 */
void
parse_format(char *format_str, COL_S *disp_form)
{
    int column = 0;
    char *p, *q;
    struct parse_tokens *pt;
    int nicknames, fullnames, addresses, not_allauto;
    int warnings = 0;

    p = format_str;
    while(p && *p && column < NFIELDS){
	p = skip_white_space(p);	/* space for next word */
    
	/* look for the ptoken this word matches */
	for(pt = ptokens; pt->name; pt++)
	    if(!struncmp(pt->name, p, strlen(pt->name)))
	      break;
	
	/* ignore unrecognized word */
	if(!pt->name){
	    char *r;

	    if((r=strindex(p, SPACE)) != NULL)
	      *r = '\0';

	    dprint((2, "parse_format: ignoring unrecognized word \"%s\" in address-book-formats\n", p ? p : "?"));
	    q_status_message1(SM_ORDER, warnings++==0 ? 1 : 0, 4,
		/* TRANSLATORS: an informative error message */
		_("Ignoring unrecognized word \"%s\" in Address-Book-Formats"), p);
	    /* put back space */
	    if(r)
	      *r = SPACE;

	    /* skip unrecognized word */
	    while(p && *p && !isspace((unsigned char)(*p)))
	      p++;

	    continue;
	}

	disp_form[column].type = pt->ctype;

	/* skip over name and look for parens */
	p += strlen(pt->name);
	if(*p == '('){
	    p++;
	    q = p;
	    while(p && *p && isdigit((unsigned char)*p))
	      p++;
	    
	    if(p && *p && *p == ')' && p > q){
		disp_form[column].wtype = Fixed;
		disp_form[column].req_width = atoi(q);
	    }
	    else if(p && *p && *p == '%' && p > q){
		disp_form[column].wtype = Percent;
		disp_form[column].req_width = atoi(q);
	    }
	    else{
		disp_form[column].wtype = WeCalculate;
		if(disp_form[column].type == Nickname)
		  disp_form[column].req_width = 8;
		else
		  disp_form[column].req_width = 3;
	    }
	}
	else{
	    disp_form[column].wtype     = WeCalculate;
	    if(disp_form[column].type == Nickname)
	      disp_form[column].req_width = 8;
	    else
	      disp_form[column].req_width = 3;
	}

	if(disp_form[column].type == Def){
	    /* If any type is DEFAULT, the widths are calculated old way */
assign_default:
	    column = 0;

	    disp_form[column].wtype  = AllAuto;
	    disp_form[column++].type = Nickname;
	    disp_form[column].wtype  = AllAuto;
	    disp_form[column++].type = Fullname;
	    disp_form[column].wtype  = AllAuto;
	    disp_form[column++].type = Addr;
	    /* Fill in rest */
	    while(column < NFIELDS)
	      disp_form[column++].type = Notused;

	    return;
	}

	column++;
	/* skip text at end of word */
	while(p && *p && !isspace((unsigned char)(*p)))
	  p++;
    }

    if(column == 0){
	q_status_message(SM_ORDER, 0, 4,
	_("Address-Book-Formats has no recognizable words, using default format"));
	goto assign_default;
    }

    /* Fill in rest */
    while(column < NFIELDS)
      disp_form[column++].type = Notused;

    /* check to see if user is just re-ordering default fields */
    nicknames = 0;
    fullnames = 0;
    addresses = 0;
    not_allauto = 0;
    for(column = 0; column < NFIELDS; column++){
	if(disp_form[column].type != Notused
	   && disp_form[column].wtype != WeCalculate)
	  not_allauto++;

	switch(disp_form[column].type){
	  case Nickname:
	    nicknames++;
	    break;

	  case Fullname:
	    fullnames++;
	    break;

	  case Addr:
	    addresses++;
	    break;

	  case Filecopy:
	  case Comment:
	    not_allauto++;
	    break;

	  default:
	    break;
	}
    }

    /*
     * Special case: if there is no address field specified, we put in
     * a special field called WhenNoAddrDisplayed, which causes list
     * entries to be displayable in all cases.
     */
    if(!addresses){
	for(column = 0; column < NFIELDS; column++)
	  if(disp_form[column].type == Notused)
	    break;
	
	if(column < NFIELDS){
	    disp_form[column].type  = WhenNoAddrDisplayed;
	    disp_form[column].wtype = Special;
	}
    }

    if(nicknames == 1 && fullnames == 1 && addresses == 1 && not_allauto == 0)
      disp_form[0].wtype = AllAuto; /* set to do default widths */
}


/*
 * Find the first selectable line greater than or equal to line.  That is,
 * the first line the cursor is allowed to start on.
 * (If there are none >= line, it will find the highest one.)
 *
 * Returns the line number of the found line or NO_LINE if there isn't one.
 */
long
first_selectable_line(long int line)
{
    long lineno;
    register PerAddrBook *pab;
    int i;

    /* skip past non-selectable lines */
    for(lineno=line;
	!line_is_selectable(lineno) && dlist(lineno)->type != End;
	lineno++)
	;/* do nothing */

    if(line_is_selectable(lineno))
      return(lineno);

    /*
     * There were no selectable lines from lineno on down.  Trying looking
     * back up the list.
     */
    for(lineno=line-1;
	!line_is_selectable(lineno) && dlist(lineno)->type != Beginning;
	lineno--)
	;/* do nothing */

    if(line_is_selectable(lineno))
      return(lineno);

    /*
     * No selectable lines at all.
     * If some of the addrbooks are still not displayed, it is too
     * early to set the no_op_possbl flag.  Or, if some of the addrbooks
     * are empty but writable, then we should not set it either.
     */
    for(i = 0; i < as.n_addrbk; i++){
	pab = &as.adrbks[i];
	if(pab->ostatus != Open &&
	   pab->ostatus != HalfOpen &&
	   pab->ostatus != ThreeQuartOpen)
	  return NO_LINE;

	if(pab->access == ReadWrite && adrbk_count(pab->address_book) == 0)
	  return NO_LINE;
    }

    as.no_op_possbl++;
    return NO_LINE;
}


/*
 * Returns 1 if this line is of a type that can have a cursor on it.
 */
int
line_is_selectable(long int lineno)
{
    register AddrScrn_Disp *dl;

    if((dl = dlist(lineno)) && (dl->type == Text      ||
				dl->type == ListEmpty ||
				dl->type == TitleCmb  ||
				dl->type == Beginning ||
				dl->type == End)){

	return 0;
    }

    return 1;
}
