//
//  OV534Driver.m
//  macam
//
//  Created by Harald on 1/10/08.
//  Copyright 2008 hxr. All rights reserved.
//

//
// Many thanks to Jim Paris at PS2 developer forums
//


#import "OV534Driver.h"


//  SCCB/sensorinterface

#define EYEREG_SCCB_ADDRESS   0xf1   /* ? */
#define EYEREG_SCCB_SUBADDR   0xf2
#define EYEREG_SCCB_WRITE     0xf3
#define EYEREG_SCCB_READ      0xf4
#define EYEREG_SCCB_OPERATION 0xf5
#define EYEREG_SCCB_STATUS    0xf6

#define EYE_SCCB_OP_WRITE_3 0x37
#define EYE_SCCB_OP_WRITE_2 0x33
#define EYE_SCCB_OP_READ_2  0xf9

@interface OV534Driver (Private)

- (void) initCamera;

@end


@implementation OV534Driver

+ (NSArray *) cameraUsbDescriptions 
{
    return [NSArray arrayWithObjects:
        
        [NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithUnsignedShort:0x3002], @"idProduct",
            [NSNumber numberWithUnsignedShort:0x06f8], @"idVendor",
            @"Hercules Blog Webcam", @"name", NULL], 
        
        [NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithUnsignedShort:0x3003], @"idProduct", 
            [NSNumber numberWithUnsignedShort:0x06f8], @"idVendor", 
            @"Hercules Dualpix HD Webcam", @"name", NULL],
        
        NULL];
}

//
// Initialize the driver
//
- (id) initWithCentral: (id) c 
{
	self = [super initWithCentral:c];
	if (self == NULL) 
        return NULL;
    
    LUT = [[LookUpTable alloc] init];
	if (LUT == NULL) 
        return NULL;
    
    [LUT setDefaultOrientation:NormalOrientation];
    
    driverType = isochronousDriver;
    
    decodingSkipBytes = 0;
    
    compressionType = proprietaryCompression;
    
	return self;
}


- (void) startupCamera
{
    [self initCamera];
    
	[self setBrightness:0.5];
	[self setContrast:0.5];
	[self setGamma:0.5];
	[self setSaturation:0.5];
	[self setHue:0.5];
	[self setSharpness:0.5];
	
    [super startupCamera];

}



- (BOOL) supportsResolution: (CameraResolution) res fps: (short) rate 
{
    switch (res) 
    {
        case ResolutionVGA:
            if (rate > 30) 
                return NO;
            return YES;
            break;
            
        case ResolutionSIF:
            if (rate > 120) 
                return NO;
            return YES; // Working now! 
            break;
            
        default: 
            return NO;
    }
}


- (CameraResolution) defaultResolutionAndRate: (short *) rate
{
	if (rate) 
        *rate = 90;
    
	return ResolutionSIF;
}

- (BOOL) canSetLed
{
    return YES;
}

- (void) setLed:(BOOL) v
{
    [self setRegister:0x21 toValue:0x80 withMask:0x80];
    [self setRegister:0x23 toValue:(v ? 0x80 : 0x00) withMask:0x80];
 
    [super setLed:v];
}

- (BOOL) isUVC{
	return YES;
}


- (BOOL) canSetFlicker{
	return YES;
}

- (void) setFlicker:(FlickerType)fType{
	if( fType == disableFlicker){
	
	}else if ( fType == enableFlicker50Hz){
		[self setSensorRegister:0x2b toValue:0x9e]; //flicker 50hz - http://www.invisible.ca/~jmcneill/outgoing/7720Rev2.set
	}else if ( fType == enableFlicker60Hz){
		[self setSensorRegister:0x2b toValue:0x00]; //flicker 60hz - http://www.invisible.ca/~jmcneill/outgoing/7720Rev2.set
	}

}


