/*
    PsychAllocOutStructArray()
    
    -If argument is optional we allocate the structure even if the argument is not present.  If this bothers you, 
    then check within the subfunction for the presense of a return argument before creating the struct array.  We
    allocate space regardeless of whether the argument is present because this is consistant with other "PsychAllocOut*" 
    functions which behave this way because in some situations subfunctions might derive returned results from values
    stored in an optional argument.
    
    -If position is -1 then don't attempt to return the created structure to the calling environment.  Instead just 
    allocate the structure and return it in pStruct.  This is how to create a structure which is embeded within another 
    structure using PsychSetStructArrayStructArray().  Note that we use -1 as the flag and not NULL because NULL is 0 and
    0 is reserved for future use as a reference to the subfunction name, of if none then the function name. 
    

*/
boolean PsychAllocOutStructArray(	int position, 
                                        PsychArgRequirementType isRequired, 
                                        int numElements,
                                        int numFields, 
                                        const char **fieldNames,  
                                        PsychGenericScriptType **pStruct)
{
    mxArray **mxArrayOut;
    int structArrayNumDims=2;
    int structArrayDims[2];
	PsychError matchError;
	Boolean putOut;
	
    
    structArrayDims[0]=1;
    structArrayDims[1]=numElements;
    
    if(position !=kPsychNoArgReturn){  //Return the result to both the C caller and the scripting environment.
        PsychSetReceivedArgDescriptor(position, PsychArgOut);
        PsychSetSpecifiedArgDescriptor(position, PsychArgOut, PsychArgType_structArray, isRequired, 1,1,numElements,numElements,0,0);
        *pStruct = mxCreateStructArray(structArrayNumDims, structArrayDims, numFields, fieldNames);
		matchError=PsychMatchDescriptors();
		putOut=PsychAcceptOutputArgumentDecider(isRequired, matchError);
		if(putOut){
			mxArrayOut = PsychGetOutArgMxPtr(position);
            *mxArrayOut=*pStruct;
		}
		return(putOut);
    }else{ //Return the result only to the C caller.  Ignore "required".    
        *pStruct = mxCreateStructArray(structArrayNumDims, structArrayDims, numFields, fieldNames);
        return(TRUE);
    }
            
}
/*
    PsychAllocOutCellArray()
    
    -If argument is optional we allocate the structure even if the argument is not present.  If this behavior bothers you, 
    then check within your code for the presense of a return argument before creating the struct array.  We
    allocate space regardeless of whether the argument is present because this is consistant with other "PsychAllocOut*" 
    functions which behave this way because in some situations subfunctions might derive returned results from values
    stored in an optional argument.
    
    -If position is -1 then don't attempt to return the created structure to the calling environment.  Instead just 
    allocate the structure and return it in pStruct.  This is how to create a structure which is embeded within another 
    structure using PsychSetStructArrayStructArray().  Note that we use -1 as the flag and not NULL because NULL is 0 and
    0 is reserved for future use as a reference to the subfunction name, of if none then the function name. 
    

*/
boolean PsychAllocOutCellVector(	int position, 
									PsychArgRequirementType isRequired, 
									int numElements,  
									PsychGenericScriptType **pCell)
{
    mxArray **mxArrayOut;
    int cellArrayNumDims=2;
    int cellArrayDims[2];
	PsychError matchError;
	Boolean putOut;

    
    cellArrayDims[0]=1;
    cellArrayDims[1]=numElements;
    
    if(position != kPsychNoArgReturn){  //Return the result to both the C caller and the scripting environment.
        PsychSetReceivedArgDescriptor(position, PsychArgOut);
        PsychSetSpecifiedArgDescriptor(position, PsychArgOut, PsychArgType_cellArray, isRequired, 1,1,numElements,numElements,0,0);
        *pCell = mxCreateCellArray(cellArrayNumDims, cellArrayDims);
        mxArrayOut = PsychGetOutArgMxPtr(position);
		matchError=PsychMatchDescriptors();
		putOut=PsychAcceptOutputArgumentDecider(isRequired, matchError);
		if(putOut)
            *mxArrayOut=*pCell;
        return(putOut);
    }else{ //Return the result only to the C caller, not to the calling environment.  Ignore "required".    
        *pCell = mxCreateCellArray(cellArrayNumDims, cellArrayDims);
        return(TRUE);
    }
            
}
PsychError PSYCHHIDReceiveReportsStop(void) 
{
	long error=0;
	int deviceIndex;
	mxArray **outErr;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(1));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	
	error=ReceiveReportsStop(deviceIndex);

	outErr=PsychGetOutArgMxPtr(1);
	if(outErr!=NULL){
		char *name="",*description="";
		const char *fieldNames[]={"n", "name", "description"};
		mxArray *fieldValue;

		PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
// GiveMeReports is called solely by PsychHIDGiveMeReports, but the code resides here
// in PsychHIDReceiveReports because it uses the typedefs and static variables that
// are defined solely in this file. The linked lists of reports are unknown outside of this file.
PsychError GiveMeReports(int deviceIndex,int reportBytes)
{
	mwSize dims[]={1,1};
	mxArray **outReports;
	ReportStruct *r,*rTail;
	const char *fieldNames[]={"report", "device", "time"};
	mxArray *fieldValue;
	unsigned char *reportBuffer;
	int i,n;
    unsigned int j;
	long error=0;
	double now;
	
	CountReports("GiveMeReports beginning.");

	outReports=PsychGetOutArgMxPtr(1); 
	r=deviceReportsPtr[deviceIndex];
	n=0;
	while(r!=NULL){
		n++;
		rTail=r;
		r=r->next;
	}
	*outReports=mxCreateStructMatrix(1,n,3,fieldNames);
	r=deviceReportsPtr[deviceIndex];
	PsychGetPrecisionTimerSeconds(&now);
	for(i=n-1;i>=0;i--){
		// assert(r!=NULL);
		if(r->error)error=r->error;
		dims[0]=1;
		//printf("%2d: r->bytes %2d, reportBytes %4d, -%4.1f s\n",i,(int)r->bytes,(int)reportBytes, now-r->time);
		if(r->bytes> (unsigned int) reportBytes)r->bytes=reportBytes;
		dims[1]=r->bytes;
		fieldValue=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL);
		reportBuffer=(void *)mxGetData(fieldValue);
		for(j=0;j<r->bytes;j++)reportBuffer[j]=r->report[j];
		if(fieldValue==NULL)PrintfExit("Couldn't allocate report array.");
		mxSetField(*outReports,i,"report",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		*mxGetPr(fieldValue)=(double)r->deviceIndex;
		mxSetField(*outReports,i,"device",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		*mxGetPr(fieldValue)=r->time;
		mxSetField(*outReports,i,"time",fieldValue);
		r=r->next;
	}
	if(deviceReportsPtr[deviceIndex]!=NULL){
		// transfer all these now-obsolete reports to the free list
		rTail->next=freeReportsPtr;
		freeReportsPtr=deviceReportsPtr[deviceIndex];
		deviceReportsPtr[deviceIndex]=NULL;
	}
	CountReports("GiveMeReports end.");
	return error;
}
boolean PsychAssignOutStructArray(	int position, 
                                        PsychArgRequirementType isRequired,   
                                        PsychGenericScriptType *pStruct)
{
    mxArray **mxArrayOut;
	PsychError matchError;
	Boolean putOut;
        
    PsychSetReceivedArgDescriptor(position, PsychArgOut);
    PsychSetSpecifiedArgDescriptor(position, PsychArgOut, PsychArgType_structArray, isRequired, 1,1,0,kPsychUnboundedArraySize,0,0);
	matchError=PsychMatchDescriptors();
	putOut=PsychAcceptOutputArgumentDecider(isRequired, matchError);
	if(putOut){
		mxArrayOut = PsychGetOutArgMxPtr(position);
        *mxArrayOut=pStruct;
	}
	return(putOut);
}
PsychError PSYCHHIDGiveMeReports(void) 
{
	long error=0;
	int deviceIndex;
	int reportBytes=1024;
	mxArray **outErr;
	pRecDevice device;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(2));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,false,&reportBytes);

	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GiveMeReports: Invalid device.\n");

	// reports
	error=GiveMeReports(deviceIndex,reportBytes); // PsychHIDReceiveReports.c
	
	// err
	outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent.
	if(outErr!=NULL){
		const char *fieldNames[]={"n", "name", "description"};
		mxArray *fieldValue;
		char *name="",*description="";

		PsychHIDErrors(error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDReceiveReports(void)
{
	long error=0;
	int deviceIndex;
	mxArray **mxErrPtr;
	const mxArray *mxOptions,*mx;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(2));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	/*
	 "\"options.print\" =1 (default 0) enables diagnostic printing of a summary of each report when our callback routine receives it. "	
	 "\"options.printCrashers\" =1 (default 0) enables diagnostic printing of the creation of the callback source and its addition to the CFRunLoop. "
	 "\"options.maxReports\" (default 10000) allocate space for at least this many reports, shared among all devices. "
	 "\"options.maxReportSize\" (default 64) allocate this many bytes per report. "
	 */
	//optionsPrintReportSummary=0;	// options.print
	//optionsPrintCrashers=0;		// options.printCrashers
	//optionsMaxReports=10000;		// options.maxReports
	//optionsMaxReportSize=64;		// options.maxReportSize
	//optionsSecs=0.010;			// options.secs
	mxOptions=PsychGetInArgMxPtr(2);
	if(mxOptions!=NULL){
		mx=mxGetField(mxOptions,0,"print");
		if(mx!=NULL)optionsPrintReportSummary=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"printCrashers");
		if(mx!=NULL)optionsPrintCrashers=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"maxReports");
		if(mx!=NULL)optionsMaxReports=(int)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"maxReportSize");
		if(mx!=NULL)optionsMaxReportSize=(int)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"secs");
		if(mx!=NULL)optionsSecs=mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"consistencyChecks");
		if(mx!=NULL)optionsConsistencyChecks=(psych_bool)mxGetScalar(mx);
	}
	if(optionsMaxReports>MAXREPORTS)printf("PsychHID ReceiveReports: Sorry, maxReports is fixed at %d.\n",(int)MAXREPORTS);
	if(optionsMaxReportSize>MAXREPORTSIZE)printf("PsychHID ReceiveReports: Sorry, maxReportSize is fixed at %d.\n",(int)MAXREPORTSIZE);

	error=ReceiveReports(deviceIndex);

	mxErrPtr=PsychGetOutArgMxPtr(1);
	if(mxErrPtr!=NULL){
		const char *fieldNames[]={"n", "name", "description"};
		char *name="",*description="";
		mxArray *fieldValue;

		PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available.
		*mxErrPtr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*mxErrPtr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDReceiveReports(void)
{
	long error=0;
	int deviceIndex;
	mxArray **mxErrPtr;
	const mxArray *mxOptions,*mx;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(2));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);

    if(deviceIndex < 0 || deviceIndex >= MAXDEVICEINDEXS) PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex, (int) MAXDEVICEINDEXS-1);
    
	/*
	 "\"options.print\" =1 (default 0) enables diagnostic printing of a summary of each report when our callback routine receives it. "	
	 "\"options.printCrashers\" =1 (default 0) enables diagnostic printing of the creation of the callback source and its addition to the CFRunLoop. "
	 "\"options.maxReports\" (default 10000) allocate space for at least this many reports, shared among all devices. "
	 "\"options.maxReportSize\" (default 65) allocate this many bytes per report. "
	 */
	//optionsPrintReportSummary=0;	// options.print
	//optionsPrintCrashers=0;		// options.printCrashers
	//optionsMaxReports=10000;		// options.maxReports
	//optionsMaxReportSize=65;		// options.maxReportSize
	//optionsSecs=0.010;			// options.secs
    
	mxOptions=PsychGetInArgMxPtr(2);
	if(mxOptions!=NULL){
		mx=mxGetField(mxOptions,0,"print");
		if(mx!=NULL)optionsPrintReportSummary=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"printCrashers");
		if(mx!=NULL)optionsPrintCrashers=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"secs");
		if(mx!=NULL)optionsSecs=mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"consistencyChecks");
		if(mx!=NULL)optionsConsistencyChecks=(psych_bool)mxGetScalar(mx);

        // Changing maxReports or maxReportSize triggers a reallocation of
        // buffer memory:
		mx=mxGetField(mxOptions,0,"maxReports");
		if(mx!=NULL) {
            oneShotRealloc = TRUE;
            optionsMaxReports = (int) mxGetScalar(mx);
        }

		mx=mxGetField(mxOptions,0,"maxReportSize");
		if(mx!=NULL) {
            oneShotRealloc = TRUE;
            optionsMaxReportSize = (int) mxGetScalar(mx);
        }
	}

    // Sanity check:
	if(optionsMaxReports < 1) PsychErrorExitMsg(PsychError_user, "PsychHID ReceiveReports: Sorry, requested maxReports count must be at least 1!");
	if(optionsMaxReportSize < 1) PsychErrorExitMsg(PsychError_user, "PsychHID ReceiveReports: Sorry, requested maxReportSize must be at least 1 byte!");
	if(optionsMaxReportSize > MAXREPORTSIZE) {
        printf("PsychHID ReceiveReports: Sorry, requested maximum report size %d bytes exceeds built-in maximum of %d bytes.\n", optionsMaxReportSize, (int) MAXREPORTSIZE);
        PsychErrorExitMsg(PsychError_user, "Invalid option.maxReportSize provided!");
    }
    
    // Start reception of reports: This will also allocate memory for the reports
    // on first invocation for this deviceIndex:
	error = ReceiveReports(deviceIndex);

	mxErrPtr=PsychGetOutArgMxPtr(1);
	if(mxErrPtr!=NULL){
		const char *fieldNames[]={"n", "name", "description"};
		char *name="",*description="";
		mxArray *fieldValue;

		PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available.
		*mxErrPtr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*mxErrPtr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDSetReport(void) 
{
	long error;
	pRecDevice device;
	int deviceIndex;
	int reportType; // kIOHIDReportTypeInput=0, kIOHIDReportTypeOutput=1, or kIOHIDReportTypeFeature=2
	int reportID;
	unsigned char *reportBuffer;
	int reportSize;
	const mxArray *report;
	mxArray **outErr;
	int i;
	
    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(4));
	outErr=PsychGetOutArgMxPtr(1);
	assert(outErr!=NULL);
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,TRUE,&reportType);
	PsychCopyInIntegerArg(3,TRUE,&reportID);
	report=PsychGetInArgMxPtr(4); 
	reportBuffer=(void *)mxGetPr(report);
	assert(reportBuffer!=NULL);
	switch(mxGetClassID(report)){
		case mxCHAR_CLASS:    
		case mxINT8_CLASS: 
		case mxUINT8_CLASS:   
		case mxINT16_CLASS:  
		case mxUINT16_CLASS:  
		case mxINT32_CLASS:  
		case mxUINT32_CLASS: 
			break;
		default:
			PrintfExit("\"report\" array must be char or integer (8-, 16-, or 32-bit).");
			break;
	}
	reportSize=mxGetElementSize(report)*mxGetNumberOfElements(report);
	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(reportSize>0 && reportID!=0) *reportBuffer=0xff&reportID; // copy low byte of reportID to first byte of report.
																 // Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3).
	assert(kIOHIDReportTypeInput==0 && kIOHIDReportTypeOutput==1 && kIOHIDReportTypeFeature==2);
	if(reportType==0){
		printf("SetReport(reportType %d, reportID %d, report ",reportType,reportID);
		for(i=0;i<reportSize;i++)printf("%d ",(int)reportBuffer[i]);
		printf(")\n");
		error=0;
	}else{
		if(1){
			IOHIDDeviceInterface122 **interface;
			interface=(IOHIDDeviceInterface122 **)(device->interface);
			if(interface)error=(*interface)->setReport(interface,reportType-1,reportID,reportBuffer,(UInt32)reportSize,50,NULL,NULL,NULL);
			else PrintfExit("PsychHID SetReport: Bad interface.\n");
		}else error=HIDSetReport(device,reportType-1,reportID,reportBuffer,(UInt32)reportSize);
	}
	if(reportID==0x11){
		PsychGetPrecisionTimerSeconds(&AInScanStart);
	}
	//if(error)printf("Warning: PsychHID: HIDSetReport error %ld (0x%lx).",error,error);
	if(0){
		*outErr=mxCreateDoubleMatrix(1,1,mxREAL);
		if(*outErr==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(*outErr)=(double)error;
	}else{
		const char *fieldNames[]={"n", "name", "description"};
		char *name="",*description="";
		mxArray *fieldValue;
		
		PsychHIDErrors(error,&name,&description);
		*outErr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
	}		
    return(PsychError_none);	
}
PsychError PSYCHHIDGetReport(void) 
{
	long error=0;
	pRecDevice device;
	int deviceIndex;
	int reportType; // 1=input, 2=output, 3=feature
	unsigned char *reportBuffer;
	UInt32 reportBytes=0;
	int reportBufferSize=0;
	int reportID=0;
	long dims[]={1,1};
	mxArray **outReport,**outErr;
	char *name="",*description="",string[256];
	IOHIDDeviceInterface122** interface=NULL;
	boolean reportAvailable;
	double reportTime;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(4));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,TRUE,&reportType);
	PsychCopyInIntegerArg(3,TRUE,&reportID);
	PsychCopyInIntegerArg(4,TRUE,&reportBufferSize);
	outReport=PsychGetOutArgMxPtr(1); 
	outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent.
	dims[0]=1;
	dims[1]=reportBufferSize;
	*outReport=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL);
	if(*outReport==NULL)PrintfExit("Couldn't allocate report array.");
	reportBuffer=(void *)mxGetData(*outReport);
	if(reportBuffer==NULL)PrintfExit("Couldn't allocate report buffer.");
	reportBytes=reportBufferSize;
	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GetReport: Invalid device.\n");
	interface=device->interface;
	if(interface==NULL)PrintfExit("PsychHID:GetReport: No interface for device.\n");
	if(reportType==0){
		printf("GetReport(reportType %d, reportID %d, reportBytes %d)\n",reportType,reportID,(int)reportBytes);
	}else{
		// Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3).
		if(0){
			// HIDGetReport
			error=HIDGetReport(device,reportType-1,reportID,reportBuffer,&reportBytes);
		}
		if(0){
			// getReport
			error=(*interface)->getReport(interface,reportType-1,reportID,reportBuffer,&reportBytes,-1,nil,nil,nil);
		}
		if(0){
			// handleReport
		}
		if(1){
			// using setInterruptReportHandlerCallback and CFRunLoopRunInMode
			error=ReceiveReports(deviceIndex);
			error=GiveMeReport(deviceIndex,&reportAvailable,reportBuffer,&reportBytes,&reportTime);
		}else{
			// using getReport
			if(error==0)reportAvailable=1;
			PsychGetPrecisionTimerSeconds(&reportTime);
		}
	}
	if(outReport==NULL)PrintfExit("Output argument is required."); // I think MATLAB always provides this.
	if(error==0 && reportBytes>reportBufferSize){
		error=2;
		name="Warning";
		description=string;
		sprintf(description,"GetReport overflow. Expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes); 
	}
	if(error==0 && reportBytes<reportBufferSize){
		// Reduce the declared size of the array to that of the received report.
		if(reportBytes>0)error=3;
		name="Warning";
		description=string;
		sprintf(description,"GetReport expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes);
		mxSetN(*outReport,reportBytes);
	}
	if(outErr!=NULL){
		const char *fieldNames[]={"n", "name", "description", "reportLength", "reportTime"};
		mxArray *fieldValue;

		PsychHIDErrors(error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,5,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		if(reportAvailable)*mxGetPr(fieldValue)=(double)reportBytes;
		else *mxGetPr(fieldValue)=-1.0;
		mxSetField(*outErr,0,"reportLength",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=reportTime;
		mxSetField(*outErr,0,"reportTime",fieldValue);
	}
    return(PsychError_none);	
}