/*
    Bt8xx QuickTime driver component
    Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )
    
    Based on : 
    
    macam - webcam app and QuickTime driver component
    Copyright (C) 2002 Matthias Krauss (macam@matthias-krauss.de)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "QTVideoDigitizer.h"
#include "Resolvers.h"
#include <ApplicationServices/ApplicationServices.h>
#include "MiscTools.h"
#include <syslog.h>
#import <lib/bt848.h>
#import <lib/Bt8xxLib.h>
#import <lib/bt848.h>
#import <lib/i2c.h>
#import <lib/Tuner.h>
#import <lib/Audio.h>
#import <bt8xx/Bt8xxRegs.h>

//#define NO_REAL_HW 1

#define SCALE 1

FILE * log_file;

static pthread_mutex_t  global_mutex;

void log_open()
{
    static int inited = 0;
    
    if( ! inited ) {
        inited = 1;
        log_file = fopen("/tmp/vdig", "w" );
    }
}

static void init_lock()
{
    static int inited = 0;
    
    if( inited ) {
    
        inited = 1;
        
        pthread_mutex_init( &global_mutex, NULL);
    }
}

void vdig_lock()
{
    init_lock();
    
    pthread_mutex_lock( &global_mutex );
}

void vdig_unlock()
{
    init_lock();
    
    pthread_mutex_unlock( &global_mutex );
}

pascal ComponentResult vdigMainEntry (ComponentParameters *params, Handle storage) 
{	
    ComponentResult err = 0;
    ProcPtr procPtr = 0;
    ProcInfoType procInfo;    
    char selectorName[200];
    
    log_open();

    vdig_lock();

    if(ResolveVDSelector(params->what, selectorName)) {
        //fprintf( log_file, "vdig: call to %s\n",selectorName);
    } else {
        //fprintf( log_file, "vdig: call unknown selector %d\n",params->what);
    }
    fflush( log_file );

    if (vdigLookupSelector(params->what,&procPtr,&procInfo)) {
	err=CallComponentFunctionWithStorageProcInfo((Handle)storage, params, procPtr,procInfo);
    } else {
        err=badComponentSelector;
    }

    vdig_unlock();
    
    //fprintf( log_file, "vdig: call resulted in %d\n",(int)err);
    fflush( log_file );
    
    return err;
}

bool vdigLookupSelector(short what,ProcPtr* ptr,ProcInfoType* info) 
{
    bool ok=true;
    if (what < 0) {
        switch(what) {
            case kComponentOpenSelect:      *info=uppCallComponentOpenProcInfo;
                                            *ptr=(ComponentRoutineUPP)vdigOpen; break;
            case kComponentCloseSelect:     *info=uppCallComponentCloseProcInfo;
                                            *ptr=(ComponentRoutineUPP)vdigClose; break;
            case kComponentCanDoSelect:     *info=uppCallComponentCanDoProcInfo;
                                            *ptr=(ComponentRoutineUPP)vdigCanDo; break;
            case kComponentVersionSelect:   *info=uppCallComponentVersionProcInfo;
                                            *ptr=(ComponentRoutineUPP)vdigVersion; break;
            default: ok=false; break;
        }
    } else {
        switch (what) {
            case kVDGetDigitizerInfoSelect:       *info=uppVDGetDigitizerInfoProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetDigitizerInfo; break;
            case kVDGetCurrentFlagsSelect:        *info=uppVDGetCurrentFlagsProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetCurrentFlags; break;
            case kVDGetMaxSrcRectSelect:          *info=uppVDGetMaxSrcRectProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetMaxSrcRect; break;
            case kVDGetActiveSrcRectSelect:       *info=uppVDGetActiveSrcRectProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetActiveSrcRect; break;
            case kVDGetDigitizerRectSelect:       *info=uppVDGetDigitizerRectProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetDigitizerRect; break;
            case kVDSetDigitizerRectSelect:       *info=uppVDSetDigitizerRectProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetDigitizerRect; break;
            case kVDGetNumberOfInputsSelect:      *info=uppVDGetNumberOfInputsProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetNumberOfInputs; break;
            case kVDGetInputFormatSelect:         *info=uppVDGetInputFormatProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetInputFormat; break;
            case kVDGetInputSelect:               *info=uppVDGetInputProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetInput; break;
            case kVDSetInputSelect:               *info=uppVDSetInputProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetInput; break;
            case kVDSetInputStandardSelect:       *info=uppVDSetInputStandardProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetInputStandard; break;
            case kVDGetPlayThruDestinationSelect: *info=uppVDGetPlayThruDestinationProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetPlayThruDestination; break;
            case kVDSetPlayThruDestinationSelect: *info=uppVDSetPlayThruDestinationProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetPlayThruDestination; break;
            case kVDPreflightDestinationSelect:   *info=uppVDPreflightDestinationProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigPreflightDestination; break;
            case kVDGrabOneFrameSelect:           *info=uppVDGrabOneFrameProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGrabOneFrame; break;
            case kVDGetFieldPreferenceSelect:     *info=uppVDGetFieldPreferenceProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetFieldPreference; break;
            case kVDSetFieldPreferenceSelect:     *info=uppVDSetFieldPreferenceProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetFieldPreference; break;
            case kVDGetVBlankRectSelect:          *info=uppVDGetVBlankRectProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetVBlankRect; break;
            case kVDGetVideoDefaultsSelect:       *info=uppVDGetVideoDefaultsProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetVideoDefaults; break;
            case kVDSetPlayThruOnOffSelect:	  *info=uppVDSetPlayThruOnOffProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetPlayThruOnOff; break;
            case kVDSetDestinationPortSelect:	  *info=uppVDSetDestinationPortProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetDestinationPort; break;
            case kVDGetBrightnessSelect:	  *info=uppVDGetBrightnessProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetBrightness; break;
            case kVDSetBrightnessSelect:	  *info=uppVDSetBrightnessProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetBrightness; break;
            case kVDGetContrastSelect:		  *info=uppVDGetContrastProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetContrast; break;
            case kVDSetContrastSelect:		  *info=uppVDSetContrastProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetContrast; break;
            case kVDGetSaturationSelect:	  *info=uppVDGetSaturationProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetSaturation; break;
            case kVDSetSaturationSelect:	  *info=uppVDSetSaturationProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetSaturation; break;
            case kVDGetSharpnessSelect:		  *info=uppVDGetSharpnessProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetSharpness; break;
            case kVDSetSharpnessSelect:	          *info=uppVDSetSharpnessProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetSharpness; break;
            case kVDGetPreferredTimeScaleSelect:  *info=uppVDGetPreferredTimeScaleProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetPreferredTimeScale; break;
            case kVDGetCompressionTypesSelect:	  *info=uppVDGetCompressionTypesProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetCompressionTypes; break;
            case kVDSetCompressionSelect:	  *info=uppVDSetCompressionProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetCompression; break;
            case kVDSetFrameRateSelect:		  *info=uppVDSetFrameRateProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetFrameRate; break;
            case kVDSetTimeBaseSelect:		  *info=uppVDSetTimeBaseProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetTimeBase; break;
            case kVDCompressOneFrameAsyncSelect:  *info=uppVDCompressOneFrameAsyncProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigCompressOneFrameAsync; break;
            case kVDCompressDoneSelect:		  *info=uppVDCompressDoneProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigCompressDone; break;
            case kVDResetCompressSequenceSelect:  *info=uppVDResetCompressSequenceProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigResetCompressSequence; break;
            case kVDGetImageDescriptionSelect:	  *info=uppVDGetImageDescriptionProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetImageDescription; break;
            case kVDSetCompressionOnOffSelect:	  *info=uppVDSetCompressionOnOffProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetCompressionOnOff; break;
            case kVDReleaseCompressBufferSelect:  *info=uppVDReleaseCompressBufferProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigReleaseCompressBuffer; break;
            case kVDGetDataRateSelect:		  *info=uppVDGetDataRateProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetDataRate; break;
            case kVDGetInputNameSelect:		  *info=uppVDGetInputNameProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetInputName; break;
            case kVDGetMaxAuxBufferSelect:        *info=uppVDGetMaxAuxBufferProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetMaxAuxBuffer; break;
            case kVDReleaseAsyncBuffersSelect:    *info=uppVDReleaseAsyncBuffersProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigReleaseAsyncBuffers; break;
            case kVDGetSoundInputDriverSelect:    *info=uppVDGetSoundInputDriverProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetSoundInputDriver; break;
            case kVDSetPreferredPacketSizeSelect: *info=uppVDSetPreferredPacketSizeProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetPreferredPacketSize; break;

            case kVDSetHueSelect:                 *info=uppVDSetHueProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetHue; break;
            case kVDGetHueSelect:                 *info=uppVDGetHueProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetHue; break;
            
            case kVDSetWhiteLevelValueSelect:     *info=uppVDSetWhiteLevelValueProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetWhiteLevelValue; break;
            case kVDGetWhiteLevelValueSelect:     *info=uppVDGetWhiteLevelValueProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetWhiteLevelValue; break;

            case kVDSetBlackLevelValueSelect:     *info=uppVDSetBlackLevelValueProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigSetBlackLevelValue; break;
            case kVDGetBlackLevelValueSelect:     *info=uppVDGetBlackLevelValueProcInfo;
                                                  *ptr=(ComponentRoutineUPP)vdigGetBlackLevelValue; break;


            default: ok=false; break;
        }
        
    }
    return ok;
}

void AdjustColorFormat()
{
#if ! defined NO_REAL_HW
    BYTE ColorFormat = kBtColorFmtRGB32;
    BYTE cFormat = ColorFormat << kBtColorFmtEvenShift |  ColorFormat << kBtColorFmtOddShift;
    BYTE colorCtl = kBtColorCtlWSwapEven | kBtColorCtlBSwapEven | kBtColorCtlWSwapOdd | kBtColorCtlBSwapOdd;

    colorCtl |= kBtColorCtlGamma;

    BT848_WriteByte(BT848_COLOR_FMT, cFormat );
    BT848_WriteByte(kBtColorCtl, colorCtl );
#endif
}

static bool initVideoHW( vdigGlobals storage )
{
#if ! defined NO_REAL_HW
    int rc;
    
    LoadSettingsFromIni();
    
    LoadCountrySettings();
    
    if( BT848_Init() != KERN_SUCCESS ) {
        return false;
    }

    Audio_SetSource(AUDIOMUX_MUTE);

    if( PreferedCard() == TVCARD_UNKNOWN ) {   

        TVCard_FirstTimeSetupHardware(0,0);
    }
    

    rc = MapVideoMem( (void**) &(**storage).mVideoBuffer, &(**storage).mVideoBufferSize );
    
    //fprintf( log_file, "MapMem() == %d videoBuf at %p with size %d\n", rc, 
    //          (void*) (**storage).mVideoBuffer, (**storage).mVideoBufferSize );

    CurrentX_OnChange( SCALE*360 );

    Start_Capture();

    Tuner_Init();

    TVCard_OnChange  ( PreferedCard() );

    TVFormat_OnChange( PreferedTVType() );

    TVTuner_OnChange ( PreferedTuner() );

    TuneChannel( Channels.freq[ PreferedChannel() ] );

    BT848_SetVideoSource( PreferedVideoSource() );
    
    Audio_SetSource( (AUDIOMUXTYPE) PreferedAudioSource() );

    AdjustColorFormat();

#endif
        
    return true;
}

pascal ComponentResult vdigOpen(vdigGlobals storage, ComponentInstance self) 
{
    OSErr err;
    if (CountComponentInstances((Component)self)>1) return validInstancesExist;	//We only want one

    storage = (vdigGlobals)NewHandleClear(sizeof(VDGlobals));

    if (err = MemError()) return err;

    (**storage).self = self;

#if ! defined NO_REAL_HW

    if( ! initVideoHW( storage ) ) {
    
        DisposeHandle((Handle)storage);
        
        return mFulErr;
    }

#endif

    (**storage).imgDesc=(ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
    if (!((**storage).imgDesc)) {
        DisposeHandle((Handle)storage);
        return mFulErr;
    }
            
    SetRect (&((**storage).maxRect),0,0,360,288);
    SetRect (&((**storage).digitizerRect),0,0,360,288);
    
    (**storage).fps=Long2Fix(5);
    (**storage).timeBase=NULL;
    (**storage).vdState=vdsIdle;
    (**storage).playThruMatrix.matrix[0][0] = Long2Fix(1);
    (**storage).playThruMatrix.matrix[0][1] = Long2Fix(0);
    (**storage).playThruMatrix.matrix[0][2] = Long2Fix(0);
    (**storage).playThruMatrix.matrix[1][0] = Long2Fix(0);
    (**storage).playThruMatrix.matrix[1][1] = Long2Fix(1);
    (**storage).playThruMatrix.matrix[1][2] = Long2Fix(0);
    (**storage).playThruMatrix.matrix[2][0] = Long2Fix(0);
    (**storage).playThruMatrix.matrix[2][1] = Long2Fix(0);
    (**storage).playThruMatrix.matrix[2][2] = Long2Fix(1);
    (**storage).fieldPreference=vdUseAnyField;
    (**storage).inputStandard=palIn;
    (**storage).destinationPort=NULL;
    (**storage).playThruDest = NULL;
    SetRect(&((**storage).playThruDestRect),0,0,360,288);
    (**storage).fillingBuffer=NULL;
    (**storage).compressedType=kRawCodecType;
    (**storage).compressedDepth=0;
    SetRect (&((**storage).compressedBounds),0,0,360,288);
    (**storage).compressedSpatialQuality=codecLosslessQuality;
    (**storage).compressedTemporalQuality=codecLosslessQuality;
    (**storage).compressedKeyFrameRate=1;


    
    (**storage).mGWorld = 0;
    
    {
        char * p = (char * ) (**storage).mVideoBuffer;

        Rect r;
        
        SetRect ( &r,0,0,360,288);
    
        err = NewGWorldFromPtr( &(**storage).mGWorld, 32, &r, 0, 0, 0,  (char*) p, 360 * 4  );

    }

    SetComponentInstanceStorage(self, (Handle)storage);
    CheckCachedStuff(storage);    

    return 0;
}

pascal ComponentResult vdigClose(vdigGlobals storage, ComponentInstance self)
{
    if(storage) {

#if ! defined NO_REAL_HW
        Stop_Capture();
#endif

        if ((**storage).imgDesc) {
            DisposeHandle((Handle)((**storage).imgDesc));
        }
        DisposeHandle((Handle)storage);
    }
    return 0;
}

pascal ComponentResult vdigCanDo(vdigGlobals storage, short ftnNumber)
{
    ProcPtr procPtr;
    ProcInfoType procInfo;
        
    if (vdigLookupSelector(ftnNumber,&procPtr,&procInfo)) return 1;
    else return 0;
}

pascal ComponentResult vdigVersion(vdigGlobals storage) 
{
    return 0x00010001;
}

pascal VideoDigitizerError vdigGetDigitizerInfo(vdigGlobals storage, DigitizerInfo *info) 
{
    VideoDigitizerError err=0;
        
    info->vdigType=vdTypeBasic; 		/* type of digitizer component */

    info->inputCapabilityFlags=			/* input video signal features */
                digiInDoesColor
                |digiInDoesComponent
                |digiInDoesPAL			//We'll simply say we do all to make
                |digiInDoesNTSC			//people happy. But we ignore it since
                |digiInDoesSECAM;		//these standards don't apply to us.

    info->outputCapabilityFlags=	 	/* output digitized video data features of digitizer component */
                digiOutDoes32