- (BOOL) canSetHue { return YES;  }
- (void) setHue:(float)v {
	
	float val = v * 255.0;
	if( val > 255) val = 255;
	if(val < 0) val = 0;
	unsigned char uval = (int)val;

	[self setSensorRegister:0x01 toValue:uval]; //hue - I think - http://www.invisible.ca/~jmcneill/outgoing/7720Rev2.set
	
    [super setHue:v];
}

- (BOOL) canSetGain { return YES; }
- (void) setGain:(float)v {
	
	//according to http://www.invisible.ca/~jmcneill/outgoing/7720Rev2.set		
	//seven steps for gain
		
//	HKR,%7720ComboGain%\0000, CamRegisters,1,00,00,ff
//	HKR,%7720ComboGain%\0001, CamRegisters,1,00,04,ff
//	HKR,%7720ComboGain%\0002, CamRegisters,1,00,08,ff
//	HKR,%7720ComboGain%\0003, CamRegisters,1,00,0a,ff
//	HKR,%7720ComboGain%\0004, CamRegisters,1,00,0f,ff
//	HKR,%7720ComboGain%\0005, CamRegisters,1,00,14,ff
//	HKR,%7720ComboGain%\0006, CamRegisters,1,00,18,ff
//	HKR,%7720ComboGain%\0007, CamRegisters,1,00,1f,ff	
		
	//	31
	//	24
	//	20
	//	15
	//	10
	//	8
	//	4
	//	0
	
	unsigned char gainVal[8] = {0, 4, 8, 10, 15, 20, 24, 31};
	
	float val = v * 7.99;
	if( val >= 8) val = 7;
	if(val < 0) val = 0;
	unsigned char uval = gainVal[ (int)val ];

	[self setSensorRegister:0x00 toValue:uval];
    [super setGain:v];
}


- (BOOL) canSetShutter { return YES; }
- (void) setShutter:(float)v {
	
	float val = v * 255.0;
	if( val > 255) val = 255;
	if(val < 0) val = 0;
	unsigned char uval = (int)val;

	[self setSensorRegister:0x10 toValue:uval]; //shutter speed - http://www.invisible.ca/~jmcneill/outgoing/7720Rev2.set
	
    [super setGain:v];
}


// Gain and shutter combined
- (BOOL) canSetAutoGain 
{
    return YES;
}

- (void) setAutoGain:(BOOL) v
{
	if( v ){
		[self setSensorRegister:0x13 toValue:0x05 withMask:0x05]; // from 0x05 enables it
	}else{
		[self setSensorRegister:0x13 toValue:0x00 withMask:0x05]; //0x00 disables auto gain
	}

    [super setAutoGain:v];    
}


//
// Set up some unusual defaults
//
- (void) setIsocFrameFunctions
{
	if( [self width] == 320 ) grabContext.chunkBufferLength = 4 * [self width] * [self height];	//theo changed from  2 * [self width] * [self height];
    else grabContext.chunkBufferLength = 2 * [self width] * [self height];	
	
	grabContext.numberOfChunkBuffers = 2;  // Must be at least 2
    grabContext.numberOfTransfers = 3;  // Must be at least 3 for the PS3 Eye!
}

//
// This is the key method that starts up the stream
//
- (BOOL) startupGrabStream 
{
    videoBulkReadsPending = 0;
    
    [self setRegister:0xe0 toValue:0x00];
	
	if( [self width] == 320 ){
		[self setSensorRegister:0x29 toValue:0x50]; //theo changed from 0xa0 to 0x50 - see http://forums.ps2dev.org/viewtopic.php?p=74541#74541
		[self setSensorRegister:0x2c toValue:0x78]; //theo changed from 0xf0 to 0x78
		[self setSensorRegister:0x65 toValue:0x2f]; //theo changed from 0x20 to 0x2f - see http://forums.ps2dev.org/viewtopic.php?p=74541#74541
	}else{
		[self setSensorRegister:0x29 toValue:0xa0]; 
		[self setSensorRegister:0x2c toValue:0xf0];
		[self setSensorRegister:0x65 toValue:0x20];
	}	

	[self setAutoGain:NO];
	
	[self setGain:0.6];
	[self setShutter:0.6];	
	
	[self setLed:YES];	
	
    return YES;
}

