// cc -o /tmp/gtf -g gtf.c -Wall


#include <mach/mach.h>
#include <mach/thread_switch.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


__private_extern__ float
ratioOver( float a, float b )
{
    if( a > b)
        return( a / b );
    else
        return( b / a );
}

static int
IODisplayGetCVTSyncWidth( int horizontalActive, int verticalActive )
{
    // CVT Table 2
    enum {
	kCVTAspect4By3    = 4,
	kCVTAspect16By9   = 5,
	kCVTAspect16By10  = 6,
	kCVTAspect5By4    = 7,
	kCVTAspect15By9   = 7,
	kCVTAspectUnknown = 10
    };

    float ratio = ((float) horizontalActive) / ((float) verticalActive);

    if (ratioOver(ratio, 4.0 / 3.0) <= 1.03125)
	return (kCVTAspect4By3);

    if (ratioOver(ratio, 16.0 / 9.0) <= 1.03125)
	return (kCVTAspect16By9);

    if (ratioOver(ratio, 16.0 / 10.0) <= 1.03125)
	return (kCVTAspect16By10);

    if (ratioOver(ratio, 5.0 / 4.0) <= 1.03125)
	return (kCVTAspect5By4);

    if (ratioOver(ratio, 15.0 / 9.0) <= 1.03125)
	return (kCVTAspect15By9);

    return (kCVTAspectUnknown);
}

