/*	SonyLDP1450.m  
	15 February 1993
	Eric Celeste / AppTech / 5 Exeter Street / Belmont, MA 02178
	efc@mit.edu
	Please send me any improvements.
	
	For example:
		player = [[SonyLDP1450 alloc] initDevice:"/dev/ttya" baud:B9600];
		...
		[player index:YES];
		[player searchForFrame:400];
		[player playForward];
		sleep(40);
		[player still];
		...
		[player free];
	
	See LDPlayer for a note about reporting errors to the outside world.
	
	I do not have Sony's documention for its LDP interface yet (the 
	Interface Manual's part number is LDM-G1000, in case you care), so
	much of what follows could easily be wrong. I did my best based on 
	my (possibly much flawed) reading of source code from David Small.
	In particular, I am not sure I have done the right thing with ack 
	(maybe each command generates two ack characters, one when the 
	command is received and one when the player is ready for the next 
	command, see SonyLDPcodes.h for this hint), but it seems to work.
	
	I also think it should be possible to search for a CLV frame, but
	this object does not seem to make that possible. Maybe I should send
	a shorter (4 byte?) frame number when searching a CLV disk?
	
	Anyway, for the most part, this works. It is certainly in good enough
	shape to help others find their way around a laserdisc player.
	
	Enjoy! ...Eric
	
    ////////////////////////////////////////// History
	930210	efc  first adapted from David Small's Ultrix source code
			     and used Michael Hawley's SerialPort object
	930212	efc  added chapter searching
	930213	efc  made this a subclass of generic LDPlayer
	930214	efc  added completion and fixed search & repeat
	930215	efc  switched to delegation method of error reporting
*/
#import "SonyLDP1450.h"
#import "SonyLDPcodes.h"

#define NOTHING 0x00

@implementation SonyLDP1450
////////////////////////////////////////////// Private Functions
const char *commandString(char  cmd)
 {
 	char tempStr[1000];
	
	switch (cmd) {
	case NOTHING:
		return ("nothing");
	case LDP_ACK: 
		return ("Ack");
	case LDP_NACK: 
		return ("Nack");
	case LDP_COMPLETION: 
		return ("Completion");
	case LDP_ERROR: 
		return ("Error");
	case LDP_LID_OPEN: 
		return ("Lid Open");
	case LDP_NO_FRAME:
		return ("No Frame");
	case LDP_NOT_TARGET:
		return ("Not Target");
	case LDP_CHAPTER_MODE: 
		return ("Chapter Mode");
	case LDP_CHAPTER_INQ: 
		return ("Chapter Inq");
	case LDP_ADDR_INQ: 
		return ("Address Inq");
	case LDP_FRAME_MODE: 
		return ("Frame Mode");
	case LDP_AUDIO_MUTE_OFF: 
		return ("Mute Off");
	case LDP_AUDIO_MUTE_ON: 
		return ("Mute On");
	case LDP_CH1_ON: 
		return ("Channel 1 On");
	case LDP_CH1_OFF: 
		return ("Channel 1 Off");
	case LDP_CH2_ON: 
		return ("Channel 2 On");
	case LDP_CH2_OFF: 
		return ("Channel 2 Off");
	case LDP_CX_ON: 
		return ("Noise Reduction On");
	case LDP_CX_OFF: 
		return ("Noise Reduction Off");
	case LDP_RESET: 
		return ("Reset");
	case LDP_EJECT: 
		return ("Eject");
	case LDP_EJECT_ENABLE: 
		return ("Eject Enable");
	case LDP_EJECT_DISABLE: 
		return ("Eject Disable");
	case LDP_SCAN_F: 
		return ("Forw Scan");
	case LDP_SCAN_R: 
		return ("Rev Scan");
	case LDP_FAST_F: 
		return ("Forw Fast");
	case LDP_FAST_R: 
		return ("Rev Fast");
	case LDP_PLAY_F: 
		return ("Forw Play");
	case LDP_PLAY_R: 
		return ("Rev Play");
	case LDP_SLOW_F: 
		return ("Forw Slow");
	case LDP_SLOW_R: 
		return ("Rev Slow");
	case LDP_ONE_FRAME_F: 
		return ("Forw Frame");
	case LDP_ONE_FRAME_R: 
		return ("Rev Frame");
	case LDP_INDEX_ON: 
		return ("Index On");
	case LDP_INDEX_OFF: 
		return ("Index Off");
	case LDP_MEMORY: 
		return ("Memory Set");
	case LDP_M_SEARCH: 
		return ("Memory Search");
	case LDP_MOTOR_ON: 
		return ("Motor On");
	case LDP_MOTOR_OFF: 
		return ("Motor Off");
	case LDP_STOP: 
		return ("Stop");
	case LDP_STILL: 
		return ("Still");
	case LDP_SEARCH: 
		return ("Search");
	case LDP_ENTER: 
		return ("Enter");
	case LDP_REPEAT: 
		return ("Repeat");
	case LDP_STATUS_INQ: 
		return ("Status Inq");
	default: 
		sprintf (tempStr, "0x%.2x", (unsigned int)cmd);
		return (tempStr);
	}  
 }
 
////////////////////////////////////////////// Private Methods
- waitFor:(char)response
 {
	char c;
	char trouble[500];
	
	c = [port getc];
	
	if (c != response) {
		sprintf (
			trouble, 
			"%s expected %s, got %s",
			commandString(lastCommand),
			commandString(response),
			commandString(c)
		); 
		[self reportProblem:trouble];
	}
	return self;
 }