//                |digiOutDoes16
//                |digiOutDoesAsyncGrabs
//                |digiOutDoesCompress
//                digiOutDoes1
//                |digiOutDoes2
//                |digiOutDoes4
//                |digiOutDoes8
//                |digiOutDoesStretch
//                |digiOutDoesDouble
//                |digiOutDoesQuad
//                |digiOutDoesQuarter
//                |digiOutDoesSixteenth
//                |digiOutDoesShrink
                  ;
                  
    info->inputCurrentFlags=		  	/* status of input video signal */
                info->inputCapabilityFlags;

    info->inputCurrentFlags|=digiInSignalLock;
    info->outputCurrentFlags=		 	/* status of output digitized video information */
                info->outputCapabilityFlags;

    info->slot=-1; 				/* for connection purposes */
    info->gdh=NULL; 				/* for digitizers with preferred screen */
    info->maskgdh=NULL; 			/* for digitizers with mask planes */
    info->minDestHeight=120; 			/* smallest resizable height */
    info->minDestWidth=160; 			/* smallest resizable width */
    info->maxDestHeight=576;			/* largest resizable height */
    info->maxDestWidth=720;			/* largest resizable width */
    info->blendLevels=0;			/* number of blend levels supported (2 if 1-bit mask) */
    info->reserved=0; 				/* reserved--set to 0 */

    return err;
}

