#include <Python.h>
#include <zephyr/zephyr.h>
#include <com_err.h>
/* write various functions to wrap it by hand... */
/* consider swig... */
/* get python2.2-ext from somewhere */

static PyObject *zephyr_getsender(PyObject *self, PyObject *args) {
  return Py_BuildValue("s", ZGetSender());
}

/*
char *ZGetVariable ZP((char *));
Code_t ZSetVariable ZP((char *var, char *value));
Code_t ZUnsetVariable ZP((char *var));

int ZGetWGPort ZP((void));

Code_t ZSetDestAddr ZP((struct sockaddr_in *));
Code_t ZFormatNoticeList ZP((ZNotice_t*, char**, int,
			     char **, int*, Z_AuthProc));
Code_t ZParseNotice ZP((char*, int, ZNotice_t *));
Code_t ZReadAscii ZP((char*, int, unsigned char*, int));
Code_t ZReadAscii32 ZP((char *, int, unsigned long *));
Code_t ZReadAscii16 ZP((char *, int, unsigned short *));
Code_t ZSendPacket ZP((char*, int, int));
Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc));
Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)()));
Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc));
Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)()));
Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc));
Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc));
Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems,
				char **buffer, int *ret_len));
Code_t ZLocateUser ZP((char *, int *, Z_AuthProc));
Code_t ZRequestLocations ZP((char *, ZAsyncLocateData_t *,
			     ZNotice_Kind_t, Z_AuthProc));
Code_t ZhmStat ZP((struct in_addr *, ZNotice_t *));
*/
/*
Code_t ZInitialize ZP((void));
*/
static PyObject *ZephyrComErr;

static PyObject *zephyr_initialize(PyObject *self, PyObject *args) {
  int retval = ZInitialize();
  if (retval != ZERR_NONE) {
    PyErr_SetString(ZephyrComErr, error_message(retval));
    return NULL;
  }
  Py_INCREF(Py_None);
  return Py_None;
}


/*
Code_t ZSetServerState ZP((int));
Code_t ZSetFD ZP((int));
Code_t ZFormatSmallRawNotice ZP((ZNotice_t*, ZPacket_t, int*));
int ZCompareUID ZP((ZUnique_Id_t*, ZUnique_Id_t*));
Code_t ZSrvSendRawList ZP((ZNotice_t*, char*[], int,
			   Code_t (*)(ZNotice_t *, char *, int, int)));
Code_t ZMakeAscii ZP((char*, int, unsigned char*, int));
Code_t ZMakeAscii32 ZP((char *, int, unsigned long));
Code_t ZMakeAscii16 ZP((char *, int, unsigned int));
Code_t ZReceivePacket ZP((ZPacket_t, int*, struct sockaddr_in*));
Code_t ZCheckAuthentication ZP((ZNotice_t*, struct sockaddr_in*));
Code_t ZInitLocationInfo ZP((char *hostname, char *tty));
Code_t ZSetLocation ZP((char *exposure));
Code_t ZUnsetLocation ZP((void));
Code_t ZFlushMyLocations ZP((void));
char *ZParseExposureLevel ZP((char *text));
Code_t ZFormatRawNotice ZP((ZNotice_t *, char**, int *));
Code_t ZRetrieveSubscriptions ZP((unsigned short, int*));
Code_t ZOpenPort ZP((unsigned short *port));
Code_t ZClosePort ZP((void));
Code_t ZFlushLocations ZP((void));
Code_t ZFlushSubscriptions ZP((void));
Code_t ZFreeNotice ZP((ZNotice_t *notice));
Code_t ZParseLocations ZP((register ZNotice_t *notice,
			   register ZAsyncLocateData_t *zald, int *nlocs,
			   char **user));
int ZCompareALDPred ZP((ZNotice_t *notice, void *zald));
void ZFreeALD ZP((register ZAsyncLocateData_t *zald));
Code_t ZCheckIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from,
			  register int (*predicate) ZP((ZNotice_t *,void *)),
			  void *args));
Code_t ZPeekPacket ZP((char **buffer, int *ret_len,
		       struct sockaddr_in *from));
Code_t ZPeekNotice ZP((ZNotice_t *notice, struct sockaddr_in *from));
Code_t ZIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from,
		     int (*predicate) ZP((ZNotice_t *, void *)), void *args));
Code_t ZSubscribeTo ZP((ZSubscription_t *sublist, int nitems,
			unsigned int port));
Code_t ZSubscribeToSansDefaults ZP((ZSubscription_t *sublist, int nitems,
				    unsigned int port));
Code_t ZUnsubscribeTo ZP((ZSubscription_t *sublist, int nitems,
			  unsigned int port));
Code_t ZCancelSubscriptions ZP((unsigned int port));
int ZPending ZP((void));
Code_t ZReceiveNotice ZP((ZNotice_t *notice, struct sockaddr_in *from));
*/

