/*===========================================================================*
 * frametype.c								     *
 *									     *
 *	procedures to keep track of frame types (I, P, B)		     *
 *									     *
 * EXPORTED PROCEDURES:							     *
 *	FType_Type						             *
 *	FType_FutureRef						             *
 *	FType_PastRef						             *
 *									     *
 * SYNOPSIS								     *
 *	FType_Type	returns the type of the given numbered frame	     *
 *	FType_FutureRef	returns the number of the future reference frame     *
 *	FType_PastRef	returns the number of the past reference frame	     *
 *									     *
 *===========================================================================*/

/*
 * Copyright (c) 1993 The Regents of the University of California.
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */


/*==============*
 * HEADER FILES *
 *==============*/

#include "all.h"
#include "prototypes.h"
#include "frames.h"
#include "frame.h"
#include "param.h"


static FrameTable *frameTable;


/*==================*
 * GLOBAL VARIABLES *
 *==================*/

boolean	    forceEncodeLast = FALSE;
extern int framePatternLen;
extern char *framePattern;


/*===============================*
 * INTERNAL PROCEDURE prototypes *
 *===============================*/

static void	ComputeFrameTable _ANSI_ARGS_((void));


/*=====================*
 * EXPORTED PROCEDURES *
 *=====================*/

/*===========================================================================*
 *
 * FType_Type
 *
 *	returns the type of the given numbered frame
 *
 * RETURNS:	the type
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int
FType_Type(frameNum)
    int frameNum;
{
    if ( forceEncodeLast && (frameNum+1 == numInputFiles) ) {
	int result;

	result = framePattern[frameNum % framePatternLen];
	if ( result == 'b' ) {
	    return 'i';
	} else {
	    return result;
	}
    } else {
	return framePattern[frameNum % framePatternLen];
    }
}


/*===========================================================================*
 *
 * FType_FutureRef
 *
 *	returns the number of the future reference frame
 *
 * RETURNS:	the number; -1 if none
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int
FType_FutureRef(currFrameNum)
    int currFrameNum;
{
    int	    index;
    int	    futureIndex;
    int	    result;

    index = currFrameNum % framePatternLen;
    futureIndex = frameTable[index].next->number;

    result = currFrameNum +
	    (((futureIndex-index)+framePatternLen) % framePatternLen);

    if ( (result >= numInputFiles) && forceEncodeLast ) {
	return numInputFiles-1;
    } else {
	return result;
    }
}


/*===========================================================================*
 *
 * FType_PastRef
 *
 *	returns the number of the past reference frame
 *
 * RETURNS:	the number
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int
FType_PastRef(currFrameNum)
    int currFrameNum;
{
    int	    index;
    int	    pastIndex;

    index = currFrameNum % framePatternLen;
    pastIndex = frameTable[index].prev->number;

    return currFrameNum -
	   (((index-pastIndex)+framePatternLen) % framePatternLen);
}


/*===========================================================================*
 *
 * SetFramePattern
 *
 *	set the IPB pattern; calls ComputeFrameTable to set up table
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    framePattern, framePatternLen, frameTable
 *
 *===========================================================================*/
void
SetFramePattern(pattern)
    char *pattern;
{
    int len = strlen(pattern);
    char *buf;
    int index;

    if ( ! pattern ) {
	fprintf(stderr, "pattern cannot be NULL\n");
	exit(1);
    }

    if ( pattern[0] != 'i' && pattern[0] != 'I' ) {
	fprintf(stderr, "first frame must be 'i'\n");
	exit(1);
    }

    buf = (char *)malloc(sizeof(char)*(len+1));
    ERRCHK(buf, "malloc");

    for ( index = 0; index < len; index++ ) {
	switch( pattern[index] ) {
	    case 'i':    case 'I':	buf[index] = 'i';	    break;
	    case 'p':    case 'P':	buf[index] = 'p';	    break;
	    case 'b':    case 'B':	buf[index] = 'b';	    break;
	    default:
		fprintf(stderr, "Frame type '%c' not supported.\n", pattern[index]);
		exit(1);
	}
    }
    buf[len] = 0;

    framePattern = buf;
    framePatternLen = len;

    ComputeFrameTable();
}


