/* ------------------------------------------------------------------------ */
/*                                sstsel.c                                  */
/* ------------------------------------------------------------------------ */
#include <dos.h>
#include <stdio.h>
#include <string.h>
#include <mem.h>
#include <stdlib.h>
#include <conio.h>

#include "sstvid.h"
#include "sstkey.h"
#include "sstwin.h"
#include "sstsel.h"

#define KEYOK        (c != UP && c != DOWN && c != ESC && c != '\r'&& \
		      c != PGDN && c != PGUP)


typedef struct _llcfg {
	int    count;     /* total items stored */
	int    maxlen;    /* string len of biggest item */
} SelCFG;

/* ------------------------------------------------------------------------ */
/*                 record structure linked list head & tail                 */
/* ------------------------------------------------------------------------ */

SelREC *llhead = NULL;     /* head address */
SelREC *lltail = NULL;     /* tail address */

static SelCFG cfg = {
	    0,0
};

#define NEXTREC         (rec->_nx)
#define PREVREC         (rec->_pv)

/* ------------------------------------------------------------------------ */
/*                        local function prototypes                         */
/* ------------------------------------------------------------------------ */
static void    putitems (WINDOW *wnd, SelREC *rec, int c, int ptr);
static void    tagitem  (WINDOW *wnd, SelREC *rec, int ptr);
static void    arrow    (WINDOW *wnd, int p);
static char   *getitem  (int p);
static int     gettag   (int p);
static void    addlist  (SelREC *rec);


/* ------------------------------------------------------------------------ */
/*                 allocate and establish a new record                	    */
/* ------------------------------------------------------------------------ */
SelREC *Sinsert(char *s)

{
   SelREC *rec;

   if ((rec = (SelREC *) malloc(sizeof (SelREC))) == NULL)
		return NULL;
   PREVREC = NEXTREC = NULL;
   memset(rec,0x00,sizeof(SelREC));
   strcpy(rec->line,s);
   cfg.maxlen = max(cfg.maxlen,strlen(s)+4);
   cfg.count++;
   addlist(rec);
   return rec;
}


/* ------------------------------------------------------------------------ */
/*                        dump the items to the window                      */
/* ------------------------------------------------------------------------ */
static void putitems(WINDOW *wnd, SelREC *rec, int c, int ptr)

{
   rec = llhead;
   if (ptr < 0 )
	   return;
	else   {
	       while(ptr--)
	       rec = NEXTREC;
	}
   Wclear(wnd);
   Wcursor(wnd,0,0);
   while (c--)  {
	 if (! rec)
	     break;
	 Wprintf(wnd,"%c %s%c",rec->tag ? 251 : 32 ,rec->line,
		 c == 0 ? '\0': '\n');
	 rec = NEXTREC;
   }
}

/* ------------------------------------------------------------------------ */
/*                        dump the items to the window                      */
/* ------------------------------------------------------------------------ */
static void tagitem(WINDOW *wnd, SelREC *rec, int ptr)

{
   rec = llhead;
   if (ptr < 0 )
	   return;
	else   {
	       while(ptr--)
	       rec = NEXTREC;
	}
   if (rec->tag)
	 rec->tag = 0;
       else
	 rec->tag = 1;
   vputch(COL+1, ROW+SELECT, WBORDER, rec->tag ? 251 : 32);
}


/* ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------ */
static void arrow(WINDOW *wnd, int p)

{
   vputch(COL + WIDTH-1, ROW + HEIGHT-2, WBORDER, 179);
   vputch(COL + WIDTH-1, ROW + 1, WBORDER, 179);
   if (p - SELECT < ( cfg.count - SCROLL -1) )
	 vputch(COL + WIDTH-1, ROW + HEIGHT-2, WTITLEC, 25);   /* down */
   if (p - SELECT > 0)
	 vputch(COL + WIDTH-1, ROW+1, WTITLEC, 24);           /* up */
}

/* ------------------------------------------------------------------------ */
/*                       extract a item from the list                       */
/* ------------------------------------------------------------------------ */
static char *getitem(int p)

{
   SelREC *rec;

   rec = llhead;
   if (p < 0)
	   return NULL;
	else   {
	       while(p--)
	       rec = NEXTREC;
	}
   return rec->line;
}

/* ------------------------------------------------------------------------ */
/*                       extract a tag from the list                        */
/* ------------------------------------------------------------------------ */
static int gettag(int p)

{
   const SelREC *rec;

   rec = llhead;
   while(p--)
       rec = NEXTREC;
   return  rec->tag;
}

/* ------------------------------------------------------------------------ */
/*                 selectcs a string with multiple tagging                  */
/* ------------------------------------------------------------------------ */
int  Sselstr(int x, int y, int t)