pascal VideoDigitizerError vdigGetCurrentFlags(vdigGlobals storage, long *inputCurrentFlag, long *outputCurrentFlag) 
{
    VideoDigitizerError err=0;
    DigitizerInfo info;

    err=vdigGetDigitizerInfo(storage, &info);

    if ((**storage).inputStandard==secamIn) info.inputCurrentFlags&=0xffffffff-(digiInDoesPAL+digiInDoesNTSC);
    else if ((**storage).inputStandard==palIn) info.inputCurrentFlags&=0xffffffff-(digiInDoesNTSC+digiInDoesSECAM);
    else info.inputCurrentFlags&=0xffffffff-(digiInDoesPAL+digiInDoesSECAM);

    if (inputCurrentFlag) *inputCurrentFlag=info.inputCurrentFlags;

    if (outputCurrentFlag) *outputCurrentFlag=info.outputCurrentFlags;

    return err;
}

pascal VideoDigitizerError vdigGetMaxSrcRect(vdigGlobals storage, short inputStd, Rect *maxSrcRect) 
{
    if (!maxSrcRect) return qtParamErr;		//force reference pointer to be valid
    *maxSrcRect=(**storage).maxRect;		//return maxRect
    return 0;
}

pascal VideoDigitizerError vdigGetActiveSrcRect(vdigGlobals storage, short inputStd, Rect *activeSrcRect) 
{
    if (!activeSrcRect) return qtParamErr;	//force reference pointer to be valid

    *activeSrcRect=(**storage).maxRect;		//return maxRect

    return 0;
}