- command:(char) cmd
 {
 	while ([port hasInput]) [self waitFor:NOTHING];
	[port putc:cmd];
	lastCommand = cmd;
	[self waitFor:LDP_ACK];
	return self;
 }

- frame:(int)frame
 {
	int i, digit;
	int div;
	
	allOK = YES;
	
	div = 10000;
	for (i = 0; i < 5; i++) {
		if (frame >= div) {
			digit = frame / div;
			frame = frame - (digit * div);
		} else {
			digit = 0;
		}
		div = div / 10;
		[self command:digit + 0x30];
	}
	if(allOK) {
		[self command:LDP_ENTER];
	}
 	return self;
 }

////////////////////////////////////////////// Housekeeping Methods
- initDevice:(const char *)device baud:(int)rate
 {
 	self = [super initDevice:device baud:rate];
	if (self) {
		lastCommand = '\0';
	}
	return self;
 }
 
////////////////////////////////////////////// Control Methods
- searchForChapter:(int)chapter
 {
 	allOK = YES;

	[self command:LDP_CHAPTER_MODE];
    if (allOK) {
		[self command:LDP_SEARCH];
		if (allOK){
			[self frame:chapter];
			if (allOK){
				[self waitFor:LDP_COMPLETION];
			}
		}
		[self command:LDP_FRAME_MODE];
	}
	return self;
 }
 
- searchForFrame:(int)frame;
 {
 	allOK = YES;
	
    [self command:LDP_SEARCH];
	if (allOK) {
		[self frame:frame];
		if (allOK) {
			[self waitFor:LDP_COMPLETION];
		}
	}
	return self;
 }

- still
 {
    return [self command:LDP_STILL];
 }

- halt
 {
    return [self command:LDP_STOP];
 }

- eject
 {
    return [self command:LDP_EJECT];
 }

- playForward
 {
    return [self command:LDP_PLAY_F];
 }

- playBack
 {
    return [self command:LDP_PLAY_R];
 }

- fastForward
 {
    return [self command:LDP_FAST_F];
 }

- fastBack
 {
    return [self command:LDP_FAST_R];
 }

- slowForward
 {
    return [self command:LDP_SLOW_F];
 }

- slowBack
 {
    return [self command:LDP_SLOW_R];
 }

- scanForward
 {
  
    return [self command:LDP_SCAN_F];
 }

- scanBack
 {
    return [self command:LDP_SCAN_R];
 }


- frameForward
 {
    return [self command:LDP_ONE_FRAME_F];
 }

- frameBack
 {
    return [self command:LDP_ONE_FRAME_R];
 }

- playFrom:(int)inFrame to:(int)outFrame repeating:(int)times;
 {
	char timecmd;
	
	allOK = YES;
	
    [self searchForFrame:inFrame];
	if (allOK) {
		[self command:LDP_REPEAT];
		if (allOK) {    
			[self frame:outFrame];
			if (allOK) {
				if (times > 9) times = 1;
				timecmd = 0x30 + times;
				[self command:timecmd];
				if (allOK) {
					[self command:LDP_ENTER];
					if (allOK) {
						[self waitFor:LDP_COMPLETION];
					}
				}
			}
		}
	}
    return self;
 }

- motor:(BOOL)on
 {
 	allOK = YES;
	
 	if (on) {
		[self command:LDP_MOTOR_ON];
		if (allOK) {
			NXRunAlertPanel(
				[NXApp appName],
				"Please wait for the motor to spin up",
				"OK",NULL, NULL
			);
			[self waitFor:LDP_ACK];
		}
	} else {
		[self command:LDP_MOTOR_OFF];
		if (allOK) {
			NXRunAlertPanel(
				[NXApp appName],
				"Please wait for the motor to spin down",
				"OK",NULL, NULL
			);
			[self waitFor:LDP_ACK];
		}
	}
	return self;
 }

- index:(BOOL)on
 {
 	if (on) {
		[self command:LDP_INDEX_ON];
	} else {
		[self command:LDP_INDEX_OFF];
	}
	return self;
 }
 
- channelOne:(BOOL)on
 {
 	if (on) {
		[self command:LDP_CH1_ON];
	} else {
		[self command:LDP_CH1_OFF];
	}
	return self;
 }
 
- channelTwo:(BOOL)on
 {
 	if (on) {
		[self command:LDP_CH2_ON];
	} else {
		[self command:LDP_CH2_OFF];
	}
	return self;
 }
 
- mute:(BOOL)on
 {
 	if (on) {
		[self command:LDP_AUDIO_MUTE_ON];
	} else {
		[self command:LDP_AUDIO_MUTE_OFF];
	}
	return self;
 }
 
- noiseReduction:(BOOL)on
 {
 	if (on) {
		[self command:LDP_CX_ON];
	} else {
		[self command:LDP_CX_OFF];
	}
	return self;
 }

- clearAll
 {
    return [self command:LDP_RESET];
	/* but we may still need to clear out a queue of waiting responses */
 	while ([port hasInput]) [self waitFor:NOTHING];
	return self;
 }

////////////////////////////////////////////// Inquiry Methods
- (int)currentFrame
 {
	int i;
	char str[10];

	[port putc:LDP_ADDR_INQ];
  
    for (i = 0; i < 5; i++) {
		str[i] = [port getc];
    }
    str[i] = '\0';
    return (atoi(str));
 }

@end