//
// The key routine for shutting down the stream
//
- (void) shutdownGrabStream 
{	
	[self setLed:NO];
    [self setRegister:0xe0 toValue:0x09];
}


int clip(int x)
{
	if (x < 0) x = 0;
	if (x > 255) x = 255;
	return x;
}


void yuv_to_rgb(UInt8 y, UInt8 u, UInt8 v, UInt8 * r, UInt8 * g, UInt8 * b)
{
	int c = y - 16;
	int d = u - 128;
	int e = v - 128;
    
	*r = clip((298 * c           + 409 * e + 128) >> 8);
	*g = clip((298 * c - 100 * d - 208 * e + 128) >> 8);
	*b = clip((298 * c + 516 * d           + 128) >> 8);
}


//
// Return YES if everything is OK
//
- (BOOL) decodeBufferProprietary: (GenericChunkBuffer *) buffer
{
	short rawWidth  = [self width];
	short rawHeight = [self height];
 
	// Decode the bytes
    
    UInt8 * ptr = buffer->buffer;
    
    int R = 0;
    int G = 1;
    int B = 2;
    
    int row, column;
    
    if (buffer->numBytes < (grabContext.chunkBufferLength - 4)) 
        return NO;  // Skip this chunk
    
	if( rawWidth == 640 ){
	
		for (row = 0; row < rawHeight; row++) 
		{
		
			UInt8 * out = nextImageBuffer + row * nextImageBufferRowBytes;
			
			for (column = 0; column < rawWidth; column += 2) 
			{
				int y1 = *ptr++;
				int u  = *ptr++;
				int y2 = *ptr++;
				int v  = *ptr++;
				
				yuv_to_rgb(y1, u, v, out + R, out + G, out + B);
				out += nextImageBufferBPP;
				
				yuv_to_rgb(y2, u, v, out + R, out + G, out + B);
				out += nextImageBufferBPP;
			}
			
		}	
	
	}else{
		
		int rowOut = 0;
		for (row = 0; row < rawHeight*2; row++) 
		{
			if( row % 2 != 0){
				ptr += rawWidth * 2;
				continue;
			}
		
			UInt8 * out = nextImageBuffer + rowOut * nextImageBufferRowBytes;
			
			for (column = 0; column < rawWidth; column += 2) 
			{
				int y1 = *ptr++;
				int u  = *ptr++;
				int y2 = *ptr++;
				int v  = *ptr++;
				
				yuv_to_rgb(y1, u, v, out + R, out + G, out + B);
				out += nextImageBufferBPP;
				
				yuv_to_rgb(y2, u, v, out + R, out + G, out + B);
				out += nextImageBufferBPP;
			}
			
			rowOut++;
		}
	
	}
    
    [LUT processImage:nextImageBuffer numRows:rawHeight rowBytes:nextImageBufferRowBytes bpp:nextImageBufferBPP];
    
    return YES;
}


- (int) getRegister:(UInt16)reg
{
    UInt8 buffer[8];
    
    BOOL ok = [self usbReadCmdWithBRequest:0x01 wValue:0x0000 wIndex:reg buf:buffer len:1];
    
    return (ok) ? buffer[0] : -1;
}


- (int) setRegister:(UInt16)reg toValue:(UInt16)val
{
    UInt8 buffer[8];
    
    buffer[0] = val;
    
    BOOL ok = [self usbWriteCmdWithBRequest:0x01 wValue:0x0000 wIndex:reg buf:buffer len:1];
    
    return (ok) ? val : -1;
}