/*=====================*
 * INTERNAL PROCEDURES *
 *=====================*/


/*===========================================================================*
 *
 * ComputeFrameTable
 *
 *	compute a table of I, P, B frames to help in determining dependencies
 *
 * RETURNS:	nothing
 *
 * SIDE EFFECTS:    frameTable
 *
 *===========================================================================*/
static void
ComputeFrameTable()
{
    register int index;
    FrameTable	*lastI, *lastIP, *firstB, *secondIP;
    FrameTable	*ptr;
    int	    numberP, numberN, numberNO;

    frameTable = (FrameTable *) malloc((framePatternLen+1)*sizeof(FrameTable));

    lastI = NULL;
    lastIP = NULL;
    firstB = NULL;
    secondIP = NULL;
    for ( index = 0; index < framePatternLen; index++ ) {
	frameTable[index].number = index;

	switch( framePattern[index] ) {
	    case 'i':
		ptr = firstB;
		while ( ptr != NULL ) {
		    ptr->next = &(frameTable[index]);
		    ptr = ptr->nextOutput;
		}
		frameTable[index].nextOutput = firstB;
		frameTable[index].prev = lastIP;	/* for freeing */
		if ( lastIP != NULL ) {
		    lastIP->next = &(frameTable[index]);
		    if ( secondIP == NULL ) {
			secondIP = &(frameTable[index]);
		    }
		}
		lastIP = &(frameTable[index]);
		firstB = NULL;
		break;
	    case 'p':
		ptr = firstB;
		while ( ptr != NULL ) {
		    ptr->next = &(frameTable[index]);
		    ptr = ptr->nextOutput;
		}
		frameTable[index].nextOutput = firstB;
		frameTable[index].prev = lastIP;
		if ( lastIP != NULL ) {
		    lastIP->next = &(frameTable[index]);
		    if ( secondIP == NULL ) {
			secondIP = &(frameTable[index]);
		    }
		}
		lastIP = &(frameTable[index]);
		firstB = NULL;
		break;
	    case 'b':
		if ( (index+1 == framePatternLen) ||
		     (framePattern[index+1] != 'b') ) {
		    frameTable[index].nextOutput = NULL;
		} else {
		    frameTable[index].nextOutput = &(frameTable[index+1]);
		}
		frameTable[index].prev = lastIP;
		if ( firstB == NULL ) {
		    firstB = &(frameTable[index]);
		}
		break;
	    default:
	        fprintf(stderr, "Programmer Error in ComputeFrameTable (%d)\n",
			framePattern[index]);
	        exit(1);
	        break;
	}
    }

    frameTable[framePatternLen].number = framePatternLen;

    ptr = firstB;
    while ( ptr != NULL ) {
	ptr->next = &(frameTable[framePatternLen]);
	ptr = ptr->nextOutput;
    }
    frameTable[framePatternLen].nextOutput = firstB;
    frameTable[framePatternLen].prev = lastIP;
    if ( secondIP == NULL )
	frameTable[framePatternLen].next = &(frameTable[0]);
    else
	frameTable[framePatternLen].next = secondIP;

    frameTable[0].prev = lastIP;
    if ( lastIP != NULL ) {
	lastIP->next = &(frameTable[framePatternLen]);
    }

    for ( index = 0; index < framePatternLen+1; index++ ) {
	if ( frameTable[index].prev == NULL ) {
	    numberP = -1;
	} else {
	    numberP = frameTable[index].prev->number;
	}

	if ( frameTable[index].next == NULL ) {
	    numberN = -1;
	} else {
	    numberN = frameTable[index].next->number;
	}

	if ( frameTable[index].nextOutput == NULL ) {
	    numberNO = -1;
	} else {
	    numberNO = frameTable[index].nextOutput->number;
	}
    }
}

