//
// VISCA.m
//
// VISCA generic driver object library (phew)
// Version 1
//
// Copyright (c) 1991,1992 by Jiro Nakamura
// All Rights Reserved.
//
// Original Author:	Jiro Nakamura
// Created:		Dec 17, 1991
//
// RCS Information
// Revision Number->	$Revision: 1.2 $
// Last Revised->	$Date: 92/07/20 23:58:36 $
//

static char rcsid[]="$Id: VISCA.m,v 1.2 92/07/20 23:58:36 jiro Exp Locker: jiro $";
static char copyrightid[]="Copyright (C) 1991,1992 by Jiro Nakamura";

#import "VISCA.h"
#include <sgtty.h>
#import <stdio.h>
#import <libc.h>
#import <c.h>
#import <sys/ioctl.h>
#import <strings.h>


#define CAT_CODE	2	/* 2nd byte pos. */

int openPort( char *deviceName, int bps);
int closePort( int fd);


int nindex( unsigned char *s, unsigned  char c, int n)
{
	int tmp;
	
	for( tmp = 0; tmp < n; tmp ++)
		{
		if( s[tmp] == c)
			return tmp;
		}
	return -1;
}

@implementation VISCA

// Opening and closing the driver
- setPort: (char *) portName
{
	strncpy( deviceName, portName, 30);
	return self;
}

- setDelegate: d
{
	delegate = d;
	return self;
}

	
- open
{
	#ifdef DEBUG
		fprintf(stderr, "\nOpening <%s>\n", deviceName);
	#endif
	
	if( (fd = openPort( deviceName, B9600)) < 0)
		{
		perror( "openPort");
		return nil;
		}
	inMessageCount = 0;
	return self;
}

- close
{
	if( 	closePort(fd) < 0)
		{
		perror("ClosePort: ");
		return nil;
		}
	return self;
}

- readStatus
{
	int readChars, tmp;
	
	ioctl(fd, FIONREAD, &readChars);
	if( readChars > 0 )
		{
		#ifdef DEBUG
			fprintf(stderr,"%d characters ready to read\n",
				 readChars);
		#endif

		tmp = read( fd, inMessage + inMessageCount, readChars);
		inMessageCount += tmp;
		}
	[self interpretStatus];		
	
	return self;
}


- interpretStatus
{
	int count; 
	int sourceAddress, destinationAddress;
	unsigned char message[128];
	
	
	if( inMessageCount <= 0 )
		return nil;
		
	if( (count = nindex( inMessage, 0xff, inMessageCount)) < 0)
		return nil;

	#ifdef DEBUG
		fprintf(stderr,"Interpreting status msg. InMesCount = %d\n",
			inMessageCount);
	#endif

	if( count == 0)
		{
		if( inMessageCount > 0 )
			inMessageCount -= 1;
		return [self interpretStatus];
		}

	bcopy(inMessage, message, count+1);
	inMessageCount -= count +1;
	
	#ifdef DEBUG
		fprintf(stderr,"We have complete message -- count = %d\n"
			" -- Message body <", count);
		for( tmp = 0; message[tmp] != (unsigned char) 0xFF; tmp++)
			fprintf(stderr,"%02X ", (unsigned char) message[tmp]);
		fprintf(stderr,">\n");
	
		fprintf(stderr,"\n----- Analysis-------\n");
	#endif
	
	device = sourceAddress = (message[0] & 0x70) >> 4;
	destinationAddress = message[0] & 0x0F;
	
	#ifdef DEBUG
		fprintf(stderr,"Source = %d, dest = %d\n", sourceAddress,
			destinationAddress);
	#endif

	switch( message[1] & 0xF0)
		{
		case 0x00:
			switch( message[1] )
				{
				case 0x01:
					fprintf(stderr,"Format 1 message\n");
					break;
				case 0x02:
					fprintf(stderr,"Format 2 message\n");
					break;
				case 0x03:
					fprintf(stderr,"Format 3 message\n");
					break;
				case 0x04:
					fprintf(stderr,"Format 4 message\n");
					break;
				}
			break;
				
		case 0x30:
			switch( message[1] )
				{
				case 0x38:
					fprintf(stderr,"Network change.\n");
					break;
				case 0x30:
					fprintf(stderr,"Address request "
						"from %d.\n",
					message[2]);
					break;
				}
			break;
		case 0x20:
			fprintf(stderr,"Cancel request from %d.\n",
				message[1] & 0x0F);
			socket = message[1] & 0x0F;
			break;
		case 0x40:
			fprintf(stderr,"Acknowledge message from %d.\n",
				message[1] & 0x0F);
			socket = message[1] & 0x0F;
			break;
		case 0x50:
			fprintf(stderr,"Completion message from %d.\n",
				message[1] & 0x0F);
			socket = message[1] & 0x0F;
			break;
		case 0x60:
			fprintf(stderr,"Error message from %d:  ",
				message[1] & 0x0F);
			socket = message[1] & 0x0F;
			break;

		default:
			fprintf(stderr,"Unrecognized message header %02Xh\n\n",
				message[1]);
			break;
		}		

	#ifdef DEBUG
		fprintf(stderr,"-------------------\n\n");
	#endif
	
	if( inMessageCount > 0 )
		[self interpretStatus];
		
	return self;
}