- (int) verifySetRegister:(UInt16)reg toValue:(UInt8)val
{
    int verify;
    
    if ([self setRegister:reg toValue:val] < 0) 
    {
        printf("OV534Driver:verifySetRegister:setRegister failed\n");
        return -1;
    }
    
    verify = [self getRegister:reg];
    
    if (val != verify) 
    {
        printf("OV534Driver:verifySetRegister:getRegister returns something unexpected! (0x%04x != 0x%04x)\n", val, verify);
    }
    
    return verify;
}


- (void) initSCCB
{
    [self verifySetRegister:0xe7 toValue:0x3a];
    
    [self setRegister:EYEREG_SCCB_ADDRESS toValue:0x60];
    [self setRegister:EYEREG_SCCB_ADDRESS toValue:0x60];
    [self setRegister:EYEREG_SCCB_ADDRESS toValue:0x60];
    [self setRegister:EYEREG_SCCB_ADDRESS toValue:0x42];
}

//
// Is SCCB OK? Return YES if OK
//
- (BOOL) sccbStatusOK
{
#define SCCB_RETRY 5
    
    int try = 0;
    UInt8 ret;
    
    for (try = 0; try < SCCB_RETRY; try++) 
    {
        ret = [self getRegister:EYEREG_SCCB_STATUS];
        
        if (ret == 0x00) 
            return YES;
        
        if (ret == 0x04) 
            return NO;
        
        if (ret != 0x03) 
            printf("OV534Driver:sccbStatus is 0x%02x, attempt %d (of %d)\n", ret, try + 1, SCCB_RETRY);
    }
    
    return NO;
}


- (int) getSensorRegister:(UInt8)reg
{
    if ([self setRegister:EYEREG_SCCB_SUBADDR toValue:reg] < 0) 
        return -1;
    
    if ([self setRegister:EYEREG_SCCB_OPERATION toValue:EYE_SCCB_OP_WRITE_2] < 0) 
        return -1;
    
    if (![self sccbStatusOK]) 
    {
        printf("OV534Driver:getSensorRegister:SCCB not OK (1)\n");
        return -1;
    }
    
    if ([self setRegister:EYEREG_SCCB_OPERATION toValue:EYE_SCCB_OP_READ_2] < 0) 
        return -1;
    
    if (![self sccbStatusOK]) 
    {
        printf("OV534Driver:getSensorRegister:SCCB not OK (2)\n");
        return -1;
    }
    
    return [self getRegister:EYEREG_SCCB_READ];
}


- (int) setSensorRegister:(UInt8)reg toValue:(UInt8)val
{
    if ([self setRegister:EYEREG_SCCB_SUBADDR toValue:reg] < 0) 
        return -1;
    
    if ([self setRegister:EYEREG_SCCB_WRITE toValue:val] < 0) 
        return -1;
    
    if ([self setRegister:EYEREG_SCCB_OPERATION toValue:EYE_SCCB_OP_WRITE_3] < 0) 
        return -1;
    
    return ([self sccbStatusOK]) ? val : -1;
}