{
   WINDOW *wnd;
   int yy , c = 0, p = 0;
   char *s;
   SelREC *rec;

   rec = llhead;
   yy = cfg.count+2;
   yy = (t ? t + 2 : yy);
   s = malloc(100);
   wnd = Westablish(x, y, yy, cfg.maxlen);
   if(wnd == NULL || s == NULL)
       return 0;
   Wsetcolour(wnd,WIN_BORDER, LIGHTGRAY, WHITE, DIM);
   Wsetcolour(wnd,WIN_TITLE , LIGHTGRAY, RED,BRIGHT);
   Wsetcolour(wnd,WIN_ACCENT, GREEN,     BLACK, DIM);
   Wsetcolour(wnd,WIN_FACE,   LIGHTGRAY, BLACK, DIM);
   Wshow(wnd);
   vhidecur();
   putitems(wnd,rec,t,0);
   p = SELECT;

   while (c != ESC && c != '\r')	{
   accent(wnd);
   while ((c = kgetch()) & KEYOK)
	  ;
   deaccent(wnd);
	switch (c)   {
	      case UP:  if (p <= SELECT)  {
			     if (p > 1)    {
				  SELECT--;
				  p--;
			     }
			     break;
			}
			if (SELECT == 1) {
			      if (p >= SELECT)  {
				     p--;
				     s = getitem(p-SELECT);
				     yy = gettag(p-SELECT);
				     scroll(wnd, DOWN);
				     vputf(COL+1,ROW+1,WFACE,"%c %s",
					   yy ? 251 : 32 ,s);
			      }
			}
			else  {
				SELECT--;
				p--;

			}
			break;

	      case DOWN: if (SELECT < SCROLL+1) {
				   SELECT++;
				   p++;
				   break;
			 }

			 if (p < cfg.count)  {
			     yy = gettag(p);
			     s = getitem(p++);
			     scroll(wnd, UP);
			     vputf(COL+1,ROW+SELECT,WFACE,"%c %s",yy ? 251 : 32 ,s);
			 }
			 break;

	      case ' '  :  tagitem(wnd,rec,p-1);
			   break;
	      case ESC  :  break;
	      case '\r' :  break;

		}
	  arrow(wnd,p);

	}
   Wdelete(wnd);
   vshowcur();
   return c;
}

/* ------------------------------------------------------------------------ */
/*                 selectcs a string with multiple tagging                  */
/* ------------------------------------------------------------------------ */
char *Sselonestr(int x, int y, int t)

{
   WINDOW *wnd;
   int yy , c = 0, p = 0;
   char *s;
   SelREC *rec;

   rec = llhead;
   yy = cfg.count+2;
   yy = (t ? t + 2 : yy);
   s = malloc(100);
   wnd = Westablish(x, y, yy, cfg.maxlen);
   if(wnd == NULL || s == NULL)
       return 0;
   Wsetcolour(wnd,WIN_BORDER, LIGHTGRAY, WHITE, DIM);
   Wsetcolour(wnd,WIN_TITLE , LIGHTGRAY, RED,BRIGHT);
   Wsetcolour(wnd,WIN_ACCENT, GREEN,     BLACK, DIM);
   Wsetcolour(wnd,WIN_FACE,   LIGHTGRAY, BLACK, DIM);
   Wshow(wnd);
   vhidecur();
   putitems(wnd,rec,t,0);
   p = SELECT;

   while (c != ESC && c != '\r')	{
   accent(wnd);
   while ((c = kgetch()) & KEYOK)
	  ;
   deaccent(wnd);
	switch (c)   {
	      case UP:  if (p <= SELECT)  {
			     if (p > 1)    {
				  SELECT--;
				  p--;
			     }
			     break;
			}
			if (SELECT == 1) {
			      if (p >= SELECT)  {
				     p--;
				     s = getitem(p-SELECT);
				     yy = gettag(p-SELECT);
				     scroll(wnd, DOWN);
				     vputf(COL+1,ROW+1,WFACE,"%c %s",
					   yy ? 251 : 32 ,s);
			      }
			}
			else  {
				SELECT--;
				p--;

			}
			break;

	      case DOWN: if (SELECT < SCROLL+1) {
				   SELECT++;
				   p++;
				   break;
			 }

			 if (p < cfg.count)  {
			     yy = gettag(p);
			     s = getitem(p++);
			     scroll(wnd, UP);
			     vputf(COL+1,ROW+SELECT,WFACE,"%c %s",yy ? 251 : 32 ,s);
			 }
			 break;

	      case ESC  :  break;
	      case '\r' :  break;

		}
	  arrow(wnd,p);

	}
   Wdelete(wnd);
   vshowcur();
   s = getitem(p-1);
   return c == ESC ? NULL : s;
}

/* ------------------------------------------------------------------------ */
/*                   add a record to the end of the list                    */
/* ------------------------------------------------------------------------ */
static void addlist(SelREC *rec)
{
	if (lltail)	{
		PREVREC = lltail;
		lltail->_nx = rec;
	}
	lltail = rec;
	if (!llhead)
		llhead = rec;
}


/* ------------------------------------------------------------------------ */
/*              delete all memory used by selection systems                 */
/* ------------------------------------------------------------------------ */
void Sdelete(void)

{
   SelREC *rec = llhead;
   SelREC *tmp = llhead;

   while (tmp)  {
	tmp = rec->_nx;
	free(rec);
	rec = tmp;
   }
  llhead = NULL;     /* head address */
  lltail = NULL;     /* tail address */
}



/* ------------------------------------------------------------------------ */
/*           call a function for each tagged selection item                 */
/* ------------------------------------------------------------------------ */
void Sforeach( void (*func) (char *s))

{
   SelREC *rec = llhead;
   while (rec)  {
       if (rec->tag)
	    func(rec->line);
       rec = NEXTREC;
   }

}



