// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//

static char copyright[] = "Copyright 1993, Center for Computer Vision and Visualization,\nUniversity of Florida.  All rights reserved.\n";

static char rcsid[] = "$Id: ImageDisplayProtocol.c,v 1.5 1993/05/31 15:57:01 thoth Exp $";

//
// $Log: ImageDisplayProtocol.c,v $
// Revision 1.5  1993/05/31  15:57:01  thoth
// unix domain port name changed.
//
// Revision 1.4  93/05/27  15:04:09  thoth
// Copyright Notices
// 
// Revision 1.3  93/05/26  16:05:04  thoth
// Array is now IA_Array.
// 
// Revision 1.2  92/11/19  01:19:55  thoth
// Flubber solves packet length problem (what a hack!)
// 
// Revision 1.1  92/11/18  15:35:10  thoth
// Initial revision
// 
//

#include "ImageDisplayProtocol.h"
#include <iostream.h>
#include <sysent.h>

//
int IDP::PacketHeader::my_version = 0;
const unsigned IDP::ImageTypeSizes[4] = {1}; // I don\'t know the others
const unsigned IDP::maxdims=2;
//
//
//

int IDP::ClientID:: write_bytes(const void *data, unsigned len)
{
#ifdef USE_FP
    return len==fwrite(data, 1, len, fp);
#else
    unsigned	written=0, rval;
    
    while (written<len) {
	rval = write(desc, (char*)data+written, len-written);
	if (rval<0) {
	    perror("writing to IDM connection");
	    close_desc();
	    return 0;
	    // exit(1);
	}
	written += rval;
    }
    return 1;
#endif
}

int IDP::ClientID:: read_bytes(void *data, unsigned len)
{
#ifdef USE_FP
    return len==fread(data, 1, len, fp);
#else
    unsigned	offset=0, rval;
    
    while (offset<len) {
	rval = read(desc, (char*)data+offset, len-offset);
	if (rval<0) {
	    perror("reading from IDM connection");
	    close_desc();
	    return 0;
	    // exit(1);
	} else if (rval==0) {
	    cerr << "EOF on IDM connection\n";
	    close_desc();
	    return 0;
	}
	offset += rval;
    }
    return 1;
#endif
}

int IDP::ClientID:: write_packet(PacketType type, const void *data, unsigned len)
{
    PacketHeader	hdr(type);
    hdr.len = len;
    return write_bytes(&hdr, sizeof(hdr))
	&& write_bytes(data, len);
}

void * IDP::ClientID:: read_packet(PacketType *type, unsigned *len)
{
    PacketHeader	hdr;
    read_bytes(&hdr, sizeof(hdr));
    if (!hdr.is_valid()) {
	cerr << "invalid IDM header read\n";
	close_desc();
	return 0;
    }
    *type = (PacketType)hdr.type;
    *len = hdr.len;

    char *rval;
    rval = new char[*len];
    read_bytes(rval, *len);

#if 0
    cout.setf(ios::basefield, ios::hex);
    for (unsigned i=0; i<*len; i++) {
	cout << (unsigned)((unsigned char)rval[i]>>8)
	     << (unsigned)(rval[i]&0x0f);
    }
    cout.setf(ios::basefield, ios::dec);
    cout << endl;
#endif
    return rval;
}

//
//
//

char * IDP::expect_packet(ClientID &id, PacketType type, unsigned *len)
{
    PacketType	temp;
    void	*rval;
    rval = id.read_packet(&temp, len);
    if (temp!=type) {
	cerr << "was expecting packet type " << type << " not "<<temp <<".\n";
	// return 0;
    }
    return (char*)rval;
}

IDP::ClientID IDP::NewClient(const char *cname)
{
    ClientID	rval;
    rval.desc = socket(AF_UNIX, SOCK_STREAM, 0);
    if (rval.desc<0) {
	perror("creating socket");
	return rval;
    }

    struct sockaddr_un	addr;
    addr.sun_family = AF_UNIX;

    char	*home = getenv("HOME");
    if (home!=0) {
	strcpy(addr.sun_path, home);
	int	len = strlen(home);
	if (addr.sun_path[len-1]!='/') {
	    addr.sun_path[len] = '/';
	    addr.sun_path[len+1] = 0;
	}
    } else {
	addr.sun_path[0] = 0;
    }
#if 0
    strcat(addr.sun_path, "IA_somethin or other");
#else
    strcat(addr.sun_path, ".IAID_SERVICE.");

    gethostname(addr.sun_path + strlen(addr.sun_path),
		sizeof(addr.sun_path) - strlen(addr.sun_path));
#endif
    if (0!=connect(rval.desc, (struct sockaddr *)&addr, sizeof(addr))) {
	perror("Error connecting to IDM");
	rval.close_desc();
	return rval;
    }
#ifdef USE_FP
    rval.fp = fdopen(rval.desc, "r+");
#endif

    PacketBody temp;
    temp.new_client.window=0;
    temp.new_client.ptype=NewClientRequest;
    strcpy(temp.new_client.label, cname);

#if 0
    rval.write_packet(NewClientRequest, &temp, 0x6c);
#else
    rval.write_packet(NewClientRequest, &temp.new_client,
		      sizeof(temp.new_client));
#endif
#ifdef USE_FP
    fflush(rval.fp);
#endif

    PacketBody	*reply;
    unsigned	rlen;
    reply = (PacketBody*)expect_packet(rval, NewClientReturn, &rlen);

    if (!reply) {
	rval.close_desc();
	return rval;
    }

    rval.id = reply->new_client_ret.id;

    delete (char*)reply;

    return rval;
}

