/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * The Initial Developer of this code is David Baum.
 * Portions created by David Baum are Copyright (C) 1999 David Baum.
 * All Rights Reserved.
 *
 * Portions created by John Hansen are Copyright (C) 2005 John Hansen.
 * All Rights Reserved.
 *
 */
#include "RCX_GhostTransport.h"
#include "RCX_Link.h"
#include "PDebug.h"
#include <cstdio>
#include <CodeFragments.h>

using std::printf;

#define SESSION "LEGO.Pbk.CommStack.Session"
#define IR_PROTOCOL "LEGO.Pbk.CommStack.Protocol.IR"
#define USB_PORT "LEGO.Pbk.CommStack.Port.USB"
//#define RS232_PORT "LEGO.Pbk.CommStack.Port.RS232"


RCX_GhostTransport::RCX_GhostTransport()
{
	fOpen = false;
}


RCX_GhostTransport::~RCX_GhostTransport()
{
	Close();
}


RCX_Result RCX_GhostTransport::Open(RCX_TargetType target, const char *deviceName, ULong options)
{
#pragma unused(target, deviceName)

	// check for GhostAPI
	if ((void*)GhCreateStack == (void*)kUnresolvedCFragSymbolAddress)
		return kRCX_GhostNotFoundError;

	fVerbose = (options & RCX_Link::kVerboseMode);

	PBKRESULT err;

	err = GhCreateStack(USB_PORT, IR_PROTOCOL, SESSION, &fStack);
	PREQUIRENOT(err, Fail_GhCreateStack);

	err = GhSelectFirstDevice(fStack, 0, 0);
	PREQUIRENOT(err, Fail_GhSelectFirstDevice);

	err = GhOpen(fStack);
	PREQUIRENOT(err, Fail_GhOpen);

	// probably should check for errors...
	GhSetWaitMode(fStack, 0);
	GhGetRetries(fStack, &fExRetries, &fDownRetries);

	fOpen = true;

	return kRCX_OK;

Fail_GhOpen:
Fail_GhSelectFirstDevice:
	GhDestroyStack(fStack);

Fail_GhCreateStack:
	return kRCX_OpenSerialError;
}


void RCX_GhostTransport::Close()
{
	if (fOpen)
	{
		GhClose(fStack);
		GhDestroyStack(fStack);
		fOpen = false;
	}
}


RCX_Result RCX_GhostTransport::Send(const UByte *txData, int txLength, UByte *rxData, int rxExpected, int rxMax, bool retry)
{
	RCX_Result result = kRCX_RequestError;
	PBKRESULT err;

	GHQUEUE queue = CreateQueue(txData, txLength, rxExpected);
	PREQUIRE(queue, Fail_CreateQueue);

	if (fVerbose)
	{
		printf("Tx: ");
		DumpData(txData, txLength);
	}

	if (rxExpected == 0) retry = false;	// can't retry a one-way packet

	if (retry)
	{
		err = GhExecute(fStack, queue);
	}
	else
	{
		GhSetRetries(fStack, 0, 0);
		err = GhExecute(fStack, queue);
		GhSetRetries(fStack, fExRetries, fDownRetries);
	}

	if (err)
	{
		result = kRCX_ReplyError;
	}
	else if (rxExpected)
	{
		result = ExtractReply(queue, rxData, rxMax);
	}
	else
	{
		result = kRCX_OK;
	}

	GhDestroyCommandQueue(queue);

Fail_CreateQueue:
	return result;
}


RCX_Result RCX_GhostTransport::ExtractReply(GHQUEUE queue, UByte *rxData, int rxMax)
{
	GHCOMMAND command;
	PBKRESULT err;

	uint32 rxLength;

	err = GhGetFirstCommand(queue, &command);
	PREQUIRENOT(err, Fail_GhGetFirstCommand);

	err = GhGetCommandReplyLen(command, &rxLength);
	PREQUIRENOT(err, Fail_GhGetCommandReplyLen);

	if (rxData)
	{
		if (rxLength > rxMax) rxLength = rxMax;

		err = GhGetCommandReply(command, rxData, rxLength);
		PREQUIRENOT(err, Fail_GhGetCommandReply);
		if (fVerbose)
		{
			printf("Rx: ");
			RCX_Transport::DumpData(rxData, rxLength);
		}
	}

	return rxLength-1;

Fail_GhGetCommandReply:
Fail_GhGetCommandReplyLen:
Fail_GhGetFirstCommand:
	return kRCX_ReplyError;
}


GHQUEUE RCX_GhostTransport::CreateQueue(const UByte *txData, int txLength, int rxExpected)
{
	PBKRESULT err;
	GHQUEUE queue;

	err = GhCreateCommandQueue(&queue);
	PREQUIRENOT(err, Fail_GhCreateCommandQueue);

	err = GhAppendCommand(queue, (UByte *)txData, (ULong)txLength, (ULong)rxExpected);
	PREQUIRENOT(err, Fail_GhAppendCommand);

	return queue;

Fail_GhCreateCommandQueue:
Fail_GhAppendCommand:
	return 0;
}