pascal VideoDigitizerError vdigGetDigitizerRect(vdigGlobals storage, Rect *digiRect) 
{
    if (digiRect) return qtParamErr;		//force reference pointer to be valid
    *digiRect=(**storage).digitizerRect;	//output currend digitizer rect

    return 0;
}

pascal VideoDigitizerError vdigSetDigitizerRect(vdigGlobals storage, Rect *digiRect) 
{
    if (!digiRect) return qtParamErr;			//force pointer to be valid
    (**storage).digitizerRect=*digiRect;		//We'll only save it here. We don't really handle it yet

    return 0;
}

pascal VideoDigitizerError vdigGetNumberOfInputs(vdigGlobals storage, short *inputs) 
{
    if (!inputs) return qtParamErr;			//force pointer to be valid
    *inputs=0;

    return 0;
}

pascal VideoDigitizerError vdigGetInputFormat(vdigGlobals storage, short input, short *format) 
{
    if (!format) return qtParamErr;			//pointer has to be valid
    //if ((input<0)||(input>=[(**storage).bridge numCams])) return qtParamErr;			//input has to be in correct range
    *format=rgbComponentIn;				//let's say it's component video...

    return 0;
}

pascal VideoDigitizerError vdigGetInput(vdigGlobals storage, short *input) 
{
    if (!input) return qtParamErr;			//force valid pointer
    *input=0;

    return 0;
}

