/*************************************************************************\
* "$RCSfile: CL-Live2.cpp,v $"                                            *
***************************************************************************
* Information by CVS or VSS:                                              *
*     Last check in on: $Date: 2010-10-04 14:03:19 $                      *
*     by                $Author: schluckebier $
*     Revision        : $Revision: 1.1 $                                 *
\*************************************************************************/
/*! \file CL-Live2.cpp
*	\brief CameraLink Live Image
*
*	Uses a task for displaying image on Ethernet.
*	\author (c) 2010 - 2013 IMAGO Technologies GmbH
**************************************************************************/

#include <std.h>
#include <clk.h>
#include <mbx.h>
#include <sem.h>
#include <string.h>
#include "vision_box.h"
#include "GUI_Lib.h"
#include "socket.h"
#include "Example.h"

//! CameraLink format
enum eCLFORMAT
{
	FORMAT_GRAY8  = 1,	//!< Gray, 8 bit
	FORMAT_GRAY10,		//!< Gray, 10 bit
	FORMAT_GRAY12,		//!< Gray, 12 bit
	FORMAT_2TAP8BIT		//!< 2 taps, 8 bit
};

//! Message structure
typedef struct sMSGDATA
{
	int x;
	int y;
	YIMAGE Image;
} MSGDATA;

MBX_Handle MbxNewImage;	//!< Mailbox handle
SEM_Handle SemDisplay;	//!< Semaphore handle
int g_fps;				//!< Frames per second

/*************************************************************************/
int CLLive(CIMAGE *pImage, FILE *fKB)
/*************************************************************************/
/*! @brief Views image from CameraLink camera.
	@param pImage Image for display
	@param fKB Keyboard handle
	@return Error code
**************************************************************************/
{
	const int CL_Board = -1;			// Number of CL board
	const int CL_Connector = 0;			// Left or right CL connector
	const int CL_CamWidth = 1024;		// Image width
	const int CL_CamHeight = 768;		// Image height
	const int CL_Format = FORMAT_2TAP8BIT;	// Image format
	const int CL_Area = CL_AREACAM;		// Area/Line camera
	const int CL_DVal = 0;				// Data Valid
	const int CL_Timeout = 500000;		// Time out value
	YIMAGE yCamImage, yTempImage;
	CL_IN_RECEIVER ClInReceiver;
	CL_IN_CHANNEL ClInChannel;
	int camWidth, camHeight, cWidth, camPosX, camPosY;
	int nKey;
	int Memsize;
	bool bActive = true;
	unsigned short *GrabBuffer[2] = {NULL, NULL}, *pTemp;
	char *CamName;
	unsigned int DataFormat;
	MSGDATA MsgData;
	struct TSK_Attrs ta = TSK_ATTRS;

	// *** Initialize camera device ***
	DataFormat = CL_FORMAT8;
	if (CL_Format == FORMAT_2TAP8BIT)
		DataFormat = CL_FORMAT16;
	DataFormat |= CL_Area | CL_DVal;
	if (CL_Format == FORMAT_GRAY10)
		DataFormat |= CL_GRAY10;
	else if (CL_Format == FORMAT_GRAY12)
		DataFormat |= CL_GRAY12;

	// *** Initialize the Receiver ***
	if (VIB_ClInSetupReceiver(&ClInReceiver, CL_Board, CL_Connector, DataFormat) < 0)
	{
		GUI_MessageBoxEx(pImage, fKB, "\nError: Camera Receiver\n\n", NULL, "_OK", NULL, NULL);
		return -1;
	}

	// *** Set image size ***
	camWidth  = CL_CamWidth;
	camHeight = CL_CamHeight;
	camPosX = (pImage->Width - camWidth) / 2;
	camPosX = _max2(camPosX, 0);
	camPosX &= ~0x3;
	camPosY = (pImage->Height - camHeight) / 2;
	camPosY = _max2(camPosY, 0);

	// *** Open a Channel ***
	cWidth = (CL_Format == FORMAT_2TAP8BIT) ? camWidth / 2 : camWidth;
	if (VIB_ClInOpenChannel(&ClInChannel, &ClInReceiver,
		0,	// Channel ID
		0, 0, cWidth, camHeight,	// ROI
		0) < 0)
	{
		GUI_MessageBoxEx(pImage, fKB, "\nError: Camera Channel\n\n", NULL, "_OK", NULL, NULL);
		return -1;
	}

	Memsize = camWidth * camHeight;
	yCamImage = GUI_YImage(NULL, camWidth, camHeight);

	GrabBuffer[0] = (unsigned short*)VIB_MemAlloc(Memsize);
	GrabBuffer[1] = (unsigned short*)VIB_MemAlloc(Memsize);

	if (!GrabBuffer[0] || !GrabBuffer[1])
	{
		printf("Error malloc!\n");
		exit(-1);
	}

	// *** Prepare display ***
	DrawInit(pImage, camWidth, camHeight);

	// *** Grab 1. image ***
	VIB_ClInStartChannel(&ClInChannel, (unsigned int*)GrabBuffer[1], 0);

	MbxNewImage = MBX_create(sizeof(MSGDATA), 4, NULL);
	SemDisplay = SEM_create(1, NULL);

	ta.priority = TSK_getpri(TSK_self());
	ta.stacksize = 0x4000;
	ta.stackseg = IRAM;
	ta.name = "TskDisplay";
	TSK_create((Fxn)TskDisplay, &ta, pImage);

	// *** Main loop ******************************************************
	while (bActive)
	{
		if (VIB_ClInWaitChannel(&ClInChannel, CL_Timeout) < 0)
		{
			memset(GrabBuffer[0], 0, Memsize);
			CamName = "Camera timeout";
		}
		else
		{
			CamName = "CameraLink";
		}

		VIB_ClInStartChannel(&ClInChannel, (unsigned int *)GrabBuffer[0], 0);

		g_fps = CalcFps();

		nKey = getc(fKB);
		switch (nKey)
		{
		case EOF:
			_UNSET(fKB, _STATEOF);
			break;
		case KB_ESC:
			bActive = 0;
			break;
		}

		if (SEM_pendBinary(SemDisplay, 0))	// Display ready?
		{
			// *** Conversion of camera image for display ***
			yTempImage = GUI_YImage((unsigned char*)GrabBuffer[1], camWidth, camHeight);
			GUI_CopyYArea(&yTempImage, &yCamImage, 0, 0, camWidth, camHeight, 0, 0);

			// *** Send mailbox to display task ***
			MsgData.x = camPosX;
			MsgData.y = camPosY;
			MsgData.Image = yCamImage;
			MBX_post(MbxNewImage, &MsgData, 500);
		}

		// *** Swap buffers ***********************************************
		pTemp = GrabBuffer[0];
		GrabBuffer[0] = GrabBuffer[1];
		GrabBuffer[1] = pTemp;
	}

	// *** Clean up *******************************************************
	VIB_ClInWaitChannel(&ClInChannel, 500000);
	VIB_ClInCloseChannel(&ClInChannel);

	if (GrabBuffer[0])
		VIB_MemFree(GrabBuffer[0]);
	if (GrabBuffer[1])
		VIB_MemFree(GrabBuffer[1]);
	GUI_FreeYImage(&yCamImage);

	GUI_Fill(pImage, &rgbDkBlue);
	return 0;
}

