/*
 * This is a crude attempt at doing some object-based programming in C
 */
#include "GDgopherdir.h"
#include "Malloc.h"


#include <string.h>
#include <stdio.h>
extern int DEBUG;


/***********************************************************************
** Stuff for GopherDirObjs
**
***********************************************************************/


GopherDirObj*
GDnew(size)
  int size;
{
     int i;
     GopherDirObj *temp;
     
     temp = (GopherDirObj*) malloc(sizeof(GopherDirObj));
     temp->Gophers = (GopherObj **) malloc(size * sizeof(GopherObj*));
     
     for (i = 0; i < size; i++)
	  temp->Gophers[i] = GSnew();

     temp->maxsize = size;
     temp->Title = STRnew();

     GDinit(temp);
     return(temp);
}


void
GDdestroy(gd)
  GopherDirObj *gd;
{
     int i;
     
     for (i = 0; i < gd->maxsize; i++)
	  GSdestroy(gd->Gophers[i]);

     STRdestroy(gd->Title);

     free(gd->Gophers);
     free(gd);
}


void
GDinit(gd)
  GopherDirObj *gd;
{
     int i;

     for (i=0; i<gd->maxsize; i++) {
	  GSinit(GDgetEntry(gd, i));
     }

     GDsetTop(gd, 0);

     STRinit(gd->Title);
}


/*
 * Increase the size of the GopherObj pointer array to "size"
 */

void
GDgrow(gd, size)
  GopherDirObj *gd;
  int size;
{
     GopherObj **temp;
     int i;

     if (size < gd->maxsize)
	  return; /** Size is smaller than requested **/

     temp = (GopherObj **) realloc(gd->Gophers, size*sizeof(GopherObj*));
 
     if (temp == NULL)
	  fprintf(stderr, "Out of memory!!!\n"), exit(-1);

     if (temp != gd->Gophers) {
	  gd->Gophers = temp;
     }

     /** Initialize the new GopherObjs.  **/

     for (i= gd->maxsize; i< size; i++)
	  gd->Gophers[i] = GSnew();
     
     gd->maxsize = size;
     return;
}


extern int DEBUG;

/** This proc adds a GopherObj to a gopherdir. **/
void
GDaddGS(gd, gs)
  GopherDirObj *gd;
  GopherObj *gs;
{
	int n, i;

	if (GDgetTop(gd) == gd->maxsize)
		  GDgrow(gd, gd->maxsize*2);

	if (DEBUG)
		fprintf(stderr, "Adding %s, Top=%d, Num=%d\n",
			GSgetTitle(gs), GDgetTop(gd), GSgetNum(gs));

	if ((n = GSgetNum(gs)) == 0)	/* if we don't care where it goes */
		n=1;			/* put it in the first available slot */

	if (GSgetType(GDgetEntry(gd, n-1)) != '\0') {	/* that slot is in use*/
		for (i=1; ; i++)		/* find any empty slot */
			if (GSgetType(GDgetEntry(gd, i-1)) == '\0')
				break;		/* got one */
		if (i >= GDgetTop(gd))	/* item was past end of list */
			GDsetTop(gd, i);	/* update end of list */
		if (GSgetNum(gs) != 0) {	/* if we want that slot */
			/* kick out the entry that's in our way */
			GScpy(GDgetEntry(gd, i-1), GDgetEntry(gd, n-1));
		} else {
			n = i;	/* don't be a bully */
		}
	}

	GScpy(GDgetEntry(gd, n-1), gs);		/* put our entry in place */
	if (n >= GDgetTop(gd))			/* item was past end of list */
		GDsetTop(gd, n);		/* update end of list */

}


/*
 * Really weird!!!  We need this for qsort,  don't know why we can't use
 * GScmp...
 */

int
GSmoocmp(gs1, gs2)
  GopherObj **gs1, **gs2;
{
     if (GSgetTitle(*gs1) == NULL)
	  return(1);
     if (GSgetTitle(*gs2) == NULL)
	  return(-1);

     return(strcmp(GSgetTitle(*gs1), GSgetTitle(*gs2)));
}

/*
 * Sorts a gopher directory
 */

void
GDsort(gd)
  GopherDirObj *gd;
{
     int i;

     /*** Find first non-numbered entry ***/

     for (i=0; ; i++) {
	  if (GSgetNum(GDgetEntry(gd, i)) == 0)
	       break;
     }

     /*** Everything up to i is already sorted by user-defined ordering ***/

     if (GDgetTop(gd) <= i)
	  /** No more sorting needed ***/
	  return;

     
     qsort((char *) (&(gd->Gophers[i])), gd->Top-i, 
	   sizeof(GopherObj*),GSmoocmp);

}


void
GDtoNet(gd, sockfd)
  GopherDirObj *gd;
  int sockfd;
{
     int i;

     for (i=0; i< GDgetTop(gd); i++) {
	  GStoNet(GDgetEntry(gd, i), sockfd);
     }	  

}

void
GDtoNetHTML(gd, sockfd)
  GopherDirObj *gd;
  int sockfd;
{
     int i;
     
     writestring(sockfd, "<MENU>\r\n");
     
     for (i=0; i< GDgetTop(gd); i++) {
	  writestring(sockfd, "<LI>");
	  GStoNetHTML(GDgetEntry(gd, i), sockfd);
     }	  
     writestring(sockfd, "</MENU>");
}


/*
 * Fill up a GopherDirObj with GopherObjs, given a gopher directory coming
 * from sockfd.
 *
 * For each GopherObj retrieved, eachitem() is executed.
 *
 */

int
GDfromNet(gd, sockfd, eachitem)
  GopherDirObj *gd;
  int sockfd;
  int (*eachitem)();
{
     static GopherObj *TempGopher;
     static char ZesTmp[1024];
     int j, i;

     if (TempGopher == NULL)
	  TempGopher = GSnew();

     for (j=0; ; j++) {

	  if (j == gd->maxsize)
	       GDgrow(gd, gd->maxsize*2);

	  ZesTmp[0] = '\0';
	  
	  GSinit(TempGopher);
	  i = GSfromNet(TempGopher, sockfd);
	  
	  if (i==0) {
	       GDaddGS(gd, TempGopher);
	       if (eachitem != NULL) 
		    eachitem();
	  }

	  else if (i==1)
	       return(j);

	  if (i<0) {
	       j = j-1;
	       if (j<0) j=0;
	       readline(sockfd, ZesTmp, 1024); /** Get the rest of the line **/
/*	       return(j);*/
	  }
     }
} 


/*
 * Given an open file descriptor and an inited GopherDirobj,
 *   read in gopher links, and add them to a gopherdir
 */

void
GDfromLink(gd, fd, host, port)
  GopherDirObj *gd;
  int          fd;
  char         *host;
  int          port;
{
     GopherObj *gs;

     gs = GSnew();

     while (GSfromLink(gs, fd, host, port) != -1)
	  GDaddGS(gd, gs);

     GSdestroy(gs);
}



void
GDtoLink(gd, fd)
  GopherDirObj *gd;
  int fd;
{
     int i;

     for (i=0; i< GDgetTop(gd); i++) {
	  GStoLink(GDgetEntry(gd, i), fd);
     }	  

}