/* need to construct a notice subclass with a keyworded constructor */
staticforward PyTypeObject Notice_Type;

typedef struct {
	PyObject_HEAD
	PyObject	*attr;	/* Attributes dictionary */
	ZNotice_t znotice;
} NoticeObject;

static void Notice_dealloc(PyObject* self) {
  /* clean up znotice here if we need to */
  PyObject_Del(self);
}

static PyMethodDef NoticeMethods[] = {
  { "send", Notice_send, METH_VARARGS, "Send zgram." },
  { NULL, NULL, 0, NULL },	/* end of list */
}

static PyObject* Notice_alloc(PyObject* self, PyObject* args, PyObject* kwds) {
  NoticeObject* nobj;

  char *kwlist[] = {
    "kind", 			/* int */
    "auth",			/* ? str */
    "class",			/* str */
    "instance",			/* str */
    "opcode",			/* str */
    "sender",			/* str */
    "recipient",		/* str */
    "default_format",		/* str */
    /* "message",			/* list of str */
    NULL
  };
  int kkind;
  char *kauth = NULL, *kclass = NULL, *kinstance = NULL;
  char *kopcode = NULL, *ksender = NULL, *krecip = NULL, *kdefform = NULL;
  char *kmessage = NULL;	/* wrong */

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "izzzzzzz:Notice_alloc", kwlist,
				   &kkind, &kauth, &kclass, &kinstance, &kopcode, 
				   &ksender, &krecip, &kdefform))
    return NULL;
  nobj = PyObject_New(NoticeObject, &Notice_Type);
  nobj->znotice.z_kind = kkind;
  nobj->znotice.z_class = kclass;
  nobj->znotice.z_class_inst = kinstance;
  nobj->znotice.z_opcode = kopcode;
  nobj->znotice.z_sender = ksender;
  nobj->znotice.z_recipient = krecip;
  nobj->znotice.z_default_format = kdefform;

  /* add methods here... */
  /* in particular, hook in Notice_send */
  /* do we have to give ourselves a dictionary? */
  /* looks like it - make a dict, stuff in Notice_send, 
     maybe stuff in the other fields, or accessors for them... */
  /* see http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/54352 */
  nobj->attr = PyDict_New();
  {
    /* loop later */
    PyMethodDef* pymeth = NoticeMethods;
    PyObject* pyfunct = PyCFunction_New(pymeth, NULL);
    PyDict_SetItemString(d, "send", pyfunct);
    Py_DECREF(pyfunct);
  }
  return (PyObject*)nobj;
}

static PyObject* Notice_getattr(PyObject* self, PyObject* args, PyObject* kwds) {
  return self;
}

static PyObject* Notice_setattr(PyObject* self, PyObject* args, PyObject* kwds) {
  return self;
}

/*
    char		*z_packet;
    char		*z_version;
    ZNotice_Kind_t	z_kind;
    ZUnique_Id_t	z_uid;
#define z_sender_addr	z_uid.zuid_addr
    struct		timeval z_time;
    unsigned short	z_port;
    int			z_auth;
    int			z_checked_auth;
    int			z_authent_len;
    char		*z_ascii_authent;
    char		*z_class;
    char		*z_class_inst;
    char		*z_opcode;
    char		*z_sender;
    char		*z_recipient;
    char		*z_default_format;
    char		*z_multinotice;
    ZUnique_Id_t	z_multiuid;
    ZChecksum_t		z_checksum;
    int			z_num_other_fields;
    char		*z_other_fields[Z_MAXOTHERFIELDS];
    caddr_t		z_message;
    int			z_message_len;
*/

/* Code_t ZSendNotice(notice, cert_routine) 
   with this we don't really need the other pieces... */

static PyObject *Notice_send(PyObject *self, PyObject *args) {
  NoticeObject* nobj;
  Code_t st;
  int authflag = 0;

  if (!PyArg_ParseTuple(args, "i:Notice_send", &authflag)) {
    return NULL;
  }

  nobj = (NoticeObject*)self;
  st = ZSendNotice(&nobj->znotice, authflag?ZAUTH:ZNOAUTH);
  if (st) {
    /* do something clever */
    return NULL;		/*  like raise an exception */
  } else {
    return NULL;		/* "normal" return */
  }
}

