/* -*- Mode:C; Tab-width:4 -*- */

#include <CMIntf.h>
#include <CTBUtils.h>   
#include <strings.h>  /* for debugstr */
#include <quickdraw.h>    /* for SetPt */
#include <types.h>     /* for Boolean type */
#include <memory.h>    /* for BlockMove */
#include <addinComm.h>  /* for acb info */

#define buffersize 1024   /* what's a valid buffer size */
#define CM_Channel 2    /* took this from tcp; we need a channel for transmit_packet, but */ 
                        /* not sure what the number should be */
#define CMCommandOpcode 1			

#define a5_save_parm 0
#define length_parm 1
#define eom_parm 2
#define channel_parm 3
#define buffer_pointer_parm 4
#define error_message_parm 5
#define total_buffer_size 5120

#define spare_op 0
#define read_op 1
#define write_op 2
#define open_op 3
#define close_op 4
#define listen_op 5
#define acb_array_size 6

CMBufferSizes mybuf;
typedef CMBufferSizes *fBufPtr, **fBufHandle;

acb *
connection_acb (ConnHandle chand, short CallType)
/* Returns the acb associated with connection for call type CallType */
/* Returns NIL if either the connection record handle is NIL or if there is no associated acb */
{
	acb   *cmd;

    if (chand != nil) {
		cmd = (acb *)(* chand)->userData;		           /* Get connection acb. It has one parm for each async call type */
        if (cmd != nil)  {
			if (CallType < acb_array_size)
				return((acb *)cmd->parms.p32b.parm[CallType]);     /* can be NIL if no ACB stored there */
			else    										/* CallType subscript out of range */
				return(nil);
		} else    											/* no acb array pointer */
        	return(nil);

    } else   												/* no connection record handle */
  		return(nil);
}

void
read_completion(ConnHandle chand)
{
	acb 	*cmd;

	cmd = connection_acb(chand, read_op);     /* Returns NIL if channel handle NIL or if no ACB */
	
	if (cmd != 0) {
		SetA5(cmd->parms.p32b.parm[a5_save_parm]);
		cmd->parms.p32b.parm[length_parm] = (* chand)->asyncCount[cmDataIn];
		/* Conn mgr documentation says tool should return async EOM here */
		cmd->parms.p32b.parm[eom_parm] = (* chand)->reserved0;
		cmd->parms.p32b.parm[error_message_parm] = (* chand)->errCode;
   		transmit_packet(cmd, cmd->parms.p32b.parm[channel_parm]);
		/* a redundant call to SetA5 that probably isn't necessary */
		SetA5(cmd->parms.p32b.parm[a5_save_parm]);
	}
}


pascal CMErr
read_cm_connection(ConnHandle chand, short bptr, long tread, CMChannel chnl, 
					Boolean async, short completor, long timeout, short flags)
/* the buffer ptr, the byte count var and the EOM flag are all in the acb passed via the */
/* connection handle; the completor pointer is only available on the mac side (i.e., not passed). */
{
  	#pragma unused (bptr, tread, completor, flags)  /* all, except completor, are passed via the acb in the connrecord;  */
                                                    /* the completor is only known on the mac side, and is only here for consistency */

	CMErr    error;
    long     myA5;
    acb      *cmd;

   	myA5 = SetCurrentA5();

   /* connection record userdata field contains a pointer to an acb, we use this acb in the */
   /* completion routine to pass back information to the lisp side */

	cmd = connection_acb(chand, read_op);  /* Returns NIL if channel handle NIL or if no ACB */

	if (cmd != 0) {
	   cmd->parms.p32b.parm[a5_save_parm] = myA5;
	   error = CMRead(chand, (Ptr)(cmd->parms.p32b.parm[buffer_pointer_parm]),
					   (long *)&(cmd->parms.p32b.parm[length_parm]), 
					   chnl, async, (ProcPtr) read_completion, timeout, 
					   (short *)&(cmd->parms.p32b.parm[eom_parm]));
	} else    /* no chand or no acb */
		error = cmFailed;  

	return(error);
}

void
write_completion(ConnHandle chand)
{
	acb	*cmd;

	cmd = connection_acb(chand, write_op);		/* Returns NIL if channel handle NIL or if no ACB */

	if (cmd != 0) {    								/* Do nothing if ACB pointer has gone NIL on us */
	   	SetA5(cmd->parms.p32b.parm[a5_save_parm]);
		cmd->parms.p32b.parm[length_parm] = (* chand)->asyncCount[cmDataOut];
		cmd->parms.p32b.parm[error_message_parm] = (* chand)->errCode;
		transmit_packet(cmd, cmd->parms.p32b.parm[channel_parm]);
	}
}

