/* do_req.c
 *
 * Manage the requester window for PIV.
 *
 * (c) Copyright 1991 J.E.Hanway
 *
 * $Id: do_req.c,v 1.1 91/03/13 22:32:41 jeh Exp $
 *
 * $Log:	do_req.c,v $
 * Revision 1.1  91/03/13  22:32:41  jeh
 * Initial revision
 * 
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <string.h>
#include <stdarg.h>

#include "PIV.h"

/* Semaphore that is held whenever the code is in use
 */
struct SignalSemaphore inuse_sema;

/* use count
 */
struct SignalSemaphore usecount_sema;
long   usecount = 0;

/* keep track of the previous operation for the "cancel once" feature
 */
static struct SignalSemaphore rc_sema;
static char   lastvol[40] = "";
static long   lastrc = -1L;

void
init_semaphores(void)
{
	InitSemaphore(&inuse_sema);
	InitSemaphore(&usecount_sema);
	InitSemaphore(&rc_sema);
}

static void
free_context(context_t *ct)
{
   FreeMem(ct, sizeof(context_t));
}

static context_t *
get_context(void)
{
   int i;
   
   context_t *ct = AllocMem(sizeof(context_t), MEMF_CLEAR);
   
   if(ct) {
      for(i = 0; i < 6; i++) {
         ct->g[i] = *gproto[i];
         ct->g[i].NextGadget = &ct->g[i+1];
      }
      ct->g[4].SpecialInfo = (APTR) &ct->si;
      ct->g[5].GadgetText = &ct->it;
      ct->g[5].NextGadget = NULL;
      ct->si.Buffer = ct->strbuf;
      ct->si.MaxChars = 80;
      ct->nw = nw_proto;
      ct->nw.FirstGadget = &ct->g[0];
      ct->it = *vol_text;
      ct->it.IText = ct->line;
   }
   
   return ct;
}

/* cat() concatenates strings together and avoids linking in a large
 * implementation of sprintf.
 *
 * Usage: cat(s1, s2, s3, ..., NULL);
 * (s1 had better have enough room!)
 */
static void
cat(char *s, ...)
{
	char *s1;
	va_list ap;
   
	va_start(ap, s);
   
	*s = '\0';
   
	while(s1 = va_arg(ap, char *)) {
		strcat(s, s1);
	}
   
	va_end(ap);
}

long
do_req(struct Window *client_win, char *volume)
{
	struct Window	*win;
	struct IntuiMessage *msg;
	extern BPTR	nilfh;
	context_t	*ct;
	long		rc = -1;	/* return code */

	/* bump the use count
	 */
	ObtainSemaphore(&usecount_sema);
	usecount++;

	/* If we're the first use, grab the inuse semaphore
	 */
	if(1L == usecount)
		ObtainSemaphore(&inuse_sema);
	ReleaseSemaphore(&usecount_sema);
	
	/* If this requester is exactly the same as one which was just canceled,
	 * then don't bother displaying anything, just cancel it again.
	 * This gets around some programs that like to retry three times.
	 *
	 * Note that the string comparison is case sensitive, so if you
	 * accidentally cancel something you didn't mean to, you can force a
	 * requester to appear by fudging the case. 
	 */
	ObtainSemaphore(&rc_sema);
	if(lastrc == 0 && !strcmp(volume, lastvol)) {
		ReleaseSemaphore(&rc_sema);
		rc = 0;
		goto done;
	}
     	ReleaseSemaphore(&rc_sema);
	 
	if(!(ct = get_context())) {
		goto done;
	}
     
	if(client_win) {
		ct->nw.Screen = client_win->WScreen;
		ct->nw.Type = CUSTOMSCREEN;
	} else {
		ct->nw.Screen = NULL;
		ct->nw.Type = WBENCHSCREEN;
	}
   
	cat(ct->line, "Volume ", volume, NULL);
   
	if(win = OpenWindow(&ct->nw)) {
		(void) ActivateGadget(&ct->g[4], win, NULL);
		while(rc == -1) {
			Wait(1L << win->UserPort->mp_SigBit);
			while(msg = (struct IntuiMessage *) GetMsg(win->UserPort)) {
				struct Gadget *gad = (struct Gadget *)(msg->IAddress);

				ReplyMsg((struct Message *) msg);
				switch(gad->GadgetID) {
				case RETRY:
					rc = 1;
					break;
				case MOUNT:
					cat(ct->cmd, "mount ", volume, ":\n", NULL);
					(void) Execute(ct->cmd, nilfh, nilfh);
					rc = 1;
					break;
				case ASSIGN:
					cat(ct->cmd, "assign ", volume, ": ", ct->strbuf, "\n", NULL);
					(void) Execute(ct->cmd, nilfh, nilfh);
					rc = 1;
					break;     
				case CANCEL:
					rc = 0;
					break;
				}
			}
		}
		CloseWindow(win);
	} 

	free_context(ct);
	
done:
	/* save the return code
	 */
	ObtainSemaphore(&rc_sema);
	lastrc = rc;
	if(0 == rc)
		strncpy(lastvol, volume, 40);
	ReleaseSemaphore(&rc_sema);

	/* One less user
	 */
	ObtainSemaphore(&usecount_sema);
	usecount--;
	/* If we're the last to leave, release the inuse semaphore
	 */
	if(0L == usecount)
		ReleaseSemaphore(&inuse_sema);
	ReleaseSemaphore(&usecount_sema);

	return rc;  
}