- (void) initCamera
{
    [self verifySetRegister:0xe7 toValue:0x3a];
    [self setRegister:0xf1 toValue:0x60];
    [self setRegister:0xf1 toValue:0x60];
    [self setRegister:0xf1 toValue:0x60];
    [self setRegister:0xf1 toValue:0x42];
    
    [self verifySetRegister:0xc2 toValue:0x0c];
    [self verifySetRegister:0x88 toValue:0xf8];
    [self verifySetRegister:0xc3 toValue:0x69];
    [self verifySetRegister:0x89 toValue:0xff];
    [self verifySetRegister:0x76 toValue:0x03];
    [self verifySetRegister:0x92 toValue:0x01];
    [self verifySetRegister:0x93 toValue:0x18];
    [self verifySetRegister:0x94 toValue:0x10];
    [self verifySetRegister:0x95 toValue:0x10];
    [self verifySetRegister:0xe2 toValue:0x00];
    [self verifySetRegister:0xe7 toValue:0x3e];
    
    [self setRegister:0x1c toValue:0x0a];
    [self setRegister:0x1d toValue:0x22];
    [self setRegister:0x1d toValue:0x06];
    [self verifySetRegister:0x96 toValue:0x00];
    
    [self setRegister:0x97 toValue:0x20];
    [self setRegister:0x97 toValue:0x20];
    [self setRegister:0x97 toValue:0x20];
    [self setRegister:0x97 toValue:0x0a];
    [self setRegister:0x97 toValue:0x3f];
    [self setRegister:0x97 toValue:0x4a];
    [self setRegister:0x97 toValue:0x20];
    [self setRegister:0x97 toValue:0x15];
    [self setRegister:0x97 toValue:0x0b];
    
    [self verifySetRegister:0x8e toValue:0x40];
    [self verifySetRegister:0x1f toValue:0x81];
    [self verifySetRegister:0x34 toValue:0x05];
    [self verifySetRegister:0xe3 toValue:0x04];
    [self verifySetRegister:0x88 toValue:0x00];
    [self verifySetRegister:0x89 toValue:0x00];
    [self verifySetRegister:0x76 toValue:0x00];
    [self verifySetRegister:0xe7 toValue:0x2e];
    [self verifySetRegister:0x31 toValue:0xf9];
    [self verifySetRegister:0x25 toValue:0x42];
    [self verifySetRegister:0x21 toValue:0xf0];
    
    [self setRegister:0x1c toValue:0x00];
    [self setRegister:0x1d toValue:0x40];
    [self setRegister:0x1d toValue:0x02];
    [self setRegister:0x1d toValue:0x00];
    [self setRegister:0x1d toValue:0x02];
    [self setRegister:0x1d toValue:0x57];
    [self setRegister:0x1d toValue:0xff];
    
    [self verifySetRegister:0x8d toValue:0x1c];
    [self verifySetRegister:0x8e toValue:0x80];
    [self verifySetRegister:0xe5 toValue:0x04];
    
    [self setSensorRegister:0x12 toValue:0x80];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x11 toValue:0x01];
    
    [self setSensorRegister:0x3d toValue:0x03];
    [self setSensorRegister:0x17 toValue:0x26];
    [self setSensorRegister:0x18 toValue:0xa0];
    [self setSensorRegister:0x19 toValue:0x07];
    [self setSensorRegister:0x1a toValue:0xf0];
    [self setSensorRegister:0x32 toValue:0x00];
	
	[self setSensorRegister:0x29 toValue:0x50]; //theo changed from 0xa0 to 0x50 - see http://forums.ps2dev.org/viewtopic.php?p=74541#74541
	[self setSensorRegister:0x2c toValue:0x78]; //theo changed from 0xf0 to 0x78
	[self setSensorRegister:0x65 toValue:0x2f]; //theo changed from 0x20 to 0x2f - see http://forums.ps2dev.org/viewtopic.php?p=74541#74541

    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x42 toValue:0x7f];
    [self setSensorRegister:0x63 toValue:0xe0];
    [self setSensorRegister:0x64 toValue:0xff];
    [self setSensorRegister:0x66 toValue:0x00];
    [self setSensorRegister:0x13 toValue:0xF0];  // auto gain  - 0x00 disables it 0xFF or 0xF0 enables it - also seems to effect color balance
    [self setSensorRegister:0x0d toValue:0x41];
    [self setSensorRegister:0x0f toValue:0xc5];
    [self setSensorRegister:0x14 toValue:0x11];
	
    [self setSensorRegister:0x22 toValue:0x7f];
    [self setSensorRegister:0x23 toValue:0x03];
    [self setSensorRegister:0x24 toValue:0x40];
    [self setSensorRegister:0x25 toValue:0x30];
    [self setSensorRegister:0x26 toValue:0xa1];
    [self setSensorRegister:0x2a toValue:0x00];
    [self setSensorRegister:0x2b toValue:0x00];
    [self setSensorRegister:0x6b toValue:0xaa];
    //[self setSensorRegister:0x13 toValue:0xFF]; // auto gain :)  - 0x00 disables it 0xFF or 0xF0 enables it
    //[self setSensorRegister:0x00 toValue:0x59]; //this is the gain amount - see http://www.invisible.ca/~jmcneill/outgoing/7720Rev2.set

    [self setSensorRegister:0x90 toValue:0x05];
    [self setSensorRegister:0x91 toValue:0x01];
    [self setSensorRegister:0x92 toValue:0x03];
    [self setSensorRegister:0x93 toValue:0x00];
    [self setSensorRegister:0x94 toValue:0x60];
    [self setSensorRegister:0x95 toValue:0x3c];
    [self setSensorRegister:0x96 toValue:0x24];
    [self setSensorRegister:0x97 toValue:0x1e];
    [self setSensorRegister:0x98 toValue:0x62];
    [self setSensorRegister:0x99 toValue:0x80];
    [self setSensorRegister:0x9a toValue:0x1e];
    [self setSensorRegister:0x9b toValue:0x08];
    [self setSensorRegister:0x9c toValue:0x20];
    [self setSensorRegister:0x9e toValue:0x81];
    
    [self setSensorRegister:0xa6 toValue:0x04];
    [self setSensorRegister:0x7e toValue:0x0c];
    [self setSensorRegister:0x7f toValue:0x16];
    
    [self setSensorRegister:0x80 toValue:0x2a];
    [self setSensorRegister:0x81 toValue:0x4e];
    [self setSensorRegister:0x82 toValue:0x61];
    [self setSensorRegister:0x83 toValue:0x6f];
    [self setSensorRegister:0x84 toValue:0x7b];
    [self setSensorRegister:0x85 toValue:0x86];
    [self setSensorRegister:0x86 toValue:0x8e];
    [self setSensorRegister:0x87 toValue:0x97];
    [self setSensorRegister:0x88 toValue:0xa4];
    [self setSensorRegister:0x89 toValue:0xaf];
    [self setSensorRegister:0x8a toValue:0xc5];
    [self setSensorRegister:0x8b toValue:0xd7];
    [self setSensorRegister:0x8c toValue:0xe8];
    [self setSensorRegister:0x8d toValue:0x20];
    
    [self setSensorRegister:0x0c toValue:0x90];
    
    [self verifySetRegister:0xc0 toValue:0x50];
    [self verifySetRegister:0xc1 toValue:0x3c];
    [self verifySetRegister:0xc2 toValue:0x0c];
    
    [self setSensorRegister:0x2b toValue:0x00];
    [self setSensorRegister:0x22 toValue:0x7f];
    [self setSensorRegister:0x23 toValue:0x03];
    [self setSensorRegister:0x11 toValue:0x01];
    [self setSensorRegister:0x0c toValue:0xd0];
    [self setSensorRegister:0x64 toValue:0xff];
    [self setSensorRegister:0x0d toValue:0x41];
    
    [self setSensorRegister:0x14 toValue:0x41];
    [self setSensorRegister:0x0e toValue:0xcd];
    [self setSensorRegister:0xac toValue:0xbf];
    [self setSensorRegister:0x8e toValue:0x00];
    [self setSensorRegister:0x0c toValue:0xd0];
    
    [self setRegister:0xe0 toValue:0x09];
//  [self setRegister:0xe0 toValue:0x00];

}

@end


@implementation OV538Driver

+ (NSArray *) cameraUsbDescriptions 
{
    return [NSArray arrayWithObjects:
        
        [NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithUnsignedShort:0x2000], @"idProduct", 
            [NSNumber numberWithUnsignedShort:0x1415], @"idVendor", 
            @"Sony HD Eye for PS3 (SLEH 00201)", @"name", NULL],
        
        NULL];
}

//
// Initialize the driver
//
- (id) initWithCentral: (id) c 
{
	self = [super initWithCentral:c];
	if (self == NULL) 
        return NULL;
    
    driverType = bulkDriver;
   
	return self;
}

@end