pascal CMErr
write_cm_connection(ConnHandle chand, short bptr, long twrite, CMChannel chnl, 
					Boolean async, short completor, long timeout, CMFlags flags)
{
#pragma unused (bptr, twrite, completor)
	
	CMErr    error;
    acb      *cmd;
    long     myA5, oldA5;
	
	myA5 = SetCurrentA5(); 
	
	cmd = connection_acb(chand, write_op);		/* Returns NIL if channel handle NIL or if no ACB */
	
	if (cmd != 0) {
		cmd->parms.p32b.parm[a5_save_parm] = myA5;
		error = CMWrite(chand, (Ptr)&(cmd->parms.p32b.parm[buffer_pointer_parm]), 
				   (long *)&(cmd->parms.p32b.parm[length_parm]), 
				   chnl, async, (ProcPtr) write_completion, timeout, flags);
	} else   /* no connection record handle or no acb */
		error = cmFailed;
	
	oldA5 = SetA5(myA5); /* not sure this is necessary for a write to work */
	return(error);    
}


void
open_completion(ConnHandle chand)
{
    acb      *cmd;
	
	cmd = connection_acb(chand, open_op);		/* Returns NIL if channel handle NIL or if no ACB */

	if (cmd != 0) {    									/* Do nothing if ACB pointer has gone NIL on us */
		SetA5(cmd->parms.p32b.parm[a5_save_parm]);
		cmd->parms.p32b.parm[error_message_parm] = (* chand)->errCode;
		transmit_packet(cmd, cmd->parms.p32b.parm[channel_parm]);
	}
}

pascal CMErr
open_cm_connection(ConnHandle chand, Boolean async, short completor, long timeout)
{
 	#pragma unused (completor)

  	CMErr     error;
  	long      myA5, ignore;
  	acb       *cmd;

  	myA5 = SetCurrentA5();

	cmd = connection_acb(chand, open_op);		/* Returns NIL if channel handle NIL or if no ACB */

	if (cmd != 0) {
		cmd->parms.p32b.parm[a5_save_parm] = myA5;
		error = CMOpen(chand, async, (ProcPtr) open_completion, timeout);
	} else  /* no connection record handle or acb */
		error = cmFailed;

  	ignore = SetA5(myA5);
  	return(error);
}


void
close_completion(ConnHandle chand)
{
    acb      *cmd;
	
	cmd = connection_acb(chand, close_op);			/* Returns NIL if channel handle NIL or if no ACB */

	if (cmd != 0) {    									/* Do nothing if ACB pointer has gone NIL on us */
		SetA5(cmd->parms.p32b.parm[a5_save_parm]);
   		cmd->parms.p32b.parm[error_message_parm] = (* chand)->errCode;
   		transmit_packet(cmd, cmd->parms.p32b.parm[channel_parm]);
	}
}


pascal CMErr
close_cm_connection(ConnHandle chand, Boolean async, short completor, long timeout, Boolean now)
{
  	#pragma unused (completor)

   	CMErr            error;
   	CMBufferSizes    sizes;	
   	CMStatFlags      status;
   	long             myA5;
   	acb              *cmd;

   	myA5 = SetCurrentA5();

	cmd = connection_acb(chand, close_op);		/* Returns NIL if channel handle NIL or if no ACB */

   	if (cmd != 0) {
   		error = CMStatus(chand, sizes, &status);
	       
   		/* if open, close it  */
   		if (error == cmNoErr)  /* This is error for CMStatus call */
      		if (((long)status & (long) (cmStatusOpen + cmStatusOpening)) != 0) {
		  		cmd->parms.p32b.parm[a5_save_parm] = myA5;
          		error = CMClose(chand, async, (ProcPtr) close_completion, timeout, now);
       		} else 
				error = cmNotOpen;
	} else   /* no acb or no connection record handle */
		 error = cmFailed;
   
   /* debug code */
   SetA5(myA5);
   return(error);
}


void
listen_completion(ConnHandle chand)
{
    acb      *cmd;
	
	cmd = connection_acb(chand, listen_op);		/* Returns NIL if channel handle NIL or if no ACB */

	if (cmd != 0) {    									/* Do nothing if ACB pointer has gone NIL on us */
		SetA5(cmd->parms.p32b.parm[a5_save_parm]);
		cmd->parms.p32b.parm[error_message_parm] = (* chand)->errCode;
		transmit_packet(cmd, cmd->parms.p32b.parm[channel_parm]);
	}
}

