/* @(#)xilbroadcast.c	1.67 94/05/18  */

/*
 * This example reads an image from MMACo1k, transmits compressed video over
 * the local network, and optionally displays the image in a Xwindow.
 */

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

#include "params.h"
#include "packet.h"
#include "transmit.h"
#include "window_state.h"
#include "cellb_util.h"


void
transmit_data(Xil_unsigned8 * data, int nbytes, int width, int height,
	      TransmitState * tstate, Parameters * param)
{
    PacketType packet;
    static int frame = 0;
    int count = 0;

    packet.header.frame = frame++;
    packet.header.session = tstate->session | (VIDEO_PACKET << 28);
    packet.header.x = width;
    packet.header.y = height;
    if (nbytes > PACKETDATASIZE * MAXPACKETS) {
	if (verbose)
	    fprintf(stderr, "Not sending frame %d due to size (%d) > maximum\n",
		    frame, nbytes);
    }
    else {
	int x = 0;
	int y = 0;

	while (nbytes > 0) {
	    int curbytes = 0;
	    int cellwidth = width / 4;

	    if (nbytes <= PACKETDATASIZE) {
		curbytes = nbytes;
		packet.header.flags = ((1 << count) - 1);
		if (count) {
		    packet.header.x = x * 4;
		    packet.header.y = y * 4;
		}
	    }
	    else {
		curbytes = PACKETDATASIZE;
		if (param->cellb_xy_addr) {
		    if (count) {
			packet.header.x = x * 4;
			packet.header.y = y * 4;
		    }
		    x += num_cells(data, &curbytes);
		    y += x / cellwidth;
		    x = x % cellwidth;
		}
		packet.header.flags = ~(1 << count);
	    }
	    packet.header.size = curbytes;
	    count++;
	    nbytes -= curbytes;
	    memcpy(packet.data, data, curbytes);
	    data += curbytes;
	    transmit_packet(tstate, &packet);
	    if (param->polling)
		poll(0, 0, param->polling);
	}
    }
}


