/***********************************************************
Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
Amsterdam, The Netherlands.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.

STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

******************************************************************/

/* Note: This file is partially converted to the new naming standard */
/* ctbcm objects */


#include "allobjects.h"
#include "modsupport.h"		/* For getargs() etc. */

#include <stdio.h>
#include <CommResources.h>
#include <Connections.h>
#include <ToolUtils.h>
#include <OSUtils.h>
/* #include <pascal.h> */
#ifndef __MWERKS__
#define ConnectionCompletionUPP ProcPtr
#define ConnectionChooseIdleUPP ProcPtr
#define NewConnectionCompletionProc(x) (x)
#define NewConnectionChooseIdleProc(x) (x)
#endif

#define 	_UnimplementedToolTrap	0xA89F
#define 	_CommToolboxTrap		0x8B
#define 	_UnimplementedOSTrap	0x9F

extern object *PyErr_Mac(object *,int);

static object *ErrorObject;

typedef struct {
	OB_HEAD
	ConnHandle hdl;		/* The handle to the connection */
	object *callback;	/* Python callback routine */
	int has_callback;	/* True if callback not None */
	int err;			/* Error to pass to the callback */
} ctbcmobject;

staticforward typeobject ctbcmtype;

#define is_ctbcmobject(v)		((v)->ob_type == &ctbcmtype)

static
TrapAvailable(short tNumber, TrapType tType)
{
	short unImplemented;
	
	if (tType == OSTrap)
		unImplemented = _UnimplementedOSTrap;
	else
		unImplemented = _UnimplementedToolTrap;

	return NGetTrapAddress(tNumber, tType) != NGetTrapAddress(unImplemented, tType);
}

static
initialize_ctb()
{
	OSErr err;
	static initialized = -1;
	
	if ( initialized >= 0 )
		return initialized;
	initialized = 0;
	
	if ( !TrapAvailable(_CommToolboxTrap, OSTrap) ) {
		err_setstr(ErrorObject, "CTB not available");
		return 0;
	}
	if ( (err=InitCTBUtilities()) ) {
		PyErr_Mac(ErrorObject, (int)err);
		return 0;
	}
	if ( (err=InitCRM()) ) {
		PyErr_Mac(ErrorObject, (int)err);
		return 0;
	}
	if ( (err=InitCM()) ) {
		PyErr_Mac(ErrorObject, (int)err);
		return 0;
	}
	initialized = 1;
	return 1;
}

static int
ctbcm_pycallback(arg)
	void *arg;
{
	ctbcmobject *self = (ctbcmobject *)arg;
	object *args, *rv;
	
	if ( !self->has_callback )    /* It could have been removed in the meantime */
		return 0;
	args = mkvalue("(i)", self->err);
	rv = call_object(self->callback, args);
	DECREF(args);
	if( rv == NULL )
		return -1;
	DECREF(rv);
	return 0;
}

/*DBG*/int ncallback;
static pascal void
ctbcm_ctbcallback(hconn)
	ConnHandle hconn;
{
	ctbcmobject *self;
	
	/* XXXX Do I have to do the A5 mumbo-jumbo? */
	ncallback++; /*DBG*/
	self = (ctbcmobject *)CMGetUserData(hconn);
	self->err = (int)((*hconn)->errCode);
	Py_AddPendingCall(ctbcm_pycallback, (void *)self);
}

static ctbcmobject *
newctbcmobject(arg)
	object *arg;
{
	ctbcmobject *self;
	self = NEWOBJ(ctbcmobject, &ctbcmtype);
	if (self == NULL)
		return NULL;
	self->hdl = NULL;
	INCREF(None);
	self->callback = None;
	self->has_callback = 0;
	return self;
}

/* ctbcm methods */

static void
ctbcm_dealloc(self)
	ctbcmobject *self;
{
	if ( self->hdl ) {
		(void)CMClose(self->hdl, 0, (ConnectionCompletionUPP)0, 0, 1);
		/*XXXX Is this safe? */
		CMDispose(self->hdl);
		self->hdl = NULL;
	}
	DEL(self);
}