pascal VideoDigitizerError vdigSetInput(vdigGlobals storage, short input) 
{
    if (input!=0) return qtParamErr;

    return 0;
}


pascal VideoDigitizerError vdigSetInputStandard(vdigGlobals storage, short inputStandard) 
{

    //We ignore this. PAL, SECAM and NTSC simply don't apply to us. But people wanted
    //to set it - let's make them happy by letting them do so.
    
    (**storage).inputStandard=inputStandard;
    
    return 0;
}


pascal VideoDigitizerError vdigGetPlayThruDestination(vdigGlobals storage,PixMapHandle* dest, Rect* destRect, MatrixRecord* m, RgnHandle* mask) 
{
    if (dest) *dest=(**storage).playThruDest;
    
    if (destRect) *destRect=(**storage).playThruDestRect;
    if (m) *m=(**storage).playThruMatrix;

    return 0;
}


pascal VideoDigitizerError vdigSetPlayThruDestination( vdigGlobals storage, PixMapHandle dest, Rect *destRect, 								       MatrixRecord *m, RgnHandle mask) 
{
    VideoDigitizerError err=vdigPreflightDestination(storage,&((**storage).digitizerRect),dest,destRect,m);
    if (err) return err;
    if (mask)  return qtParamErr;
    (**storage).playThruDest=dest;				//Parameters alright. Remember them.
    (**storage).playThruDestRect=*destRect;

    return 0;
}


pascal VideoDigitizerError vdigPreflightDestination(vdigGlobals storage, Rect *digitizerRect, PixMap **dest, Rect *destRect, MatrixRecord *m) 
{
    if (m) {						//If there's a matrix...
        if (m->matrix[0][0]=!Long2Fix(1)) return matrixErr;	//make sure it's the identity matrix
        if (m->matrix[0][1]=!Long2Fix(0)) return matrixErr;
        if (m->matrix[0][2]=!Long2Fix(0)) return matrixErr;
        if (m->matrix[1][0]=!Long2Fix(0)) return matrixErr;
        if (m->matrix[1][1]=!Long2Fix(1)) return matrixErr;
        if (m->matrix[1][2]=!Long2Fix(0)) return matrixErr;
        if (m->matrix[2][0]=!Long2Fix(0)) return matrixErr;
        if (m->matrix[2][1]=!Long2Fix(0)) return matrixErr;
        if (m->matrix[2][2]=!Long2Fix(1)) return matrixErr;
    }
    if (!digitizerRect) return qtParamErr;
    if (!dest) return qtParamErr;

    return 0;
}

pascal VideoDigitizerError vdigGrabOneFrame(vdigGlobals storage) 
{
    Ptr tmpBuf;

    Rect srcRect,dstRect;
    Point pt={0,0};

    if ((**storage).vdState!=vdsIdle) return badCallOrderErr;	//we need to be in uncompressed mode and no grab running
    if (!((**storage).destinationPort)) {

        //fprintf( log_file, "vdigGrabOneFrame:No destination port set\n");
        return badCallOrderErr;
    }

    if (!((**storage).playThruDest)) {
        //fprintf( log_file, "vdigGrabOneFrame:No destination PixMap set\n");
        return badCallOrderErr;
    }

    //initiate grab

    SetRect(&srcRect,0,0, SCALE*360, SCALE*288 );
    
    SetPort((**storage).destinationPort);
    dstRect=(**storage).playThruDestRect;
    GlobalToLocal(&pt);
    OffsetRect(&dstRect,pt.h,pt.v);

    BT848_Wait4Video();
    
#if ! defined NO_REAL_HW
    
    DecompressImage( (**storage).mVideoBuffer,
                    (**storage).imgDesc,
                    (**storage).playThruDest,
                    &srcRect,
                    &dstRect,
                    srcCopy,
                    NULL);
    
#endif

    return 0;
}

pascal VideoDigitizerError vdigGetFieldPreference(vdigGlobals storage, short* fieldFlag) 
{
    if (!fieldFlag) return qtParamErr;		//force valid pointer
    *fieldFlag=(**storage).fieldPreference;	//return our saved value

    return 0;
}

pascal VideoDigitizerError vdigSetFieldPreference(vdigGlobals storage, short fieldFlag) 
{
    if ((fieldFlag!=vdUseAnyField)&&(fieldFlag!=vdUseOddField)&&(fieldFlag!=vdUseEvenField)) 
        return qtParamErr;	//force valid parameter

    (**storage).fieldPreference=fieldFlag;	//remeber (just for sake of consistency)

    return 0;
}

pascal VideoDigitizerError vdigGetVBlankRect(vdigGlobals storage, short inputStd, Rect* vBlankRect) 
{

    if (!vBlankRect) return qtParamErr;		//force valid pointer
    SetRect(vBlankRect,0,0,(**storage).maxRect.right,0);	//create full-width, zero-height rectangle

    return 0;
}