IDP::LinkID IDP::CreateDisplay(ClientID &id, const char *devname, Access access)
{
    PacketBody	xmit;
    xmit.create_display.window = 0;
    xmit.create_display.ptype = CreateDisplayRequest;
    xmit.create_display.client = id.id;
    strcpy(xmit.create_display.devname, devname);
    xmit.create_display.access = access;
    id.write_packet(CreateDisplayRequest, &xmit.create_display,
		    sizeof(xmit.create_display));

    PacketBody	*reply;
    unsigned	rlen;
    reply = (PacketBody*)expect_packet(id, CreateDisplayReturn, &rlen);

    LinkID	rval;

    if (!reply) {
	id.close_desc();
	return rval;
    }

    rval.id = reply->any.window;
    rval.master = &id;

    delete (char*)reply;

    return rval;
}

IDP::LinkID IDP::PickDisplay(ClientID &id, const char *devname, Access access)
{
    PacketBody	xmit;
    xmit.create_display.window = 0;
    xmit.create_display.ptype = PickDisplayRequest;
    xmit.create_display.client = id.id;
    strcpy(xmit.create_display.devname, devname);
    xmit.create_display.access = access;
    id.write_packet(PickDisplayRequest, &xmit.create_display,
		    sizeof(xmit.create_display));
#ifdef USE_FP
    fflush(id.fp);
#endif

    PacketBody	*reply;
    unsigned	rlen;
    reply = (PacketBody*)expect_packet(id, PickDisplayReturn, &rlen);

    LinkID	rval;

    if (!reply) {
	id.close_desc();
	return rval;
    }

    rval.id = reply->any.window;
    rval.master = &id;

    delete (char*)reply;

    return rval;
}

IDP::LinkID IDP::WriteImage(LinkID id, const char*label, ImageType type,
			    unsigned elsize, IA_Array<unsigned> dimensions,
			    const void *values)
{
    PacketBody	xmit;
    xmit.write_image.window = id.id;
    xmit.write_image.ptype = WriteImageRequest;
    strcpy(xmit.write_image.label,label);
    xmit.write_image.type = type;
    xmit.write_image.elt_size = elsize;

    unsigned count=1;
    for (unsigned i=0; i<maxdims && i<dimensions.len(); i++) {
	xmit.write_image.lengths[i] = dimensions[i];
	count *=  dimensions[i];
    }
    xmit.write_image.ndims = i;

    id.master->write_packet(WriteImageRequest, &xmit.write_image,
			    sizeof(xmit.write_image));

    id.master->write_bytes(values, elsize*count);
    static unsigned char	kludge[] = {0, 0, 0, 0, 0};
#ifdef USE_FP
    fflush(id.master->fp);
#endif

    PacketBody	*reply;
    unsigned	rlen;
    reply = (PacketBody*)expect_packet(*id.master, WriteImageReturn, &rlen);

    if (!reply) {
	id.master->close_desc();
	return id;
    }

    delete (char*)reply;

    return id;
}

void * IDP::ReadImage(LinkID id, int UseROI, ImageType *type,
		      unsigned *elsize, IA_Array<unsigned> *dimensions)
{
    PacketBody	xmit;
    xmit.read_image.window = id.id;
    xmit.read_image.ptype = ReadImageRequest;
    xmit.read_image.roi = UseROI;

    id.master->write_packet(ReadImageRequest, &xmit.read_image,
			    sizeof(xmit.read_image));
#ifdef USE_FP
    fflush(id.master->fp);
#endif
    
    PacketBody	*reply;
    unsigned	rlen;
    reply = (PacketBody*)expect_packet(*id.master, ReadImageReturn, &rlen);
    *type = reply->read_image_ret.type;
    *elsize = reply->read_image_ret.elt_size;
    dimensions->resize(reply->read_image_ret.ndims);
    unsigned size=1;
    for (unsigned i=0; i<reply->read_image_ret.ndims; i++) {
	(*dimensions)[i] = reply->read_image_ret.lengths[i];
	size *= (*dimensions)[i];
    }

    void *rval = new char[size* *elsize];
    id.master->read_bytes(rval, size* *elsize);
    return rval;
}