static object *
ctbcm_open(self, args)
	ctbcmobject *self;
	object *args;
{
	long timeout;
	OSErr err;
	ConnectionCompletionUPP cb_upp = NewConnectionCompletionProc(ctbcm_ctbcallback);
	
	if (!getargs(args, "l", &timeout))
		return NULL;
	if ( (err=CMOpen(self->hdl, self->has_callback, cb_upp, timeout)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	INCREF(None);
	return None;
}

static object *
ctbcm_listen(self, args)
	ctbcmobject *self;
	object *args;
{
	long timeout;
	OSErr err;
	ConnectionCompletionUPP cb_upp = NewConnectionCompletionProc(ctbcm_ctbcallback);
	
	if (!getargs(args, "l", &timeout))
		return NULL;
	if ( (err=CMListen(self->hdl,self->has_callback, cb_upp, timeout)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	INCREF(None);
	return None;
}

static object *
ctbcm_accept(self, args)
	ctbcmobject *self;
	object *args;
{
	int accept;
	OSErr err;
	
	if (!getargs(args, "i", &accept))
		return NULL;
	if ( (err=CMAccept(self->hdl, accept)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	INCREF(None);
	return None;
}

static object *
ctbcm_close(self, args)
	ctbcmobject *self;
	object *args;
{
	int now;
	long timeout;
	OSErr err;
	ConnectionCompletionUPP cb_upp = NewConnectionCompletionProc(ctbcm_ctbcallback);
	
	if (!getargs(args, "(li)", &timeout, &now))
		return NULL;
	if ( (err=CMClose(self->hdl, self->has_callback, cb_upp, timeout, now)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	INCREF(None);
	return None;
}

static object *
ctbcm_read(self, args)
	ctbcmobject *self;
	object *args;
{
	long timeout, len;
	int chan;
	CMFlags flags;
	OSErr err;
	object *rv;
	ConnectionCompletionUPP cb_upp = NewConnectionCompletionProc(ctbcm_ctbcallback);
	
	if (!getargs(args, "(lil)", &len, &chan, &timeout))
		return NULL;
	if ((rv=newsizedstringobject(NULL, len)) == NULL)
		return NULL;
	if ((err=CMRead(self->hdl, (Ptr)getstringvalue(rv), &len, (CMChannel)chan,
				self->has_callback, cb_upp, timeout, &flags)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	resizestring(&rv, len);
	return mkvalue("(Oi)", rv, (int)flags);
}

static object *
ctbcm_write(self, args)
	ctbcmobject *self;
	object *args;
{
	long timeout, len;
	int chan, ilen, flags;
	OSErr err;
	char *buf;
	ConnectionCompletionUPP cb_upp = NewConnectionCompletionProc(ctbcm_ctbcallback);
	
	if (!getargs(args, "(s#ili)", &buf, &ilen, &chan, &timeout, &flags))
		return NULL;
	len = ilen;
	if ((err=CMWrite(self->hdl, (Ptr)buf, &len, (CMChannel)chan,
				self->has_callback, cb_upp, timeout, (CMFlags)flags)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	return newintobject((int)len);
}

static object *
ctbcm_status(self, args)
	ctbcmobject *self;
	object *args;
{
	CMBufferSizes sizes;
	CMStatFlags flags;
	OSErr err;
	object *rv;
	
	if (!getnoarg(args))
		return NULL;
	if ((err=CMStatus(self->hdl, sizes, &flags)) < 0)
		return PyErr_Mac(ErrorObject, (int)err);
	rv = mkvalue("(llllll)", sizes[0], sizes[1], sizes[2], sizes[3], sizes[4], sizes[5]);
	if ( rv == NULL )
		return NULL;
	return mkvalue("(Ol)", rv, (long)flags);
}

static object *
ctbcm_getconfig(self, args)
	ctbcmobject *self;
	object *args;
{
	char *rv;

	if (!getnoarg(args))
		return NULL;
	if ((rv=(char *)CMGetConfig(self->hdl)) == NULL ) {
		err_setstr(ErrorObject, "CMGetConfig failed");
		return NULL;
	}
	return newstringobject(rv);
}

static object *
ctbcm_setconfig(self, args)
	ctbcmobject *self;
	object *args;
{
	char *cfg;
	OSErr err;
	
	if (!getargs(args, "s", &cfg))
		return NULL;
	if ((err=CMSetConfig(self->hdl, (Ptr)cfg)) < 0)
		return PyErr_Mac(ErrorObject, err);
	return newintobject((int)err);
}

static object *
ctbcm_choose(self, args)
	ctbcmobject *self;
	object *args;
{
	int rv;
	Point pt;

	if (!getnoarg(args))
		return NULL;
	pt.v = 40;
	pt.h = 40;
	rv=CMChoose(&self->hdl, pt, (ConnectionChooseIdleUPP)0);
	return newintobject(rv);
}

static object *
ctbcm_idle(self, args)
	ctbcmobject *self;
	object *args;
{
	if (!getnoarg(args))
		return NULL;
	CMIdle(self->hdl);
	INCREF(None);
	return None;
}

static object *
ctbcm_abort(self, args)
	ctbcmobject *self;
	object *args;
{
	if (!getnoarg(args))
		return NULL;
	CMAbort(self->hdl);
	INCREF(None);
	return None;
}

static object *
ctbcm_reset(self, args)
	ctbcmobject *self;
	object *args;
{
	if (!getnoarg(args))
		return NULL;
	CMReset(self->hdl);
	INCREF(None);
	return None;
}

static object *
ctbcm_break(self, args)
	ctbcmobject *self;
	object *args;
{
	long duration;
	ConnectionCompletionUPP cb_upp = NewConnectionCompletionProc(ctbcm_ctbcallback);
	
	if (!getargs(args, "l", &duration))
		return NULL;
	CMBreak(self->hdl, duration,self->has_callback, cb_upp);
	INCREF(None);
	return None;
}

static struct methodlist ctbcm_methods[] = {
	{"Open",		(method)ctbcm_open},
	{"Close",		(method)ctbcm_close},
	{"Read",		(method)ctbcm_read},
	{"Write",		(method)ctbcm_write},
	{"Status",		(method)ctbcm_status},
	{"GetConfig",	(method)ctbcm_getconfig},
	{"SetConfig",	(method)ctbcm_setconfig},
	{"Choose",		(method)ctbcm_choose},
	{"Idle",		(method)ctbcm_idle},
	{"Listen",		(method)ctbcm_listen},
	{"Accept",		(method)ctbcm_accept},
	{"Abort",		(method)ctbcm_abort},
	{"Reset",		(method)ctbcm_reset},
	{"Break",		(method)ctbcm_break},
	{NULL,		NULL}		/* sentinel */
};

static object *
ctbcm_getattr(self, name)
	ctbcmobject *self;
	char *name;
{
	if ( strcmp(name, "callback") == 0 ) {
		INCREF(self->callback);
		return self->callback;
	}
	return findmethod(ctbcm_methods, (object *)self, name);
}

static int
ctbcm_setattr(self, name, v)
	ctbcmobject *self;
	char *name;
	object *v;
{
	if ( strcmp(name, "callback") != 0 ) {
		err_setstr(AttributeError, "ctbcm objects have callback attr only");
		return -1;
	}
	if ( v == NULL ) {
		v = None;
	}
	INCREF(v);	/* XXXX Must I do this? */
	self->callback = v;
	self->has_callback = (v != None);
	return 0;
}

static typeobject ctbcmtype = {
	OB_HEAD_INIT(&Typetype)
	0,			/*ob_size*/
	"ctbcm",			/*tp_name*/
	sizeof(ctbcmobject),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)ctbcm_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)ctbcm_getattr, /*tp_getattr*/
	(setattrfunc)ctbcm_setattr, /*tp_setattr*/
	0,			/*tp_compare*/
	0,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};
/* --------------------------------------------------------------------- */

/* Function of no arguments returning new ctbcm object */

static object *
ctb_cmnew(self, args)
	object *self; /* Not used */
	object *args;
{
	int strlen;
	object *sizes_obj;
	char *c_str;
	unsigned char p_str[255];
	CMBufferSizes sizes;
	short procid;
	ConnHandle hdl;
	ctbcmobject *rv;
	
	if (!getargs(args, "(s#O)", &c_str, &strlen, &sizes_obj))
		return NULL;
	strncpy((char *)p_str+1, c_str, strlen);
	p_str[0] = strlen;
	if (!initialize_ctb())
		return NULL;
	if ( sizes_obj == None ) {
		memset(sizes, '\0', sizeof sizes);
	} else {
		if ( !getargs(sizes_obj, "(llllll)", &sizes[0], &sizes[1], &sizes[2],
						&sizes[3], &sizes[4], &sizes[5]))
			return NULL;
	}
	if ( (procid=CMGetProcID(p_str)) < 0 )
		return PyErr_Mac(ErrorObject, procid);
	hdl = CMNew(procid, cmNoMenus|cmQuiet, sizes, 0, 0);
	if ( hdl == NULL ) {
		err_setstr(ErrorObject, "CMNew failed");
		return NULL;
	}
	rv = newctbcmobject(args);
	if ( rv == NULL )
	    return NULL;   /* XXXX Should dispose of hdl */
	rv->hdl = hdl;
	CMSetUserData(hdl, (long)rv);
	return (object *)rv;
}

static object *
ctb_available(self, args)
	object *self;
	object *args;
{
	int ok;
	
	if (!getnoarg(args))
		return NULL;
	ok = initialize_ctb();
	err_clear();
	return newintobject(ok);
}

/* List of functions defined in the module */

static struct methodlist ctb_methods[] = {
	{"CMNew",		ctb_cmnew},
	{"available",	ctb_available},
	{NULL,		NULL}		/* sentinel */
};


/* Initialization function for the module (*must* be called initctb) */

void
initctb()
{
	object *m, *d, *o;

	/* Create the module and add the functions */
	m = initmodule("ctb", ctb_methods);

	/* Add some symbolic constants to the module */
	d = getmoduledict(m);
	
#define CMCONST(name, value) o = newintobject(value); dictinsert(d, name, o)

	CMCONST("cmData", 1);
	CMCONST("cmCntl", 2);
	CMCONST("cmAttn", 3);
	
	CMCONST("cmFlagsEOM", 1);
	
	CMCONST("chooseDisaster", -2);
	CMCONST("chooseFailed", -1);
	CMCONST("chooseAborted", 0);
	CMCONST("chooseOKMinor", 1);
	CMCONST("chooseOKMajor", 2);
	CMCONST("chooseCancel", 3);
	
	CMCONST("cmStatusOpening", 1);
	CMCONST("cmStatusOpen", 2);
	CMCONST("cmStatusClosing", 4);
	CMCONST("cmStatusDataAvail", 8);
	CMCONST("cmStatusCntlAvail", 0x10);
	CMCONST("cmStatusAttnAvail", 0x20);
	CMCONST("cmStatusDRPend", 0x40);
	CMCONST("cmStatusDWPend", 0x80);
	CMCONST("cmStatusCWPend", 0x100);
	CMCONST("cmStatusCWPend", 0x200);
	CMCONST("cmStatusARPend", 0x400);
	CMCONST("cmStatusAWPend", 0x800);
	CMCONST("cmStatusBreakPending", 0x1000);
	CMCONST("cmStatusListenPend", 0x2000);
	CMCONST("cmStatusIncomingCallPresent", 0x4000);
	
	ErrorObject = newstringobject("ctb.error");
	dictinsert(d, "error", ErrorObject);

	/* Check for errors */
	if (err_occurred())
		fatal("can't initialize module ctb");
}