//FromRefreshRate
// 7.3
void GenTiming ( int requestedWidth, int requestedHeight, 
		 float frameRate, boolean_t needInterlace,
		 int genType )
{
    int		charSize = 8; 

    float	m = 600;  // % / kHz;
    float	c = 40;   // %
    float	k = 128;
    float	j = 20;   // %
    
    float	cPrime = ((c - j) * k / 256) + j;
    float	mPrime = k / 256 * m;

    float	fieldRate;						// V_FIELD_RATE_RQD
    // 7.
    float	interlace = needInterlace ? 0.5 : 0.0;
    float	interlaceFactor = needInterlace ? 2.0 : 1.0;

    // 1.
    fieldRate = frameRate * interlaceFactor;

    // 3.
    int		leftMargin = 0;
    int		rightMargin = 0;
    // 4.
    int		horizontalActive = roundf(requestedWidth / charSize) * charSize
				    + leftMargin + rightMargin;		// TOTAL_ACTIVE_PIXELS 

    // 5.
    int		verticalActive = roundf(requestedHeight / interlaceFactor);	// V_LINES_RND
    // 6.
    int		topMargin = 0;
    int		bottomMargin = 0;

    int		verticalSyncWidth; 					// V_SYNC_RND
    int		verticalSyncAndBackPorch;    				// V_SYNC_BP
    int		verticalSyncFrontPorch;
    int		horizontalTotal;					// TOTAL_PIXELS
    int		horizontalBlanking;					// H_BLANK
    int 	horizontalSyncWidth;					// H_SYNC_PIXELS


    if (0 == genType)
	verticalSyncWidth = 3;
    else
	verticalSyncWidth = IODisplayGetCVTSyncWidth(horizontalActive, verticalActive * interlaceFactor);

    if (0 == genType)		/******************************* GTF */

    {
	float horizontalSyncPercent = 8.0/100.0;			// H_SYNC_PER
	float verticalSyncAndBackPorchTime = 550e-6; 			// MIN_VSYNC_BP
	int   minVerticalFrontPorch = 1;  				// MIN_V_PORCH_RND
	float estimatedHorizontalPeriod;				// H_PERIOD_EST 
	float verticalFieldTotal;					// TOTAL_V_LINES 
	float estimatedFieldRate;					// V_FIELD_RATE_EST

	// 7.
	estimatedHorizontalPeriod = 
	    ((1 / fieldRate) - verticalSyncAndBackPorchTime) 
	    / (verticalActive + (2 * topMargin) + minVerticalFrontPorch + interlace);

	// 8.
	verticalSyncAndBackPorch = roundf(verticalSyncAndBackPorchTime / estimatedHorizontalPeriod);
	verticalSyncFrontPorch = minVerticalFrontPorch;

//    	printf("estimatedHorizontalPeriod %.9f us, verticalSyncAndBackPorch %d\n",
//		    estimatedHorizontalPeriod*1e6, verticalSyncAndBackPorch);
    
	// 10.
	verticalFieldTotal = verticalActive + topMargin + bottomMargin 
			    + verticalSyncAndBackPorch + interlace + minVerticalFrontPorch;
	// 11.
	estimatedFieldRate = 1.0 / estimatedHorizontalPeriod / verticalFieldTotal;
    
//    printf("verticalFieldTotal %.9f, estimatedFieldRate %.9f\n",
//			verticalFieldTotal, estimatedFieldRate);
    
	// 12.
	float hPeriod = estimatedHorizontalPeriod / (fieldRate / estimatedFieldRate);
    
	printf("hPeriod %.9f us, ", hPeriod*1e6);
	printf("hFreq %.9f kHz\n", 1/hPeriod/1e3);
    
	// 18.
	float idealDutyCycle = cPrime - (mPrime * hPeriod * 1e6 / 1000.0);
	// 19.
	horizontalBlanking = 2 * charSize * roundf(
				(horizontalActive * idealDutyCycle / (100.0 - idealDutyCycle) 
				/ (2 * charSize)));
    
// 	printf("idealDutyCycle %.9f, horizontalBlanking %d\n", idealDutyCycle, horizontalBlanking);
    
	// 20.
	horizontalTotal = horizontalActive + horizontalBlanking;
	// 21.
	float pixelFreq = horizontalTotal / hPeriod;
	
	printf("pixFreq %.9f Mhz\n", pixelFreq/1e6);
    
	// gtf 2.17.
	horizontalSyncWidth = roundf(horizontalSyncPercent * horizontalTotal / charSize) * charSize;

    }
    else if (1 == genType)		/******************************* CVT */
    {
	float horizontalSyncPercent = 8.0/100.0; 			// H_SYNC_PER
	float verticalSyncAndBackPorchTime = 550e-6; 			// MIN_VSYNC_BP
        int   minVerticalBackPorch = 6;					// MIN_VBPORCH
	int   minVerticalFrontPorch = 3;  				// MIN_V_PORCH_RND
	float estimatedHorizontalPeriod;				// H_PERIOD_EST 
	float verticalFieldTotal;					// TOTAL_V_LINES 

	// 8.
	estimatedHorizontalPeriod = 
	    ((1 / fieldRate) - verticalSyncAndBackPorchTime) 
	    / (verticalActive + (topMargin + bottomMargin) + minVerticalFrontPorch + interlace);
	// 9.

	verticalSyncAndBackPorch = 1 + truncf(verticalSyncAndBackPorchTime / estimatedHorizontalPeriod);
    
	if (verticalSyncAndBackPorch < (verticalSyncWidth + minVerticalBackPorch))
	    verticalSyncAndBackPorch = verticalSyncWidth + minVerticalBackPorch;
    
	// 10.
//	int verticalSyncBackPorch = verticalSyncAndBackPorch - verticalSyncWidth;

	verticalSyncFrontPorch = minVerticalFrontPorch;
    
//        printf("estimatedHorizontalPeriod %.9f us, verticalSyncAndBackPorch %d\n", 
//		    estimatedHorizontalPeriod*1e6, verticalSyncAndBackPorch);
    
	// 11.
	verticalFieldTotal = verticalActive + topMargin + bottomMargin 
			    + verticalSyncAndBackPorch + interlace + minVerticalFrontPorch;
	// 12.
	float idealDutyCycle = cPrime - (mPrime * estimatedHorizontalPeriod * 1e6 / 1000.0);
    
	// 13.
	if (idealDutyCycle < 20.0)
	    idealDutyCycle = 20.0;

	horizontalBlanking = 2 * charSize * truncf(
			    (horizontalActive * idealDutyCycle / (100.0 - idealDutyCycle) 
			    / (2 * charSize)));
    
	// 14.
	horizontalTotal = horizontalActive + horizontalBlanking;
    
	// 15.
	float frequencyStep = 0.25e6;					// CLOCK_STEP
	float pixelFrequency = frequencyStep * truncf(
			(horizontalTotal / estimatedHorizontalPeriod) / frequencyStep);
    
	printf("pixFreq %.9f Mhz\n", pixelFrequency/1e6);
    
	// 16.
	float horizontalFrequency = pixelFrequency / horizontalTotal;
    
	printf("hPeriod %.9f us, ", (1/horizontalFrequency)*1e6);
	printf("hFreq %.9f kHz\n", horizontalFrequency/1e3);

	// gtf 2.17.
	horizontalSyncWidth = charSize * truncf(
	    horizontalSyncPercent * horizontalTotal / charSize);

    }
    else	/******************************* CVT reduced blank */
    {
	float minVerticalBlankTime = 460e-6;				// RB_MIN_V_BLANK 
	float estimatedHorizontalPeriod;				// H_PERIOD_EST 
	int   verticalBlanking;						// VBI_LINES
        int   minVerticalBackPorch = 6;					// MIN_VBPORCH

	verticalSyncFrontPorch = 3;					// RB_V_FPORCH
	horizontalBlanking = 160;					// RB_H_BLANK
	horizontalSyncWidth = 32;					// RB_H_SYNC
    
	// 8.
	estimatedHorizontalPeriod = ((1 / fieldRate) - minVerticalBlankTime) 
				    / (verticalActive + topMargin + bottomMargin);
	// 9.
	verticalBlanking = truncf(minVerticalBlankTime / estimatedHorizontalPeriod) + 1; // VBI_LINES

//        printf("estimatedHorizontalPeriod %.9f us, verticalBlanking %d\n",
//			estimatedHorizontalPeriod*1e6, verticalBlanking);

	// 10.
	if (verticalBlanking < (verticalSyncFrontPorch + verticalSyncWidth + minVerticalBackPorch))
	    verticalBlanking = (verticalSyncFrontPorch + verticalSyncWidth + minVerticalBackPorch);


	verticalSyncAndBackPorch = verticalBlanking - verticalSyncFrontPorch;

	// 11.
	int verticalFieldTotal = verticalBlanking  + verticalActive 
				+ topMargin + bottomMargin + interlace;

	// 12.
	horizontalTotal = horizontalActive + horizontalBlanking;

	// 13.
	float frequencyStep = 0.25e6;					// CLOCK_STEP
    	float pixelFrequency = frequencyStep * truncf(
			    (horizontalTotal * verticalFieldTotal * fieldRate) / frequencyStep);

	printf("pixFreq %.9f Mhz\n", pixelFrequency/1e6);
    
	// 14.
	float horizontalFrequency = pixelFrequency / horizontalTotal;
    
	printf("hPeriod %.9f us, ", (1/horizontalFrequency)*1e6);
	printf("hFreq %.9f kHz\n", horizontalFrequency/1e3);

    }

//    printf("verticalFieldTotal %.9f, estimatedFieldRate %.9f\n",
//		verticalFieldTotal, estimatedFieldRate);
//    printf("idealDutyCycle %.9f, horizontalBlanking %d\n",
//		idealDutyCycle, horizontalBlanking);

    // 17.
//    float vFieldRate = hFreq / verticalFieldTotal;
    // 18.
//    float vFrameRate = vFieldRate / interlaceFactor;
    
    // stage 2
   
    // 3.
    int verticalTotal = interlaceFactor *
	(verticalActive + topMargin + bottomMargin 
	    + verticalSyncAndBackPorch + interlace + verticalSyncFrontPorch);

    int horizontalSyncOffset = (horizontalBlanking / 2) - horizontalSyncWidth;
    // 30.
    float verticalOddBlanking = verticalSyncAndBackPorch + verticalSyncFrontPorch;
    // 32.
    float verticalEvenBlanking = verticalSyncAndBackPorch + 2 * interlace + verticalSyncFrontPorch;
    // 36.
    float verticalSyncOddFrontPorch = verticalSyncFrontPorch + interlace;

    printf("hTotal %d(%d), hFP %d(%d), hBlank %d(%d), hSync %d(%d)\n",
	    horizontalTotal/8, horizontalTotal, horizontalSyncOffset/8, 
	    horizontalSyncOffset, horizontalBlanking/8, horizontalBlanking, 
	    horizontalSyncWidth/8, horizontalSyncWidth);

    printf("vTotal %d, vFP %.1f(E:%.1f), vBlank %.1f(E:%.1f), vSync %d\n\n",
	    verticalTotal, verticalSyncOddFrontPorch, (float) verticalSyncFrontPorch, 
	    verticalOddBlanking, verticalEvenBlanking, verticalSyncWidth);

}


int main (int argc, char * argv[])
{
    char * endstr;
    boolean_t needInterlace = FALSE;
    int requestedWidth = 1400, requestedHeight = 1050;
    float frameRate = 60.0;

    requestedWidth = strtol(argv[1], 0, 0);
    requestedHeight = strtol(argv[2], 0, 0);
    frameRate = strtol(argv[3], &endstr, 0);
    needInterlace = (endstr[0] == 'i') || (endstr[0] == 'I');

    printf("\nGTF:\n\n");
    GenTiming( requestedWidth, requestedHeight, frameRate, needInterlace, 0 );
    printf("\nCVT:\n\n");
    GenTiming( requestedWidth, requestedHeight, frameRate, needInterlace, 1 );
    printf("\nCVT reduced blank:\n\n");
    GenTiming( requestedWidth, requestedHeight, frameRate, needInterlace, 2 );

    return(0);
}

