/*------------------------------------------------------------------------
 *
 * Copyright (c) 1997-1998 by Cornell University.
 * 
 * See the file "license.txt" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *------------------------------------------------------------------------
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <dvmbasic.h>
#include <dvmmpeg.h>
#include <dvmcolor.h>
#include <dvmavi.h>


void swap (ByteImage **x, ByteImage **y)
{
    ByteImage *temp;
    temp = *x;
    *x = *y;
    *y = temp;
}


/*
*---------------------------------------------------------------
* This proc make sure that there are at least size bytes of 
* data in the bitstream bs, which is attached to bitparser bp.  
* If there is not enough data, fill up the bitstream by reading 
* from tcl channel chan.
*---------------------------------------------------------------
*/
void CheckBitStreamUnderflow (bs, bp, chan, size)
    BitStream *bs;
    BitParser *bp;
    FILE *chan;
    int size;
{
    int off  = BitParserTell(bp);
    int left = BitStreamBytesLeft(bs, off);
    if (left < size) {
        BitStreamShift (bs, off);
        BitStreamFileRead (bs, chan, left);
        BitParserSeek (bp, 0);
    }
}


int main(int argc, char *argv[])
{
    BitParser *bp = BitParserNew();
    BitStream *bs = BitStreamNew(65536);
    BitParser *outbp;
    BitStream *outbs;
    MpegSeqHdr *sh;
    MpegPicHdr *fh;
    ByteImage *r, *g, *b, *y, *u, *v, *br, *bg, *bb;
    ByteImage *prevy, *prevu, *prevv, *futurey, *futureu, *futurev;
    ScImage *scy, *scu, *scv;
    VectorImage *fwdmv, *bwdmv;
    AviFile *aviFile;
    AviStream *aviStream;
    FILE *file;
    int codec, play_order, bnum=0, togo=0;
    int currCode, len;
    int halfw, halfh, w, h, seqw, seqh, picSize, remw, remh, counter, type;

    if (argc < 4) {
	fprintf(stderr, "usage : %s input output.avi codec\n", argv[0]);
	exit(1);
    }
    file = fopen(argv[1], "rb");
    if (file == NULL) {
	fprintf(stderr, "unable to open %s for reading.\n", argv[1]);
	exit(1);
    }

    BitStreamFileRead(bs, file, 0);
    BitParserWrap(bp, bs);

    sh = MpegSeqHdrNew();
    MpegSeqHdrFind(bp);
    MpegSeqHdrParse(bp, sh);

    seqw = MpegSeqHdrGetWidth(sh);
    seqh = MpegSeqHdrGetHeight(sh);
    picSize = MpegSeqHdrGetBufferSize(sh);
    remw = seqw % 16;
    remh = seqh % 16;
    if (remw != 0) {
	w = seqw + 16 - remw;
    } else {
	w = seqw;
    }
    if (remh != 0) {
	h = seqh + 16 - remh;
    } else {
	h = seqh;
    }
    halfw = w/2;
    halfh = h/2;

    y       = ByteNew (w, h);
    prevy   = ByteNew (w, h);
    futurey = ByteNew (w, h);
    r       = ByteNew (seqw, seqh);
    g       = ByteNew (seqw, seqh);
    b       = ByteNew (seqw, seqh);
    br      = ByteNew (seqw, seqh);
    bg      = ByteNew (seqw, seqh);
    bb      = ByteNew (seqw, seqh);
    u       = ByteNew (halfw, halfh);
    prevu   = ByteNew (halfw, halfh);
    futureu = ByteNew (halfw, halfh);
    v       = ByteNew (halfw, halfh);
    prevv   = ByteNew (halfw, halfh);
    futurev = ByteNew (halfw, halfh);
    fwdmv   = VectorNew (w/16, h/16);
    bwdmv   = VectorNew (w/16, h/16);
    scy     = ScNew (w/8, h/8);
    scu     = ScNew (w/16, h/16);
    scv     = ScNew (w/16, h/16);
    fh      = MpegPicHdrNew();

    outbs = BitStreamNew(3*seqw*seqh + 20);
    outbp = BitParserNew();
    BitParserWrap(outbp, outbs);

    len = MpegPicHdrFind(bp);
    counter = 0;

    AVIFileInit();
    if (AviFileCreate(argv[2], &aviFile) != 0) {
	fprintf(stderr, "unable to create avifile %s \n", argv[2]);
	exit(1);
    }

    codec = mmioFOURCC(argv[3][0], argv[3][1], argv[3][2], argv[3][3]); 
    if (AviVideoStreamCreate (aviFile, codec, seqw, seqh,
                              15, 30, 75, 
                              50000, &aviStream) != 0) {
	fprintf(stderr, "unable to create avistream \n");
	exit(1);
    }

    while (1) {
	CheckBitStreamUnderflow(bs, bp, file, picSize);
	MpegPicHdrParse(bp, fh);
        play_order = MpegPicHdrGetTemporalRef(fh);
	type = MpegPicHdrGetType(fh);
	if (type == I_FRAME) {
	    swap(&futurey, &prevy);
	    swap(&futureu, &prevu);
	    swap(&futurev, &prevv);
	    MpegPicIParse(bp, sh, fh, scy, scu, scv);
	    ScIToByte(scy, y);
	    ScIToByte(scu, u);
	    ScIToByte(scv, v);
	    YuvToRgb420(y, u, v, r, g, b);
	    swap(&y, &futurey);
	    swap(&u, &futureu);
	    swap(&v, &futurev);
	} else if (type == P_FRAME) {
	    swap(&futurey, &prevy);
	    swap(&futureu, &prevu);
	    swap(&futurev, &prevv);
	    MpegPicPParse(bp, sh, fh, scy, scu, scv, fwdmv);
	    ScPToY(scy, fwdmv, prevy, y);
	    ScPToUV(scu, fwdmv, prevu, u);
	    ScPToUV(scv, fwdmv, prevv, v);
	    YuvToRgb420(y, u, v, r, g, b);
	    swap(&y, &futurey);
	    swap(&u, &futureu);
	    swap(&v, &futurev);
	} else {
	    MpegPicBParse(bp, sh, fh, scy, scu, scv, fwdmv, bwdmv);
	    ScBToY(scy, fwdmv, bwdmv, prevy, futurey, y);
	    ScBToUV(scu, fwdmv, bwdmv, prevu, futureu, u);
	    ScBToUV(scv, fwdmv, bwdmv, prevv, futurev, v);
	    YuvToRgb420(y, u, v, r, g, b);
	}
        /* Convert to display order */
        if (play_order == togo) {
            AviVideoFrameWrite(aviStream, r, g, b);
            togo++;
            if (bnum == togo) {
                AviVideoFrameWrite(aviStream, br, bg, bb);
                bnum=0;
                togo++;
            }
        } else if(play_order > togo) {
            swap(&r, &br);
            swap(&g, &bg);
            swap(&b, &bb);
            bnum = play_order;
        } else {
            togo = 0;
            if (play_order == togo) {
                AviVideoFrameWrite(aviStream, r, g, b);
                togo++;
            } else {
                swap(&r, &br);
                swap(&g, &bg);
                swap(&b, &bb);
                bnum = play_order;
            }
        }
                    
	MpegPicHdrFind(bp);
	currCode = MpegGetCurrStartCode(bp);
	if (currCode == SEQ_END_CODE) {
	    break;
	}
	counter++;
        printf("Frame %d\n", counter);
    }

    MpegPicHdrFree(fh);
    MpegSeqHdrFree(sh);
    BitStreamFree(bs);
    BitParserFree(bp);
    ByteFree(r);
    ByteFree(g);
    ByteFree(b);
    ByteFree(br);
    ByteFree(bg);
    ByteFree(bb);
    ByteFree(y);
    ByteFree(u);
    ByteFree(v);
    ByteFree(prevy);
    ByteFree(prevu);
    ByteFree(prevv);
    ByteFree(futurey);
    ByteFree(futureu);
    ByteFree(futurev);
    ScFree(scy);
    ScFree(scu);
    ScFree(scv);
    VectorFree(fwdmv);
    VectorFree(bwdmv);
    AviStreamClose(aviStream);
    AviFileClose(aviFile);

    return 0;
}