- setAddressFrom: (int) frm to: (int) to
{
	char nibble1, nibble2;
	
	nibble1 = (0x08 | frm) & 0x0f;
	nibble2 = to & 0x0f;
	
	
	outMessage[0] = nibble1 << 4 | nibble2;
	
	#ifdef DEBUG
		fprintf(stderr, "Set Address, from = %1X, to = "
			"%1X, out = %02Xh\n",
			frm, to, outMessage[0] );
	#endif

	return self;
}

- setAddressBroadcastFrom: (int) frm
{
	char nibble1, nibble2;
	
	nibble1 = (0x08 | frm) & 0x0f;
	nibble2 = 0x08;
	
	outMessage[0] = nibble1 << 4 | nibble2;
	
	#ifdef DEBUG
		fprintf(stderr, "Set Broadcast Address, from = %1X, "
			"out = %02Xh\n",
			frm,  outMessage[0] );
	#endif
	
	return self;
}



- setWhenNow
{
	outMessage[1] = 0x01;
	outMessage[2] = 0xFF;	// cat code
	
	headerSize = 3;	
	return self;
}


- setWhenClock: (struct ct_hmst *) value
{
	outMessage[1] = 0x02;
	outMessage[2] = 0xFF;	// cat code
	outMessage[3] = value->hh;
	outMessage[4] = value->mm;
	outMessage[5] = value->ss;
	outMessage[6] = value->t1;
	outMessage[6] = value->t2;
	headerSize = 7;	
	return self;
}


- setWhenCounter: (union counterType *) value 
	counterType: (int) type 
	before: (BOOL) b
{
	outMessage[1] = b ? 0x04 : 0x03;
	outMessage[2] = 0xFF;	// cat code
	outMessage[3] = type;
	outMessage[4] = value->bcd.d1;
	outMessage[5] = value->bcd.d1;
	outMessage[6] = value->bcd.d1;
	outMessage[6] = value->bcd.d1;
	headerSize = 7;	


	return self;
}



- (int) v_address
{
	unsigned char message[4];	// use own since we will corrupt header
	
	#ifdef DEBUG
		fprintf(stderr,"V_address\n");
	#endif

	message[0] = outMessage[0];
	message[1] = 0x30;
	message[2] = 0x01;
	message[3] = 0xFF;
	return [self sendMessage: message size: 4];	
}

- (int) md_interfaceInquiry: (int) type
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Interface Inquiry = %02Xh\n", type);
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x00;
	message[3] = type;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}



- (int) md_power: (int) stat
{
	#ifdef DEBUG
		fprintf(stderr,"V_power: %s\n", stat == POWER_ON ? "ON" : 
			"OFF");
	#endif
	
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x00;
	outMessage[headerSize + 1] = stat;
	outMessage[headerSize + 2] = 0xFF;

	return [self sendMessage: outMessage size: headerSize + 3];	
}




- (int) md_modeControl_1: (int) smode
{
	#ifdef DEBUG
		fprintf(stderr,"V_modecon1 = %02Xh\n", smode);
	#endif
	
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x01;
	outMessage[headerSize + 1] = smode;
	outMessage[headerSize + 2] = 0xFF;
	return [self sendMessage: outMessage size: headerSize + 3];	
}

- (int) md_modeControl_2: (int) smode
{
	#ifdef DEBUG
		fprintf(stderr,"V_modecon2 = %02Xh\n", smode);
	#endif
	
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x02;
	outMessage[headerSize + 1] = smode;
	outMessage[headerSize + 2] = 0xFF;
	return [self sendMessage: outMessage size: headerSize + 3];	
}

- (int) md_subControl: (int) smode
{
	#ifdef DEBUG
		fprintf(stderr,"V_subControl = %02Xh\n", smode);
	#endif
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x10;
	outMessage[headerSize + 1] = smode;
	outMessage[headerSize + 2] = 0xFF;
	return [self sendMessage: outMessage size: headerSize + 3];	
}

