/* @(#)xilvc.c	1.29 94/05/13  */

/*
 * This example receives compressed video over the local network and displays
 * the decompressed images in a Xwindow.
 */


#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>

#include "packet.h"
#include "params.h"
#include "window_state.h"
#include "receive.h"
#include "cellb_util.h"
#include "blank261.h"

extern void create_big_h261_packet(BigPacketType * big, PacketType * packetlist[]);

int 
packetnum(unsigned short flag)
{
    int count = 0;

    while ((flag & 0x1) == 1) {
	count++;
	flag >>= 1;
    }
    return count;
}

void
create_big_packet(BigPacketType * big, PacketType * packetlist[])
{
    int i;
    unsigned char *curloc;
    int totsize = 0;
    int size;

    curloc = big->data;
    big->header = packetlist[0]->header;
    for (i = 0; i < MAXPACKETS; i++) {
	if (packetlist[i]) {
	    size = packetlist[i]->header.size;
	    memcpy(curloc, packetlist[i]->data, size);
	    totsize += size;
	    curloc += size;
	}
    }
    big->header.size = totsize;
}

add_skip_codes(unsigned char ** curloc, int *totsize, PacketType * lastpacket,
	       int x, int y, int newx, int newy, int width)
{
    int skipped_cells;
    int size = lastpacket->header.size;

    x += 4 * num_cells(lastpacket->data, &size);
    y += (x / width) * 4;
    x = x % width;
    skipped_cells = (((newy - y) * width) >> 4) + ((newx - x) >> 2);
    size = add_skip(*curloc, skipped_cells);
    *totsize += size;
    *curloc += size;
}


/*
 * similar to create_big_packet, but actually fills in skip codes for missing
 * data.  return TRUE if fixed, FALSE if unable to fix.
 */
int
fix_big_packet(BigPacketType * big, PacketType * packetlist[])
{
    int i = 0;
    unsigned char *curloc;
    int totsize = 0;
    int size;
    int x = 0;
    int y = 0;
    int width, height;
    int finalpacket = 0;
    int prevpacket = -1;

    curloc = big->data;
    if (!packetlist[0])		/* can't handle missing first packet */
	return FALSE;
    big->header = packetlist[0]->header;
    width = big->header.x;
    height = big->header.y;
    while ((!finalpacket) && (i < MAXPACKETS)) {
	if (packetlist[i]) {
	    if (prevpacket != i - 1) {	/* missed a packet */
		add_skip_codes(&curloc, &totsize, packetlist[prevpacket], x, y,
			   packetlist[i]->header.x, packetlist[i]->header.y,
			       width);
	    }
	    if (i != 0) {
		x = packetlist[i]->header.x;
		y = packetlist[i]->header.y;
	    }
	    size = packetlist[i]->header.size;
	    memcpy(curloc, packetlist[i]->data, size);
	    totsize += size;
	    curloc += size;
	    prevpacket = i;
	    finalpacket = !(packetlist[i]->header.flags & 0x8000);
	}
	i++;
    }
    if (!finalpacket) {
	add_skip_codes(&curloc, &totsize, packetlist[prevpacket], x, y,
		       width, height, width);
    }
    big->header.size = totsize;
    return TRUE;
}