pascal VideoDigitizerError vdigGetVideoDefaults(vdigGlobals storage,
                                        unsigned short *blackLevel, unsigned short *whiteLevel,
                                        unsigned short *brightness, unsigned short *hue, unsigned short *saturation,
                                        unsigned short *contrast, unsigned short *sharpness) 
{
    //Give back our standard values
    if (blackLevel) *blackLevel=32767;
    if (whiteLevel) *whiteLevel=32767;
    if (brightness) *brightness = (unsigned short) ((((float)(DEFAULT_BRIGHTNESS_NTSC+128))/ 256.0 ) * 65535.0);
    if (hue)        *hue        = (unsigned short) ((((float)(DEFAULT_HUE_NTSC+128))       / 256.0 ) * 65535.0);
    if (saturation) *saturation = (unsigned short) ((((float)(DEFAULT_SAT_V_NTSC + DEFAULT_SAT_U_NTSC)/2)/256.0) * 65535.0);
    if (contrast)   *contrast   = (unsigned short) ((((float)(DEFAULT_CONTRAST_NTSC))     / 256.0 ) * 65535.0);
    if (sharpness)  *sharpness=32767;
    return 0;
}

pascal VideoDigitizerError vdigSetPlayThruOnOff(vdigGlobals storage, short state) 
{
    switch ((**storage).vdState) {
        case vdsIdle:
            if (state) (**storage).vdState=vdsPlayThru;
            break;
        case vdsPlayThru:
            if (!state) (**storage).vdState=vdsIdle;
            break;
        default:
            return badCallOrderErr;
    }

    return 0;
}

pascal VideoDigitizerError vdigSetDestinationPort(vdigGlobals storage, CGrafPtr port) 
{

    (**storage).destinationPort=port; //We take everything here - the error will arise when grabbing with NULL port

    return 0; 
}

pascal VideoDigitizerError vdigGetBrightness(vdigGlobals storage,unsigned short* val) 
{
    // *val should be in 0 .. 65536

    extern long InitialBrightness;
    
    float v =  ((float) InitialBrightness + 128.0 ) / 256.0;
    
    if( val ) * val = ( v * 65536.0 );
    
    //fprintf( log_file, "vdigGetBrightness(%d)\n", *val );

    return 0;
}

pascal VideoDigitizerError vdigSetBrightness(vdigGlobals storage,unsigned short* val) 
{
    // *val is in 0 .. 65536
    
    float v = (float) (*val) / 65536.0;
    
    int b = (int) ( 256 * v ) - 128;

    //fprintf( log_file, "vdigSetBrightness(%d)\n", b );

#if ! defined NO_REAL_HW    
    BT848_Brightness_OnChange( b & 0xff );
#endif

    return 0;
}

pascal VideoDigitizerError vdigGetContrast(vdigGlobals storage,unsigned short* val) 
{
    // *val should be in 0 .. 65536

    extern long InitialContrast;
    
    float v =  ((float) InitialContrast ) / 256.0;
    
    if( val ) * val = ( v * 65536.0 );
    
    //fprintf( log_file, "vdigGetContrast(%d)\n", *val );

    return 0;
}

pascal VideoDigitizerError vdigSetContrast(vdigGlobals storage,unsigned short* val) 
{
    // *val is in 0 .. 65536
    
    float v = (float) (*val) / 65536.0;
    
    int b = (int) ( 256 * v );

    //fprintf( log_file, "vdigSetContrast(%d)\n", b );
    
#if ! defined NO_REAL_HW
    BT848_Contrast_OnChange( b & 0xff );
#endif

    return 0;
}

pascal VideoDigitizerError vdigGetSaturation(vdigGlobals storage,unsigned short* val) 
{
    // *val should be in 0 .. 65536

    extern long InitialSaturation;
    
    float v =  ((float) InitialSaturation ) / 256.0;
    
    if( val ) * val = ( v * 65536.0 );
    
    //fprintf( log_file, "vdigGetSaturation(%d)\n", *val );

    return 0;
}

pascal VideoDigitizerError vdigSetSaturation(vdigGlobals storage,unsigned short* val) 
{
    // *val is in 0 .. 65536
    
    float v = (float) (*val) / 65536.0;
    
    int b = (int) ( 256 * v );

    //fprintf( log_file, "vdigSetSaturation(%d)\n", b );
    
#if ! defined NO_REAL_HW
    BT848_Saturation_OnChange( b & 0xff );
#endif

    return 0;
}

VideoDigitizerError vdigSetHue( VideoDigitizerComponent ci, unsigned short * val )
{
    // *val is in 0 .. 65536
    
    float v = (float) (*val) / 65536.0;
    
    int b = (int) ( 256 * v ) - 128;

    //fprintf( log_file, "vdigSetHue(%d)\n", b );

#if ! defined NO_REAL_HW    
    BT848_Hue_OnChange( b & 0xff );
#endif

    return 0;
}

VideoDigitizerError vdigGetHue( VideoDigitizerComponent ci, unsigned short * val )
{
    // *val should be in 0 .. 65536

    extern long InitialHue;
    
    float v =  ((float) InitialHue + 128.0 ) / 256.0;
    
    if( val ) * val = ( v * 65536.0 );
    
    //fprintf( log_file, "vdigGetHue(%d)\n", *val );

    return 0;
}