pascal CMErr
listen_cm_connection(ConnHandle chand, Boolean async, short completor, long timeout)
{
 	#pragma unused (completor)
 
    CMErr    error;
    long     myA5;
    acb      *cmd;

	cmd = connection_acb(chand, listen_op);		/* Returns NIL if channel handle NIL or if no ACB */

   	if (cmd != 0) {
   		myA5 = SetCurrentA5();
   		cmd->parms.p32b.parm[a5_save_parm] = myA5;
   		error = CMListen(chand, async, (ProcPtr)listen_completion, timeout);
	} else
		error = cmFailed;

    return(error);
}


pascal void
cm_idle(ConnHandle chand)
{
	if (chand != nil)
  		CMIdle(chand);
}


pascal Ptr
get_cm_config(ConnHandle chand)
{
    Ptr strg;

    if (chand != nil) { 
    	strg = CMGetConfig(chand);
	    return(c2pstr(strg));
	} else
		return(nil);
}

/* still a problem here, but push on */
pascal short
set_cm_config(ConnHandle chand, Str255 config_string)
{
    short   error;
	char    *cstring;
    
	if (chand != nil) {
		cstring = p2cstr(config_string);
    	error = CMSetConfig(chand, cstring);
	} else
		error = cmFailed;

    return(error);
}

pascal CMErr
status_cm_connection(ConnHandle chand, fBufPtr *sizes, long *flags)
{
   	CMErr            error;
   	CMBufferSizes    mybuf;
   
	if (chand != nil) {
   		error = CMStatus(chand, mybuf, (CMStatFlags *) flags); 
   		*sizes = (fBufPtr) &mybuf;
	} else
		error = cmFailed;

   return(error);
}

pascal ConnHandle
new_cm_connection(short procid, CMRecFlags flags, long bufsizes, long refcon, long userdata)
{
   	ConnHandle     chand;
   	CMBufferSizes  mybuf;
   
   	mybuf[cmDataIn] = bufsizes;	
   	mybuf[cmDataOut] = bufsizes;
   	mybuf[cmCntlIn] = 0;
   	mybuf[cmCntlOut] = 0;
   	mybuf[cmAttnIn] = 0;
   	mybuf[cmAttnOut] = 0;
 	
   	chand = CMNew(procid, flags, mybuf, refcon, userdata);
     
	if (chand != nil)
		return(chand);
}

pascal CMErr
accept_cm_connection(ConnHandle chand, Boolean accept)
{
  	CMErr    error;
  
	if (chand != nil)
		error = CMAccept(chand, accept);
    else
		error = cmFailed;

	return(error);
}

pascal CMErr
kill_cm_io(ConnHandle chand, CMBufFields which)
{
	CMErr 	error;

	if (chand != nil)
   		error = CMIOKill(chand, which);
    else
		error = cmFailed; 
    
	return(error);
}

/* *ab* This should do nothing.  Init actually called in application startup */
pascal CMErr
init_cm()
{
   	return(nil);
}

/* *ab* This is implemented properly in the read-connection interface now, so just do nothing here.  This should not be called */
pascal short
get_eom_flag(ConnHandle chand)
{
	#pragma unused (chand)
	return(nil);

/*  CMErr          error;
  ConnEnvironRec environ; 
  short          rslt;
  
  environ.version = 0;
  
  error = CMGetConnEnvirons(chand, &environ);
  
  if (error == 0)
	 rslt = (short)(environ.flags & cmFlagsEOM);
  else rslt = error;
	
  return (rslt);        */

}	

pascal CMErr
get_cm_connenvirons(ConnHandle chand, ConnEnvironRec *environrec)
{
	CMErr     error;
  
    if (chand != nil)
	  error = CMGetConnEnvirons(chand, environrec);
    else
      error = cmFailed;
  
  	return(error);
}
   

pascal Boolean
validate_cm_connection(ConnHandle chand)
{
	Boolean    rslt;
  
   	if (chand != nil) 
		rslt = CMValidate(chand);
	else
		rslt = cmFailed;    

	return(rslt);
}

pascal void
default_cm_connection(Ptr theConfig, short procid, Boolean allocate)
{
  	CMDefault(&theConfig, procid, allocate);
}

pascal CMErr
abort_cm_connection(ConnHandle chand)
/* Abort any pending asynch open or listen. */
{
	CMErr            error;
   	
   	if (chand != nil) 
		error = CMAbort(chand);
	else   /* no connection record handle */
		error = cmFailed;

   return(error);
}

pascal void 
dispose_cm_connection(ConnHandle chand)
{
	if (chand != nil)
    	CMDispose(chand);
}

pascal void
reset_cm_connection(ConnHandle chand)
{
  	if (chand != nil)
    	CMReset(chand);
}

pascal short
get_cm_procid(Str255 name)
{
  	short   procid;
  
  	procid = CMGetProcID(name);
 	return(procid);
}