statichere PyTypeObject Notice_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"zephyr.Notice",		/*tp_name*/
	sizeof(NoticeObject),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)Notice_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)Notice_getattr, /*tp_getattr*/
	(setattrfunc)Notice_setattr, /*tp_setattr*/
	0,			/*tp_compare*/
	0,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
        0,                      /*tp_call*/
        0,                      /*tp_str*/
        0,                      /*tp_getattro*/
        0,                      /*tp_setattro*/
        0,                      /*tp_as_buffer*/
        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
        0,                      /*tp_doc*/
        0,                      /*tp_traverse*/
        0,                      /*tp_clear*/
        0,                      /*tp_richcompare*/
        0,                      /*tp_weaklistoffset*/
        0,                      /*tp_iter*/
        0,                      /*tp_iternext*/
        0,                      /*tp_methods*/
        0,                      /*tp_members*/
        0,                      /*tp_getset*/
        0,                      /*tp_base*/
        0,                      /*tp_dict*/
        0,                      /*tp_descr_get*/
        0,                      /*tp_descr_set*/
        0,                      /*tp_dictoffset*/
        0,                      /*tp_init*/
        0,                      /*tp_alloc*/
        0,                      /*tp_new*/
        0,                      /*tp_free*/
        0,                      /*tp_is_gc*/
};

static PyMethodDef ZephyrMethods[] = {
  { "initialize", zephyr_initialize, METH_NOARGS, "Initialize Zephyr Library." },
  { "getsender", zephyr_getsender, METH_NOARGS, "Get Zephyr Sender." },
  { "Notice", Notice_alloc, METH_KEYWORDS|METH_VARARGS, "Notice subtype." },
  { NULL, NULL, 0, NULL },	/* end of list */
};

#define def_to_string(n) \
  PyDict_SetItemString(d, #n, PyString_FromString(n))

#define def_to_int(n) \
  PyDict_SetItemString(d, #n, PyInt_FromLong((long)n))

void initzephyr(void) {
  PyObject *m, *d;

  m = Py_InitModule("zephyr", ZephyrMethods);
  d = PyModule_GetDict(m);
  
  /* should these even bother to be exposed? */
  def_to_string(HM_SVCNAME);
  def_to_string(HM_SRV_SVCNAME);
  def_to_string(SERVER_SVCNAME);
  def_to_string(SERVER_INSTANCE);
  def_to_string(SERVER_SERVICE);

  /* these make more sense */

  def_to_int(ZAUTH_FAILED);
  def_to_int(ZAUTH_YES);
  def_to_int(ZAUTH_NO);

  def_to_int(UNSAFE);
  def_to_int(UNACKED);
  def_to_int(ACKED);
  def_to_int(HMACK);
  def_to_int(HMCTL);
  def_to_int(SERVACK);
  def_to_int(SERVNAK);
  def_to_int(CLIENTACK);
  def_to_int(STAT);
  /* mirror ZNoticeKinds too? */


  /* com_err stuff. we *shouldn't* need the constants, but... */

  def_to_int(ZERR_PKTLEN);
  def_to_int(ZERR_HEADERLEN);
  def_to_int(ZERR_ILLVAL);
  def_to_int(ZERR_HMPORT);
  def_to_int(ZERR_PORTINUSE);
  def_to_int(ZERR_BADPKT);
  def_to_int(ZERR_VERS);
  def_to_int(ZERR_NOPORT);
  def_to_int(ZERR_NONOTICE);
  def_to_int(ZERR_QLEN);
  def_to_int(ZERR_HMDEAD);
  def_to_int(ZERR_INTERNAL);
  def_to_int(ZERR_NOLOCATIONS);
  def_to_int(ZERR_NOMORELOCS);
  def_to_int(ZERR_FIELDLEN);
  def_to_int(ZERR_BADFIELD);
  def_to_int(ZERR_SERVNAK);
  def_to_int(ZERR_AUTHFAIL);
  def_to_int(ZERR_LOGINFAIL);
  def_to_int(ZERR_NOSUBSCRIPTIONS);
  def_to_int(ZERR_NOMORESUBSCRIPTIONS);
  def_to_int(ZERR_TOOMANYSUBS);
  def_to_int(ZERR_EOF);
  def_to_int(ERROR_TABLE_BASE_zeph);

  ZephyrComErr = PyErr_NewException("zephyr.comerr", NULL, NULL);
  PyDict_SetItemString(d, "comerr", ZephyrComErr);

}