pascal VideoDigitizerError vdigGetSharpness(vdigGlobals storage,unsigned short* val) 
{
    if( val ) * val = 32767;
    
    return 0;
}

pascal VideoDigitizerError vdigSetSharpness(vdigGlobals storage,unsigned short* val) 
{
    return 0;
}

pascal VideoDigitizerError vdigGetPreferredTimeScale(vdigGlobals storage,TimeScale* ts) 
{
    if (!ts) return qtParamErr;			//force valid pointer
    *ts=600;					//we take the usual QuickTime TimeScale
    return 0;
}

pascal VideoDigitizerError vdigGetCompressionTypes(vdigGlobals storage, VDCompressionListHandle h) 
{
    ComponentDescription cDesc={decompressorComponentType,kRawCodecType,0,0,0};

    Component decoder=FindNextComponent(NULL,&cDesc);

    assert(decoder);
    if (!h) return qtParamErr;		//force valid handle

    if (GetHandleSize((Handle)h)!=sizeof(VDCompressionList)) {	//incorrect handle size:
        SetHandleSize((Handle)h,sizeof(VDCompressionList));	//try to resize
        if (GetHandleSize((Handle)h)!=sizeof(VDCompressionList))  return mFulErr;	//could not resize
    }

    //Handle size ok. Give back info.
    (**h).codec=decoder;
    (**h).cType=kRawCodecType;
    CStr2PStr("Raw (uncompressed) data",(**h).typeName);
    CStr2PStr("Raw (uncompressed) data",(**h).name);
    
    (**h).formatFlags=0;
    (**h).compressFlags=0;
    (**h).reserved=0;

    return 0;
}

pascal VideoDigitizerError vdigSetCompression(vdigGlobals storage,OSType compressType, short depth, Rect* bounds,
                                            CodecQ spatialQuality, CodecQ temporalQuality, long keyFrameRate) 
{
    //We ignore almost all of these parameters. This is not fatal because we send an imageDescription with it.
    if (!bounds) return qtParamErr;		//force valid pointer
    (**storage).compressedType=compressType;
    (**storage).compressedDepth=depth;
    (**storage).compressedBounds=*bounds;
    (**storage).compressedSpatialQuality=spatialQuality;
    (**storage).compressedTemporalQuality=temporalQuality;
    (**storage).compressedKeyFrameRate=keyFrameRate;

    return 0;
}

pascal VideoDigitizerError vdigSetFrameRate(vdigGlobals storage,Fixed fps) 
{
    (**storage).fps=fps;	//We remember but ignore this

    return 0;
}

pascal VideoDigitizerError vdigSetTimeBase(vdigGlobals storage,TimeBase t) 
{
    (**storage).timeBase=t;		//We'll save it for later timing info

    return 0;
}

pascal VideoDigitizerError vdigCompressOneFrameAsync(vdigGlobals storage) 
{
#if 0
//We don't start if a transfer is already active
    if ([(**storage).bridge isAsyncGrabRunning]==YES) return badCallOrderErr;  
//or if we don't have a timebase yet
   if (!((**storage).timeBase)) return badCallOrderErr;  
//Calculate the currently required buffer size
//allocate a buffer or recycle a used one
   (**storage).fillingBuffer=GetRawBuffer(storage);
    if (!((**storage).fillingBuffer)) return mFulErr;
//Initiate the transfer
    [(**storage).bridge grabOneFrameAsync:(**storage).fillingBuffer];
#endif
    return badCallOrderErr;
}

pascal VideoDigitizerError vdigCompressDone(vdigGlobals storage,Boolean* done,Ptr* theData,long* dataSize,
                                            UInt8* similarity,TimeRecord* t) 
                                            {
#if 0
    if (!done) return qtParamErr;			//We need this to return the state
    switch ((**storage).vdState) {
        case vdsCompressedIdle:
        case vdsCompressedAsyncGrab:
            if ([(**storage).bridge isAsyncGrabRunning]==YES) { //Are we still grabbing?
                *done=false;
                return 0;
            } else {						//we are done. Give back the info
                (**storage).vdState=vdsCompressedIdle;
                *done=true;
                if (theData) *theData=(**storage).fillingBuffer;
                if (dataSize) *dataSize=(**storage).rawPoolBufferSize;
                if (similarity) *similarity=255;
                if (t) {
                    GetTimeBaseTime((**storage).timeBase,600,t);	//This is no accurate time, but there are more serious problems
                }
            }
            return 0;
            break;
        default: return badCallOrderErr; break;	//Not in correct state - calling this makes only sense in vdsCompressedAsyncGrab
    }
#else
    return badCallOrderErr;
#endif
}

pascal VideoDigitizerError vdigResetCompressSequence(vdigGlobals storage) 
{
    //We don't need to do anything since every frame is a keyframe here
    return 0;
}

