/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
#include <stdio.h>
#include <util.h>
#include <malloc.h>
#include <varargs.h>
#include "rast_img.h"

/* Writes a string to a socket */
static
write_string(sock,s)
	char *s ;
{
	long l = strlen(s) ;
	if (write_long(sock,l) < 0)
	{
		return -1 ;
	}
	return (must_write(sock,s,(int)l) != l) ? -1 : 0 ;
}

/* Reads a string from a socket */
static
read_string(sock,buf,mb)
	char *buf ;
{
	long nbytes ;
	int l ;
	if (read_long(sock,&nbytes) < 0)
	{
		return -1 ;
	}
	l = nbytes ;

	/* If more than can be handled then truncate */
	if (l > mb - 1)
	{
		l = mb - 1 ;
	}

	/* Subtract amount we are going to read from amount that was sent */
	nbytes -= l ;

	/* Read in the buffer */
	if (must_read(sock,buf,(int)l) != l)
	{
		return -1 ;
	}

	/* NULL terminate */
	buf[l] = 0 ;

	if (read_fluff(sock,(int)nbytes) < 0)
	{
		return -1 ;
	}

	return 0 ;
}

/*
 *	Writes a long array toa socket.
 */
static
write_long_ll(sock,ll)
	LongLinkedList *ll ;
{
	LongLinkNode i = ll->first ;
	if (write_long(sock,(long)ll->n) < 0)
	{
		return -1 ;
	}
	while(i != NULL)
	{
		if (write_long(sock,i->l) < 0)
		{
			return -1 ;
		}
		i = i->next ;
	}
	return 0 ;
}

/*
 *	Reads a long array from a socket.
 */
static
read_long_array(sock,l,n)
	long **l ;
	int *n ;
{
	long size ;
	long *array ;
	int i ;

	/* Get size and validate it */
	if (read_long(sock,&size) < 0)
	{
		return -1 ;
	}
	if (size < 0)
	{
		return -1 ;
	}

	/* Allocate the array and read it in */
	array = (long *)malloc((unsigned)(size * sizeof(long))) ;
	for(i = 0; i < size; i++)
	{
		if (read_long(sock,&(array[i])) < 0)
		{
			free((char *)array) ;
			return -1 ;
		}
	}

	/* Fill in info */
	*n = size ;
	*l = array ;
	return 0 ;
}

/* Chomps away at unwanted bytes */
read_fluff(sock,nbytes)
{
	/* Read in any leftovers as efficiently as possible */
	static char dum[512] ;
	int size = sizeof dum ;
	while(size && nbytes)
	{
		while(nbytes >= size)
		{
			if (must_read(sock,dum,size) != size)
			{
				return -1 ;
			}
			nbytes -= size ;
		}
		size /= 2 ;
	}
	return 0 ;
}

