/*
 * ScianTQ2026F.c
 *   Written by Marvin Landis
 *   April 10, 1992
 *   Controls the Panasonic TQ-2026F optical disk recorder
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianWindows.h"
#include "ScianDialogs.h"
#include "ScianIDs.h"
#include "ScianTQ2026F.h"

#ifdef TERMIO

/* Internal prototypes */
#ifdef PROTO
void TQ_ReportError(int, char *);
void TQ_SendCmd(int, char *);
static int TQ_OpenPort(void);
static int TQ_VerifyResponse(int, char *, int);
static ObjPtr ConnectTQ2026F(ObjPtr);
static ObjPtr PrepareToRecordTQ2026F(ObjPtr, long);
static ObjPtr StopRecordingTQ2026F(ObjPtr);
static ObjPtr DisconnectTQ2026F(ObjPtr);
static ObjPtr SnapOneFrameTQ2026F(ObjPtr);
#endif

void TQ_ReportError(error, procName)
/* Reports the error returned from the TQ_VerifyResponse function */
int error;
char *procName;
{
    switch (error) {
	case -100:
		   ReportError(procName, "STX not received");
		   break;
	case -101:
		   ReportError(procName, "Completion response is invalid");
		   break;
	case -102:
		   ReportError(procName, "Timeout error");
		   break;
	default:
		   ReportError(procName, "NAK received from recorder");
		   break;
    }
}

static int TQ_OpenPort()
/* Opens a port to the recorder, returns it if successful */
{
    int portDev;
    struct termio ioStuff;
    char *devName;

    devName = getenv("SCIAN_RECORDER_DEV");
    if (!devName || !devName[0])
    {
	devName = TQ2026F_DEV;
    }

    portDev = open(devName, O_RDWR | O_NDELAY | O_EXCL);
    if (portDev < 0)
    {
	return portDev;
    }
    ioctl(portDev, TCGETA, &ioStuff);
    ioStuff . c_oflag = 0;
    ioStuff . c_iflag = IGNBRK;
    ioStuff . c_lflag &= ~( ICANON | ECHO );
    ioStuff . c_cflag = (unsigned short) C_CFLAG;
    ioStuff . c_cc[4] = 1;
    ioStuff . c_cc[0] = 1;

    ioctl(portDev, TCSETA, &ioStuff);
    return portDev;
}

void TQ_SendCmd(portDev, command)
/* Encapsulates the command in a STX / ETX pair, then sends the command
 * to the recorder.
*/
int portDev;
char *command;
{
    char fullcommand[256];
    int bytesWritten;

    sprintf(fullcommand, "%c%s%c", STX, command, ETX);
    bytesWritten = write(portDev, fullcommand, strlen(fullcommand));
}

static int TQ_VerifyResponse(portDev, command, timeout)
/* Reads the completion response from the recorder, checks to see if it is
 * the correct response, and returns the result. Returns either a 0 or a
 * positive integer (the number of remaining frames to be recorded, as returned
 * from the RECORD_REMAIN command) iff the command is successfully completed.
 * Returns the following negative numbers if there was a problem decoding the
 * command:
 *
 * -1 to -99            NAK response error
 * -100                 STX not received
 * -101                 Completion response does not match the command
 * -102                 Timeout
*/
int portDev;
char *command;
int timeout;
{
    char retVal[256], fullRetVal[256];
    int i, j;
    int bytesRead;
    int retInt = 0;
    long startTime, curTime;
    struct tms buffer;

    startTime = times(&buffer);
    strcpy(fullRetVal, "");
    do
    {
	sleep(1);     /* Couldn't get consistent results without this delay */
	bytesRead = read(portDev, retVal, 256);
	if (bytesRead > 0)
	{
	    retVal[bytesRead] = 0;
	    strcat(fullRetVal, retVal);
	    if (fullRetVal[0] != ACK)
	    {
		/* NAK has been received.  Decode the error number, and return
		 * the negative value of the error
		*/
		j = 0;
		while (fullRetVal[++j])
		    retInt = retInt * 10 + (fullRetVal[j] - '0');
		return -retInt;
	    }
	    else
	    {
		/* ACK has been received.  STX is next, then the completion
		 * response.  Optional number follows, then terminates with
		 * an ETX.
		*/
		j = 0;
		while (fullRetVal[++j])
		{

		    /* The very first time an ON8: command is sent to the
		     * recorder, the ACK is followed by a CR/LF.  After ON8:
		     * is executed, there are no more CR/LF's sent.  This
		     * statement takes care of that first CR/LF.
		    */
		    if (fullRetVal[j] == CR) j += 2;

		    if (fullRetVal[j] != STX) return -100;
		    if (fullRetVal[++j] == 'E') {
			retInt = 0;
			while (fullRetVal[++j] != ETX)
			    retInt = retInt * 10 + (fullRetVal[j] - '0');
			retInt = -retInt;
		    }
		    else
		    {
			if (fullRetVal[j] != command[0]) return -101;
			if (fullRetVal[++j] != command[1]) return -101;
			retInt = 0;
			while (fullRetVal[++j] != ETX)
			    retInt = retInt * 10 + (fullRetVal[j] - '0');
			return retInt;
		    }
		}
	    }
	}
    curTime = times(&buffer);
    } while (curTime < startTime + timeout * HEARTBEAT);
    return -102;
}