/*************************************************************************/
void DrawInit(CIMAGE *pImage, int Width, int Height)
/*************************************************************************/
/*! @brief Initializes camera screen.
	@param pImage Display image
	@param Width Width of camera image
	@param Height Height of camera image
**************************************************************************/
{
	int PosX, PosY;

	PosX = (pImage->Width - Width) / 2;
	PosX = _max2(PosX, 0);
	PosX &= ~0x3;
	PosY = (pImage->Height - Height) / 2;
	PosY = _max2(PosY, 0);

	GUI_Fill(pImage, &rgbDkBlue);
	if (Width<(pImage->Width - 4) && Height<(pImage->Height - 4))
	{
		GUI_Border(pImage, PosX - 2, PosY - 2, Width + 4, Height + 4, 5);
		GUI_Shadow(pImage, PosX + Width + 1, PosY + 8, 8, Height - 7);
		GUI_Shadow(pImage, PosX + 8, PosY + Height + 1, Width + 1, 8);
	}
	GUI_Display(pImage, 0, 0);
	GUI_Display(pImage, 0, 0);
}

/*************************************************************************/
void ShowInfo(CIMAGE *pImage, YIMAGE *pyCamImage, int PosX, int PosY,
	const char *pCamName)
/*************************************************************************/
/*! @brief Prints information in camera image and shows the image.
	@param pImage Display image
	@param pyCamImage Gray camera image
	@param xOff x offset in camera image
	@param yOff y offset in camera image
	@param PosX x destination in display image
	@param PosY y destination in display image
	@param pCamName Camera name
**************************************************************************/
{
	const char oOo[] = ".oOo";
	const int camWidth = pyCamImage->Width;
	const int camHeight= pyCamImage->Height;
	const int camMinHeight = _min2(camHeight, pImage->Height);
	char Text[64];
	static int k = 0;

	// *** Print info text ***
	sprintf(Text, "%dx%d %2d fps %c", camWidth, camHeight, g_fps, oOo[++k % 4]);
	GUI_TextOutY(pyCamImage, 12, camMinHeight - 42, pCamName);
	GUI_TextOutY(pyCamImage, 12, camMinHeight - 22, Text);

	GUI_DisplayY(pyCamImage, PosX, PosY);
	TSK_sleep(50);
}

/*************************************************************************/
unsigned int CalcFps(void)
/*************************************************************************/
/*! @brief Calculates fps from time between two calls.
	@return Frames per second
**************************************************************************/
{
	unsigned int t1;
	unsigned int usec, fps;
	static unsigned int t0 = 0;

	// *** Calculate frames per second ***
	t1 = VIB_TimerCount();
	usec = (t1 - t0) / VIB_TimerFreq();
	t0 = t1;
	fps = (int)(1000000.0 / usec + 0.5);
	return fps;
}

/*************************************************************************/
void TskDisplay(CIMAGE *pImage)
/*************************************************************************/
/*! @brief Task: Displays image.
	@param pImage Background image
**************************************************************************/
{
	MSGDATA MsgData;

	fdOpenSession(TSK_self());

	while (1)
	{
		if (MBX_pend(MbxNewImage, &MsgData, SYS_FOREVER))
		{
			ShowInfo(pImage, &MsgData.Image, MsgData.x, MsgData.y, "CameraLink");
			SEM_postBinary(SemDisplay);
		}
	}
}

/*************************************************************************/