main(int argc, char *argv[])
{
    Parameters *params;
    ReceiveState *receiver;
    WindowState *window;
    PacketType packets[MAXPACKETS];	/* circular buffer of rec'vd packets */
    PacketType *packetlist[MAXPACKETS];	/* packets for this frame */
    PacketType *curpacket;
    BigPacketType bigpacket;
    int packetcnt = 0;
    int currentframe;
    unsigned int currentsession;
    int lastw, lasth;
    int i;
    unsigned short myflags;
    XilSystemState state;
    XilCis cis;
    
    long display_frames = 0;

    /*
     * .... initialize everything ....
     */
    params = initialize_parameters(argc, argv);
    receiver = initialize_receive(params);
    state = xil_open();
    if (state == NULL) {
	fprintf(stderr, "unable to open xil library\n");
	exit(-1);
    }
    cis = xil_cis_create(state, params->cis_type);

    /*
     * If CellB or UYVY, set attributes since width & height are
     * "out-of-band" info
     */
    if (params->do_cis == CELLB || params->do_cis == UYVY) {
	xil_cis_set_attribute(cis, "WIDTH", (void *) params->width);
	xil_cis_set_attribute(cis, "HEIGHT", (void *) params->height);
    }

    if (params->do_cis == H261) 
      xil_cis_set_max_frames(cis, 4);
    else
      xil_cis_set_max_frames(cis, 1);    


    if ((window = initialize_window(params, state)) == NULL) {
	fprintf(stderr, "ERROR: Unable to create window. ");
	xil_close(state);
	exit(-1);
    }

    /*
     * .... receive video ....
     */
    currentframe = 0;
    currentsession = 0;
    lastw = params->width;
    lasth = params->height;

    /*
     * H261 CIF is defined as 352x288 not 320x240. We set the width
     * and height here.
     */
    if (params->do_cis == H261) {
      lastw = 352;
      lasth = 288;
    }
    
    /*
     * If JPEG, compress and decompress a dummy frame to load all the
     * necessary tables for the decompressor.  This allows us to decompress
     * received frames created with COMPRESSED_DATA_FORMAT set to
     * ABBREVIATED_FORMAT even if we miss the first frame. NOTE: this
     * technique assumes that the compressed bitstream was created using the
     * default JPEG CIS attributes.
     */
    if (params->do_cis == JPEG) {
	XilImage dummy = xil_create(state, lastw, lasth, 3, XIL_BYTE);

	xil_compress(dummy, cis);
	xil_decompress(cis, dummy);
	xil_sync(dummy);
	xil_destroy(dummy);
    }

    /*
     * Because of the non-byte aligned nature of H261 and because
     * the XIL H261 CIS can not tolerate zero stuffing, the first
     * frame added to an H261 CIS must begin with a perfect PSC.
     * Below we add a dummy blank (a black H261 frame) that starts
     * with a perfect PSC. Since the XIL H261 CIS needs possibly at
     * least two frames in order to decompress a given frame, we
     * add the blank frame twice.
     */
     
    if (params->do_cis == H261) {
      xil_cis_put_bits(cis, sizeof(blank_261_frame_cif), 0, blank_261_frame_cif);
    }
    
    myflags = 0xffff;
    for (i = 0; i < MAXPACKETS; i++)
	packetlist[i] = NULL;

    while (display_frames < params->frames) {

	curpacket = &packets[packetcnt++];
	if (packetcnt >= MAXPACKETS)
	    packetcnt = 0;

	/*
	 * Get video packet if available.  It no video then the window state
	 * is passed in so we can at least check for X events.  (If the user
	 * pulled down a `quit' menu.)
	 */
	receive_packet(window, receiver, curpacket);

	if (currentsession != curpacket->header.session) {

	    /*
	     * if you join a conference in progress then the current frame
	     * and session will be grabbed from the first received packet
	     * received
	     */
	    if (verbose) {
		fprintf(stderr, "changing session from %d to %d\n",
			currentsession, curpacket->header.session);
	    }
	    for (i = 0; i < MAXPACKETS; i++)
		packetlist[i] = NULL;
	    myflags = 0xffff;
	    currentframe = curpacket->header.frame;
	    currentsession = curpacket->header.session;
	}

	/* if this is the first packet of a frame, check for a resize */
	if ((curpacket->header.flags & 1) == 0) {
          /*
           * the size of each frame is sent on the first packet of each
           * frame
           */
          if ((curpacket->header.x != lastw) ||
              (curpacket->header.y != lasth)) {
            
            lastw = curpacket->header.x;
            lasth = curpacket->header.y;

            if (verbose)
              printf("resizing video %dx%d\n",lastw, lasth);
            
            resize(window, lastw, lasth);
            xil_cis_reset(cis);
            
            if (params->do_cis == CELLB || params->do_cis == UYVY) {
              xil_cis_set_attribute(cis, "WIDTH", (void *) lastw);
              xil_cis_set_attribute(cis, "HEIGHT", (void *) lasth);
            }
            /* see comments above about dummy compress/decompress */
            if (params->do_cis == JPEG) {
              XilImage dummy = xil_create(state, lastw, lasth, 3,
                                          XIL_BYTE);
              
              xil_compress(dummy, cis);
              xil_decompress(cis, dummy);
              xil_sync(dummy);
              xil_destroy(dummy);
            }
            
            /* see comments above */
            if (params->do_cis == H261) {
              if (lastw == 352)
                xil_cis_put_bits(cis, sizeof(blank_261_frame_cif), 0, blank_261_frame_cif);
              else
                xil_cis_put_bits(cis, sizeof(blank_261_frame_qcif), 0, blank_261_frame_qcif);                  
            }
            if (params->do_cis == H261)
              xil_cis_set_max_frames(cis, 4);
            else
              xil_cis_set_max_frames(cis, 1);
          }
        }

	if (curpacket->header.frame < currentframe) {
	    /*
	     * if this packet is from an earlier frame disregard it
	     */
	    if (verbose)
		fprintf(stderr, "received stale packet %d, current frame %d\n",
			curpacket->header.frame, currentframe);
	    continue;
	}
	if (curpacket->header.frame > currentframe) {

	    /*
	     * If the frame number increased then we probably missed the last
	     * packet of the previous frame. We display what we can of the
	     * previous frame if '-b' option was selected, otherwise we drop
	     * the partial frame.
	     */
	    if (myflags != 0) {
		if (params->cellb_xy_addr) {
		    if (fix_big_packet(&bigpacket, packetlist)) {
			xil_cis_put_bits_ptr(cis, bigpacket.header.size, 1,
					     bigpacket.data, NULL);
			display_frames++;
			display(window, cis);
		    }
		}
		else {
                  if (verbose)
                    fprintf(stderr, "dropping %d due to getting packet %d (flags %x)\n",
                          currentframe, curpacket->header.frame, myflags);
		}
	    }

	    for (i = 0; i < MAXPACKETS; i++)
		packetlist[i] = NULL;
	    myflags = 0xffff;
	    currentframe = curpacket->header.frame;
	}

	/*
	 * save the packet for decoding
	 */
        
	packetlist[packetnum(curpacket->header.flags)] = curpacket;
	myflags &= curpacket->header.flags;

	if (myflags == 0) {
          if (params->do_cis == H261){
            /*
             * H261 is very sensitive to packet lost. This routine fixes
             * any problems with a lost packet
             */
            create_big_h261_packet(&bigpacket, packetlist);
          } else
            create_big_packet(&bigpacket, packetlist);

          xil_cis_put_bits(cis, bigpacket.header.size, 0, bigpacket.data); 

          display_frames++;
          if (params->do_cis == H261){
            /*
             * we could check to make sure there is at least two frames 
             * in the XIL H261 CIS at this point but since we've added
             * a dummy blank frame to start with and we only call
             * display after we've added a frame, we don't need to check.
             */
            display(window, cis);
          } else 
            display(window, cis);
	}
    }
    
    /*
     * .... close everything ....
     */
    close_window(window);
    close_receive(receiver);
    close_parameters(params);

    xil_close(state);


    return 0;
}