static ObjPtr ConnectTQ2026F(object)
/* Connects to the Panasonic TQ-2026F.  Returns true iff successful */
ObjPtr object;
{
    int portDev;
    int response;

    /*Open the serial port*/
    portDev = TQ_OpenPort();
    if (portDev < 0)
    {
	return ObjFalse;
    }

    /*Now that a port is open, connect to it*/
    TQ_SendCmd(portDev, ONLINE);
    response = TQ_VerifyResponse(portDev, ONLINE, 5);
    if (response == 0)
    {

	/* Exercise the recorder a little to verify it is ready */
	TQ_SendCmd(portDev, STEP_FORWARD);
	response = TQ_VerifyResponse(portDev, STEP_FORWARD, 20);
	if (response == 0)
	{
	    TQ_SendCmd(portDev, STEP_BACKWARD);
	    response = TQ_VerifyResponse(portDev, STEP_BACKWARD, 5);
	    if (response == 0) {
		SetVar(object, PORTNUMBER, NewInt(portDev));
		return ObjTrue;
	    }
	}

    }
    close(portDev);
    TQ_ReportError(response, "ConnectTQ2026F");
    return ObjFalse;
}

static ObjPtr PrepareToRecordTQ2026F(object, nFrames)
/* Prepares to record nFrames on an TQ2026F.  Returns true iff successful. */
ObjPtr object;
long nFrames;
{
    ObjPtr port;
    int portDev;
    int remFrames;
    int response;
    char searchCommand[256];

    port = GetVar(object, PORTNUMBER);
    if (port)
    {
	portDev = GetInt(port);

	do
	{

	    /* Manual record mode searches for next blank recording area. */
	    TQ_SendCmd(portDev, MANUAL_RECORD_MODE);
	    response = TQ_VerifyResponse(portDev, MANUAL_RECORD_MODE, 400);
	    if (response < 0)
	    {
		TQ_ReportError(response, "PrepareToRecordTQ2026F");
		return ObjFalse;
	    }

	    /* Now find out how many blank frames there are to record on */
	    TQ_SendCmd(portDev, RECORD_REMAIN);
	    remFrames = TQ_VerifyResponse(portDev, RECORD_REMAIN, 5);
	    if (remFrames < 0) {
		TQ_ReportError(response, "PrepareToRecordTQ2026F");
		return ObjFalse;
	    }

	    /* If not enough, prepare to search for more */
	    if (remFrames < nFrames)
	    {
		TQ_SendCmd(portDev, RECORD_MODE_CLEAR);
		response = TQ_VerifyResponse(portDev, RECORD_MODE_CLEAR, 5);
		if (response < 0)
		{
		    TQ_ReportError(response, "PrepareToRecordTQ2026F");
		    return ObjFalse;
		}

		/* Search to the next recorded frame, then try again */
		sprintf(searchCommand, "%s+%d:", SEARCH, remFrames);
		TQ_SendCmd(portDev, searchCommand);
		response = TQ_VerifyResponse(portDev, SEARCH, 5);
		if (response < 0)
		{
		    TQ_ReportError(response, "PrepareToRecordTQ2026F");
		    return ObjFalse;
		}
	    }
	} while (remFrames < nFrames);
	return ObjTrue;
    }
    return ObjFalse;
}

static ObjPtr StopRecordingTQ2026F(object)
/* Stops recording */
ObjPtr object;
{
    ObjPtr port;
    int response;

    port = GetVar(object, PORTNUMBER);
    if (port)
    {
	int portDev;
	portDev = GetInt(port);

	TQ_SendCmd(portDev, RECORD_MODE_CLEAR);
	response = TQ_VerifyResponse(portDev, RECORD_MODE_CLEAR, 5);
	if (response == 0)
	    return ObjTrue;
	else
	{
	    TQ_ReportError(response, "StopRecordingTQ2026F");
	    return ObjFalse;
	}
    }
    return ObjFalse;
}

static ObjPtr DisconnectTQ2026F(object)
/* Takes the recorder offline */
ObjPtr object;
{
    int portDev;
    ObjPtr port;
    int response;

    port = GetVar(object, PORTNUMBER);
    if (port)
    {
	portDev = GetInt(port);
	TQ_SendCmd(portDev, OFFLINE);
	response = TQ_VerifyResponse(portDev, OFFLINE, 5);
	if (response == 0)
	{
	    close(portDev);
	    SetVar(object, PORTNUMBER, NULLOBJ);
	    return ObjTrue;
	}
	else
	{
	    TQ_ReportError(response, "DisconnectTQ2026F");
	    return ObjFalse;
	}
    }
    return ObjFalse;
}

static ObjPtr SnapOneFrameTQ2026F(object)
/* Records one frame on the Panasonic recorder */
ObjPtr object;
{
    int portDev;
    int response;
    ObjPtr port;

    port = GetVar(object, PORTNUMBER);
    if (port)
    {
	portDev = GetInt(port);
	TQ_SendCmd(portDev, RECORD_FRAME);
	response = TQ_VerifyResponse(portDev, RECORD_FRAME, 5);
	if (response ==  0)
	    return ObjTrue;
	else
	{
	    TQ_ReportError(response, "SnapOneFrameTQ2026F");
	    return ObjFalse;
	}
    }
    return ObjFalse;
}
#endif

ObjPtr NewTQ2026F()
/* Return a new TQ-2026F object */
{
#ifdef TERMIO
    ObjPtr retVal;

    retVal = NewObject(NULLOBJ, 0);
    SetVar(retVal, NAME, NewString("Panasonic TQ-2026F"));
    SetMethod(retVal, CONNECT, ConnectTQ2026F);
    SetMethod(retVal, DISCONNECT, DisconnectTQ2026F);
    SetMethod(retVal, PREPARETORECORD, PrepareToRecordTQ2026F);
    SetMethod(retVal, STOPRECORDING, StopRecordingTQ2026F);
    SetMethod(retVal, SNAPONEFRAME, SnapOneFrameTQ2026F);
    return retVal;
#else
    return NULLOBJ;
#endif
}