/*VARARGS*/
read_f(sock,fmt,va_alist)
	char *fmt ;
	va_dcl
{
	va_list pvar ;
	va_start(pvar) ;
	while(*fmt)
	{
		switch(*fmt++)
		{
			case 'a' :
			{
				long **l = va_arg(pvar,long **) ;
				int *n = va_arg(pvar,int *) ;
				if (read_long_array(sock,l,n) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			case 's' :
			{
				char *s = va_arg(pvar,char *) ;
				int ls = va_arg(pvar,long) ;
				if (read_string(sock,s,ls) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			case 'l' :
			{
				long *l = va_arg(pvar,long *) ;
				if (read_long(sock,l) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			case 'z' :
			{
				Rast_Img *img = va_arg(pvar,Rast_Img *) ;
				if (read_img(sock,img) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			default :
				abort() ;
		}
	}
	va_end(pvar) ;
	return 0 ;
}

/*VARARGS*/
write_f(sock,fmt,va_alist)
	char *fmt ;
	va_dcl
{
	va_list pvar ;
	va_start(pvar) ;
	while(*fmt)
	{
		switch(*fmt++)
		{
			case 'a' :
			{
				LongLinkedList *ll = va_arg(pvar,LongLinkedList *) ;
				if (write_long_ll(sock,ll) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			case 's' :
			{
				char *s = va_arg(pvar,char *) ;
				if (write_string(sock,s) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			case 'l' :
			{
				long l = va_arg(pvar,long) ;
				if (write_long(sock,l) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			case 'z' :
			{
				Rast_Img *img = va_arg(pvar,Rast_Img *) ;
				if (write_img(sock,img) < 0)
				{
					va_end(pvar) ;
					return -1 ;
				}
				break ;
			}
			default :
				abort() ;
		}
	}
	va_end(pvar) ;
	return 0 ;
}

must_read(sock,buf,bc)
    char *buf ;
{
    char *sbuf = buf ;
    while(bc > 0)
    {
        int real_bc = read(sock,buf,bc) ;
        if (real_bc < 0)
        {
            return real_bc ;
        }
		if (real_bc == 0)
		{
			break ;
		}
        buf += real_bc ;
        bc -= real_bc ;
    }
    return buf - sbuf ;
}

must_write(sock,buf,bc)
    char *buf ;
{
    char *sbuf = buf ;
    while(bc > 0)
    {
        int real_bc = write(sock,buf,bc) ;
        if (real_bc < 0)
        {
            return real_bc ;
        }
		if (real_bc == 0)
		{
			break ;
		}
        buf += real_bc ;
        bc -= real_bc ;
    }
    return buf - sbuf ;
}

convert_bl(b)
	unsigned char *b ;
{
#ifdef NONIEEE
	/* If first bit set then number is negative */
	int i ;
	int neg = b[0] & 0x80 ;
	long l = 0 ;
	
	/* Put in the bytes */
	for(i = 0; i < sizeof(long); i++)
	{
		int j ;

		/* Get a byte and invert if negative */
		unsigned char tmp = neg ? (~b[i]) : (b[i]) ;

		for(j = 7; j >= 0; j--)
		{
			l *= 2 ;
			l += (tmp >> j) & 1 ;
		}
	}

	/* If negative then negate */
	*((long *)b) = neg ? (-1 - l) : l ;
#endif
}

/*
 *		Reads a long from a socket.
 */
read_long(sock,val)
	long *val ;
{
	long l ;

	/* Read in length of string, any errors just punt */
	if (must_read(sock,(char *)&l,sizeof(long)) != sizeof(long))
	{
		return -1 ;
	}

	convert_bl((unsigned char *)&l) ;
	*val = l ;
	return 0 ;
}

convert_lb(b)
	unsigned char *b ;
{
#ifdef NONIEEE
	long l = *((long *)b) ;
	int neg = 0 ;
	int i ;

	/* If negative then negate the value */
	if (l < 0)
	{
		neg = !neg ;
		l = -1 - l ;
	}

	/* Copy in the bits */
	for(i = sizeof(long) - 1; i >= 0; i--)
	{
		int j ;
		unsigned char tmp = 0 ;
		for(j = 0; j < 8; j++)
		{
			tmp |= (l % 2) << j ;
			l /= 2 ;
		}

		/* Invert if negative */
		b[i] = neg ? (~tmp) : tmp ;
	}
#endif
}

/* Write a long to a socket */
write_long(sock,l)
	long l ;
{
	convert_lb((unsigned char *)&l) ;
	return (must_write(sock,(char *)&l,sizeof(long)) == sizeof(long)) ? 0 : -1 ;
}

static
read_img(sock,img)
        Rast_Img *img ;
{
		long width ;
		long height ;
		long depth ;
		long map_type ;
        int size ;
        if (read_long(sock,&width) < 0)
        {
                return -1 ;
        }
        if (read_long(sock,&height) < 0)
        {
                return -1 ;
        }
        if (read_long(sock,&depth) < 0)
        {
                return -1 ;
        }
        if (read_long(sock,&map_type) < 0)
        {
                return -1 ;
        }
        size = (((width * depth + 15) / 16) * 2) * height ;
        img->height = height ;
        img->width = width ;
        img->depth = depth ;
        img->cmap_type = map_type ;
        img->data = (unsigned char *)malloc(size) ;
        if (map_type == CMAP_RGB)
        {
                int map_size ;
                int map_s ;
                if (read_long(sock,&map_size) < 0)
                {
                        return -1 ;
                }
                map_s = map_size * sizeof(struct rgb) ;
                img->cmap_data.rgb.map_size = map_size ;
                img->cmap_data.rgb.map = (struct rgb *)malloc(map_s) ;
                if (must_read(sock,img->cmap_data.rgb.map,map_s) != map_s)
                {
                        return -1 ;
                }
        }
        if (must_read(sock,img->data,size) != size)
        {
                return -1 ;
        }
        return 0 ;
}

static
write_img(sock,img)
        Rast_Img *img ;
{
        int size ;
        if (write_long(sock,img->width) < 0)
        {
                return -1 ;
        }
        if (write_long(sock,img->height) < 0)
        {
                return -1 ;
        }
        if (write_long(sock,img->depth) < 0)
        {
                return -1 ;
        }
        if (write_long(sock,img->cmap_type) < 0)
        {
                return -1 ;
        }
        size = (((img->width * img->depth + 15) / 16) * 2) * img->height ;
        if (img->cmap_type == CMAP_RGB)
        {
                int map_s ;
                if (write_long(sock,img->cmap_data.rgb.map_size) < 0)
                {
                        return -1 ;
                }
                map_s = img->cmap_data.rgb.map_size * sizeof(struct rgb) ;
                if (must_write(sock,img->cmap_data.rgb.map,map_s) != map_s)
                {
                        return -1 ;
                }
        }
        if (must_write(sock,img->data,size) != size)
        {
                return -1 ;
        }
        return 0 ;
}