- (int) md_search: (int) type counter: (union counterType *) ct
{
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x03;
	outMessage[headerSize + 1] = type;
	outMessage[headerSize + 2] = ct->bcd.d1;
	outMessage[headerSize + 3] = ct->bcd.d2;
	outMessage[headerSize + 4] = ct->bcd.d3;
	outMessage[headerSize + 5] = ct->bcd.d4;
	outMessage[headerSize + 6] = 0xFF;
	
	switch( type)
		{
		case CT_TME:
			outMessage[headerSize + 3] = 0;
			outMessage[headerSize + 4] = 0;
		case CT_HMS:
			outMessage[headerSize + 5] = 0;
			break;	
		}			

	return [self sendMessage: outMessage size: headerSize + 7];	
}

- (int) md_cameraZoom: (int) zoomMode
{
	#ifdef DEBUG
		fprintf(stderr,"V_cameraZoom = %02Xh\n", zoomMode);
	#endif
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x07;
	outMessage[headerSize + 1] = zoomMode;
	outMessage[headerSize + 2] = 0xFF;
	return [self sendMessage: outMessage size: headerSize + 3];	
}

- (int) md_cameraFocus: (int) focusMode
{
	#ifdef DEBUG
		fprintf(stderr,"V_cameraFocus = %02Xh\n", focusMode);
	#endif
	outMessage[CAT_CODE	 ] = 0x02;
	outMessage[headerSize    ] = 0x08;
	outMessage[headerSize + 1] = focusMode;
	outMessage[headerSize + 2] = 0xFF;
	return [self sendMessage: outMessage size: headerSize + 3];	
}



- (int) md_powerInquiry
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Power Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x00;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}

- (int) md_modeInquiry
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Mode Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x01;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}

- (int) md_channelInquiry
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Channel Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x04;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}

- (int) md_editControlInquiry
{
	unsigned char message[6];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Edit Control Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x05;
	message[4] = 0x01;
	message[5] = 0xFF;
	return [self sendMessage: message size: 6];	
}


- (int) md_mediaInquiry
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Media Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x12;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}

- (int) md_inputSelectInquiry
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Media Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x13;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}

- (int) md_configureInquiry
{
	unsigned char message[5];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Media Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x120;
	message[4] = 0xFF;
	return [self sendMessage: message size: 5];	
}


- (int) md_positionInquiry
{
	unsigned char message[6];	// use own since we will corrupt header

	#ifdef DEBUG
		fprintf(stderr,"V_Position Inquiry\n");
	#endif
	
	message[0] = outMessage[0];
	message[1] = 0x09;
	message[2] = 0x02;	
	message[3] = 0x03;
	message[4] = 0x10;
	message[5] = 0xFF;
	return [self sendMessage: message size: 6];	
}



- (int) sendMessage: (unsigned char *) m  size: (int) hs
{
	unsigned char reply[128];
	int replyCount;
	
	if( write( fd, m, hs) < 0)
		{
		perror("sendMessage: ");
		return -1;
		}

	replyCount =0;
	do
		{
		read( fd, reply + replyCount, 1);
		replyCount += 1;
		}
	while( reply[replyCount -1] != 0xFF );
	
	device = (reply[0] & 0x70) >> 4;
	socket = reply[1] & 0x0F;	
	errorCode = ERROR_NOERROR;

	#ifdef DEBUG
		fprintf(stderr, "=========Sending Message=======\n");
	#endif
	
	switch( reply[1] & 0xF0 )
		{
		case CMD_ACKNOWLEDGE:
			#ifdef DEBUG
				fprintf(stderr,"Reply: Acknowledged from %d.\n"
					"========================\n\n",
					socket);
			#endif
			return  CMD_ACKNOWLEDGE;
		case CMD_COMPLETION:
			#ifdef DEBUG
				fprintf(stderr,"Reply: Completed from %d.\n"
					"========================\n\n",
					socket);
			#endif
			bcopy( reply + 2, completionMessage, replyCount -2 );
			completionMessageCount = replyCount - 2;
			return CMD_COMPLETION;
		case CMD_ERROR:
			errorCode = reply[2];
			#ifdef DEBUG
				fprintf(stderr,"Error %d from socket %d.\n"
					"==============================\n\n",
					errorCode, socket);
			#endif
			return CMD_ERROR;
		}
	
	bcopy( reply, inMessage, replyCount);
	inMessageCount += replyCount;
	return -1;
}


-  (int) copyCompletionMessage: (unsigned char *) buf;
{
	bcopy( completionMessage, buf, completionMessageCount);
	return completionMessageCount;
}

- (unsigned short int) errorCode 	{ return errorCode; };
- (unsigned short int) socket 		{ return socket; };
- (unsigned short int) device 		{ return device; };


@end