main(int argc, char **argv)
{
    int scalex = 1, scaley = 1;
    XilSystemState state;
    XilImage image, o1k_image;
    XilDataType datatype;
    XilImage scaled_image;
    XilCis cis;
    Xil_unsigned8 *data;

    Parameters *param;
    TransmitState *tstate;
    WindowState *wstate;

    unsigned int width, height, nbands;
    int frames;
    double t;
    struct timeval t0, t1;
    int nframes = 0;
    int nbytes;
    int multiple_frames = 0;
    const char *video_format;
    int bytes_for_first_frame;
    FILE *df;

    param = initialize_parameters(argc, argv);

    if (param->transmit)
	tstate = initialize_transmit(param);

    state = xil_open();
    if (state == NULL) {
	fprintf(stderr, "unable to open xil library\n");
	exit(1);
    }


    {
	XilDevice device;

	if (!(device = xil_device_create(state, "MMACo1k"))) {
	    fprintf(stderr, "Unable to create a device object\n");
	    xil_close(state);
	    exit(1);
	}
	xil_device_set_value(device, "DEVICE_NAME", (void *) param->devname);
	if (!(image = xil_create_from_device(state, "MMACo1k", device))) {
	    fprintf(stderr, "ERROR: failed to open MMACo1k device\n");
	    xil_close(state);
	    exit(1);
	}
	xil_device_destroy(device);
    }
    o1k_image = image;

    xil_set_device_attribute(image, "PORT_V", (void *) param->whichinput);

    {
	int format;

	xil_get_device_attribute(o1k_image, "FORMAT_V", (void **) &format);
	if (format == 0) {
	    fprintf(stderr, "Unknown video format, exiting.\n");
	    xil_close(state);
	    exit(1);
	}
    }

    xil_set_device_attribute(image, "IMAGE_SKIP", (void *) param->image_skip);
    xil_set_device_attribute(image, "MAX_BUFFERS", (void *) param->max_buffer);
    xil_get_device_attribute(image, "FORMAT", (void **) &video_format);

    xil_get_info(image, &width, &height, &nbands, &datatype);

    /*
     * If the user has not changed the default to something else, reset the
     * default capture width and height
     */
    if (param->width == -1)
	param->width = width;
    if (param->height == -1)
	param->height = height;

    /* check for width or height too big */
    if ((param->width > width) || (param->height > height)) {
	fprintf(stderr, "ERROR: Maximum width & height are %d & %d\n",
		width, height);
	xil_close(state);
	exit(1);
    }

    /*
     * if user specifies '-s 0' for no scaling, set local 'scalefactor' to
     * 1.0
     */
    scalex = (param->scalex ? param->scalex : 1);
    scaley = (param->scaley ? param->scaley : 1);

    /* CellB compression will produce 0.25 bytes/pixel for the first frame */
    bytes_for_first_frame = param->width * param->height / (scalex * scaley * 4);

    if (param->transmit &&
	    (bytes_for_first_frame > PACKETDATASIZE * MAXPACKETS)) {
	fprintf(stderr, " Warning: Max number of packets can be exceeded \n");
	fprintf(stderr, "   given this size frame. No packets will be trans-\n");
	fprintf(stderr, "   mitted on any frame in which the maximum number \n");
	fprintf(stderr, "   of packets is exceeded.\n\n");
	fprintf(stderr, "   You can increase -s or reduce -w or -h\n");
    }

    if (param->do_cis == CELLB) {

	/*
	 * CellB requires that the dimensions of the image used with
	 * xil_compress each be a multiple of 4
	 */
	if ((int) (((float) param->width / (float) scalex) + 0.5) % 4) {
	    param->width = ((param->width / scalex) & ~3) * scalex;
	    fprintf(stderr, "capture width adjusted to %d\n", param->width);
	}

	if ((int) (((float) param->height / (float) scaley) + 0.5) % 4) {
	    param->height = ((param->height / scaley) & ~3) * scaley;
	    fprintf(stderr, "capture height adjusted to %d\n", param->height);
	}
    }

    if (param->do_cis == UYVY) {

	/*
	 * UYVY requires that the width of the image used with xil_compress
	 * each be a multiple of 2
	 */
	if ((int) (((float) param->width / (float) scalex) + 0.5) % 2) {
	    param->width = ((param->width / scalex) & ~1) * scalex;
	    fprintf(stderr, "capture width adjusted to %d\n", param->width);
	}

    }

    if ((param->width < width) || (param->height < height)) {
	image = xil_create_child(o1k_image,
				 (width - param->width) / 2,
				 (height - param->height) / 2,
				 param->width, param->height,
				 0, nbands);
	if (image == NULL) {
	    fprintf(stderr, "ERROR: failed to open child image on MMACo1k\n");
	    xil_close(state);
	    exit(1);
	}
	xil_get_info(image, &width, &height, &nbands, &datatype);
    }

    if (scalex || scaley) {
	width = (int) ((float) width / (float) scalex + 0.5);
	height = (int) ((float) height / (float) scaley + 0.5);
	scaled_image = xil_create(state, width, height, nbands, XIL_BYTE);
    }

    if (param->preview)
	wstate = initialize_window(param, state);

    cis = xil_cis_create(state, param->cis_type);

    /*
     * set the buffers to make sure that you don't keep information you do
     * not need.
     */
    xil_cis_set_keep_frames(cis, 1);
    xil_cis_set_max_frames(cis, 1);

    /*
     * We need to be able to seek backwards to both get-bits, and decompress
     */
    if ((param->do_cis == JPEG) || (param->do_cis == CELLB))
	xil_cis_set_attribute(cis, "IGNORE_HISTORY", (void *) TRUE);

    if (param->do_cis == JPEG) {
	/* this attribute must be TRUE for acceleration */
	xil_cis_set_attribute(cis, "ENCODE_411_INTERLEAVED", (void *) TRUE);
    }

    if (param->datafile)
	df = fopen(param->datafile, "w");
    else
	df = (FILE *) NULL;

    for (frames = 0; frames < param->frames; frames++) {
	/* check for X events like a `quit' from the pulldown menu */
	if (param->preview)
	    check_events(wstate);


	if (scalex || scaley) {
	    xil_scale(image, scaled_image, "nearest",
		      1.0 / (float) scalex, 1.0 / (float) scaley);
	}
	else {
	    scaled_image = image;
	}

	xil_compress(scaled_image, cis);
	xil_cis_sync(cis);
	xil_toss(scaled_image);


	/* don't include overhead of first frame in frame rate calculation */
	if (frames == 0)
	    gettimeofday(&t0, NULL);

	if (xil_cis_has_frame(cis)) {

            /* 
	     * If we're not in the midst of handling multiple frames, retrieve
	     * as many as we can via xil_cis_get_bits_ptr().
	     */

	    if (!multiple_frames) {
		data = (void *) xil_cis_get_bits_ptr(cis, &nbytes, &nframes);

		if (df)
		    fwrite(data, 1, nbytes, df);

		if (param->transmit)
		    transmit_data(data, nbytes, width, height, tstate, param);
	    }


	    if (param->preview) {

		/*
		 * xil_cis_get_bits() moved the CIS read frame forward by
		 * nframes. To decompress these, seek back nframes. If we're
		 * in the midst of handling multiple frames, no seek is
		 * needed.
		 */
		if (!multiple_frames)
		    xil_cis_seek(cis, -nframes, 1);
		display(wstate, cis);
	    }
	}

	if (nframes > 0) {

	    /*
	     * If we're in the midst of handling multiple frames, decrement
	     * the count.  Else, set multiple_frames if nframes is more than
	     * 1.
	     */
	    if (multiple_frames)
		multiple_frames--;
	    else
		multiple_frames = nframes - 1;
	}

    }

    gettimeofday(&t1, NULL);

    t = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) * 1e-6;
    printf("%d frames in %g seconds, %g frames/second\n",
	   frames - 1, t, (frames - 1) / t);

    /*
     * cleanup
     */
    if (df) {
	/* flush any oustanding compresses */
	xil_cis_flush(cis);
	while (xil_cis_has_frame(cis)) {
	    data = (void *) xil_cis_get_bits_ptr(cis, &nbytes, &nframes);
	    fwrite(data, 1, nbytes, df);
	}

	fclose(df);
    }

    if (param->transmit)
	close_transmit(tstate);

    if (param->preview)
	close_window(wstate);

    xil_destroy(o1k_image);
    xil_cis_destroy(cis);

    /*
     * shutdown
     */
    xil_close(state);

    return 0;
}