pascal VideoDigitizerError vdigGetImageDescription(vdigGlobals storage, ImageDescriptionHandle imgDesc) 
{
    if (!imgDesc) return qtParamErr;	//force valid handle
    if (GetHandleSize((Handle)imgDesc)!=sizeof(ImageDescription)) {	//size handle to fit
        SetHandleSize((Handle)imgDesc,sizeof(ImageDescription));
        if (GetHandleSize((Handle)imgDesc)!=sizeof(ImageDescription)) return mFulErr;
    }
    HLock((Handle)imgDesc);
    HLock((Handle)((**storage).imgDesc));
    memcpy(*imgDesc,*((**storage).imgDesc),sizeof(ImageDescription));
    HUnlock((Handle)imgDesc);    
    HUnlock((Handle)((**storage).imgDesc));    
    return 0;
}

pascal VideoDigitizerError vdigSetCompressionOnOff(vdigGlobals storage, Boolean state) 
{
    switch ((**storage).vdState) {
        case vdsIdle:
            if (state) (**storage).vdState=vdsCompressedIdle;
            return 0;
            break;
        case vdsCompressedIdle:
            if (!state) {
                (**storage).vdState=vdsIdle;
                CheckCachedStuff(storage);
            }
            return 0;
            break;
        default: return badCallOrderErr; break;
    }
}

pascal VideoDigitizerError vdigReleaseCompressBuffer(vdigGlobals storage,Ptr buffer) 
{
    if (!buffer) return qtParamErr;

    return 0;
}

pascal VideoDigitizerError vdigGetDataRate(vdigGlobals storage, long* mspf, Fixed* fps, long* bps) 
{
    if (mspf) *mspf=0;
    if (fps) *fps=25;
    if (bps) *bps=25*SCALE*360*SCALE*288*4;
    return 0;
}

pascal VideoDigitizerError vdigGetInputName(vdigGlobals storage, long videoInput, Str255 name) 
{
    if (!name) return qtParamErr;
    CStr2PStr("BT848/BT878",name);
    return 0;
}

//

VideoDigitizerError vdigGetMaxAuxBuffer( vdigGlobals storage, PixMapHandle * pm, Rect * r )
{
    int err;
    
    if( r )  SetRect ( r,0,0,360,288);
    
    if( pm ) * pm = GetGWorldPixMap( (**storage).mGWorld );

    return 0;
}

VideoDigitizerError vdigReleaseAsyncBuffers( VideoDigitizerComponent ci )
{
    return 0;
}

pascal VideoDigitizerError vdigGetSoundInputDriver( VideoDigitizerComponent ci, Str255 soundDriverName )
{
    return digiUnimpErr;
}

pascal VideoDigitizerError vdigSetPreferredPacketSize( VideoDigitizerComponent ci, long preferredPacketSizeInBytes )
{
    return digiUnimpErr;
}

VideoDigitizerError vdigSetBlackLevelValue( VideoDigitizerComponent ci, unsigned short * blackLevel )
{
    return 0;
}

VideoDigitizerError vdigGetBlackLevelValue( VideoDigitizerComponent ci, unsigned short * blackLevel )
{
    return 0;
}

VideoDigitizerError vdigSetWhiteLevelValue( VideoDigitizerComponent ci, unsigned short * whiteLevel )
{
    return 0;
}

VideoDigitizerError vdigGetWhiteLevelValue( VideoDigitizerComponent ci, unsigned short * whiteLevel )
{
    return 0;
}

//Tool functions

//Check everything we have cached and recycled and update if nedessary. This is called each time the raw buffer pool is accessed or something similar
void CheckCachedStuff(vdigGlobals storage) 
{
    //long neededBufSize=[(**storage).bridge width]*[(**storage).bridge height]*3;
    long neededBufSize=SCALE*360*SCALE*288*4;

    (**((**storage).imgDesc)).idSize=sizeof(ImageDescription);	//update image description
    (**((**storage).imgDesc)).cType=kRawCodecType;
    (**((**storage).imgDesc)).resvd1=0;
    (**((**storage).imgDesc)).resvd2=0;
    (**((**storage).imgDesc)).dataRefIndex=0;
    (**((**storage).imgDesc)).version=1;
    (**((**storage).imgDesc)).revisionLevel=1;
    (**((**storage).imgDesc)).vendor=0;
    (**((**storage).imgDesc)).temporalQuality=codecLosslessQuality;
    (**((**storage).imgDesc)).spatialQuality=codecLosslessQuality;
    (**((**storage).imgDesc)).width= SCALE*360; //[(**storage).bridge width];
    (**((**storage).imgDesc)).height= SCALE*288; //[(**storage).bridge height];
    (**((**storage).imgDesc)).hRes=Long2Fix(SCALE*360);
    (**((**storage).imgDesc)).vRes=Long2Fix(SCALE*288);
    (**((**storage).imgDesc)).dataSize=neededBufSize;
    (**((**storage).imgDesc)).frameCount=1;
    (**((**storage).imgDesc)).name[0]=0;
    (**((**storage).imgDesc)).depth=32;
    (**((**storage).imgDesc)).clutID=-1;
        
}
