/*************************************************************************\
* "$RCSfile: CPSApp.cpp,v $"                                              *
***************************************************************************
* Information by CVS or VSS:                                              *
*     Last check in on: $Date: 2011/09/07 16:34:17 $                      *
*     by                $Author: goebel $
*     Revision        : $Revision: 1.59 $                                 *
\*************************************************************************/
/*! \file CPSApp.cpp
*	\brief Implementation CPSApp Class
**************************************************************************/

#include <mathf.h>
#include "VIC_PS.h"
#include "GBit.h"
#include "ftp.h"
#include "CRegion.h"
#include "CZoomFactor.hpp"
#include "CLog.hpp"
#include "CFeature.hpp"
#include "CCamFeatures.hpp"
#include "CPSApp.hpp"
#include "CDlg.hpp"
#include "CDlgMain.hpp"
#include "CDlgSaveImage.hpp"
#include "CDlgCam.hpp"
#include "CDlgCamExpert.hpp"
#include "CDlgAppV.hpp"
#include "CDlgTools.hpp"
#include "CDlgDigIO.hpp"
#include "CDlgLog.hpp"
#include "CDlgConfig.hpp"
#include "CDlgPreviewSettings.hpp"
#include "CSmtp.hpp"
#include "CCpuLoad.hpp"

CPSApp *CPSApp::m_pThis = NULL;	//!< The one and only CPSApp instance

/*************************************************************************/
CPSApp::CPSApp()
/*************************************************************************/
/*! @brief Constructor
**************************************************************************/
{
	m_pThis = this;
	m_LibVersion = "PS CAM Framework "__DATE__;
	printf("%s\n", m_LibVersion);

	m_bAppActive = true;
	m_hGrabTask = NULL;
	m_pCamImage = NULL;
	m_pDisplayImage = NULL;
	m_EthError = 0;
	m_bCameraActive = false;
	m_bCameraPresent = false;
	m_FullScreen = 0;
	m_DlgAreaWidth = 216;
	m_DlgFocus = DialogId::MAIN;
	m_GraphUpdateCounter = GRAPH_UPDATE_CYCLE;
	m_ShowGraph = GraphType::FPS;
	m_View = m_ViewOld = ViewType::CAMERA;
	m_Info = InfoType::LOGFILE;
	m_HistStretch = 0;
	m_TaskDelay = 20;
	m_CamPixelFormat = (VIB_HWInfo.Type == VIB_TYPE_PSC) ? CamMode::_2TAP_8BIT : CamMode::_1TAP_8BIT;
}

/*************************************************************************/
void CPSApp::Init()
/*************************************************************************/
/*! @brief Initialization.
**************************************************************************/
{
	int CVWidth, CVHeight;
	SEM_Attrs SemAttrs = SEM_ATTRS;
	char pPath[128];

	ReadAppIni();

	std::snprintf(pPath, 128, "/%s/colors.ini", m_SkinPath);
	m_ColIni = Config::loadFromIni(VIB_Drive[1], pPath, "COLORS_INI");
	m_VicPs = Config::loadFromIni(VIB_Drive[1], "/vic_ps.ini", "VIC_PS_INI");
	m_VicPsStrings = NULL;
	InitStringTable();

	if (VIB_GetSerialNumber(m_SerialNumber) < 0)
		strcpy(m_SerialNumber, "SerialnumberInvalid");

	SetColors();
	InitNetwork();

	m_DefaultPreset = m_VicPs->getValue("DefaultPreset", "", 16);
	m_VicPs->setComment("DefaultPreset", "Default Preset .INI file");

	m_CurrentIni = new Config("PRESET_INI");
	m_CurrentIni->setValue("PresetName", "Current");
	if (LoadPreset(m_DefaultPreset)<0)
		CLog::Print("Loading preset from sensor...\n");

	InitDisplay();
	SemAttrs.type = 0;
	m_GrabTaskSem = SEM_create(0, &SemAttrs);
	m_StartTime = VIB_time(NULL);

	strcpy(m_SI.Path, m_VicPs->getValue("SIPath", "PICS", 64));
	strcpy(m_SI.FilenamePrefix, m_VicPs->getValue("SIFilenamePrefix", "pic", 5));
	m_SI.Format = m_VicPs->getValue("SIFormat", 1, 1, 3);
	m_SI.Counter = m_VicPs->getValue("SICounter", 0, 0, 999);
	m_SI.Autonumber = m_VicPs->getValue("SIAutonumber", 1, 0, 1);

	m_MbxCam = MBX_create(sizeof(CAM_MSG), MAILBOXSIZE, NULL);
	m_MbxVis = MBX_create(sizeof(CAM_MSG), MAILBOXSIZE, NULL);
	m_MbxXmlCmd = MBX_create(sizeof(CAM_MSG), 1, NULL);
	m_MbxXmlRet = MBX_create(sizeof(CAM_MSG), 1, NULL);

	m_CPULoadPic = GUI_CImage(NULL, NULL, NULL, 160, 160);
	m_LogImage = GUI_CImage(NULL, NULL, NULL,
		m_ScreenWidth - m_DlgAreaWidth - m_CPULoadPic.Width, 160);
	m_HistImage = GUI_CImage(NULL, NULL, NULL, 
		m_ScreenWidth - m_DlgAreaWidth - m_CPULoadPic.Width, 160);
	PrepareHistogram();

	m_LogPosX = m_ScreenWidth - m_CPULoadPic.Width - m_LogImage.Width;
	m_LogPosY = m_ScreenHeight - m_LogImage.Height;

	m_CVImage = GUI_CImage(NULL, NULL, NULL, m_ScreenWidth - m_DlgAreaWidth,
		m_ScreenHeight - m_CPULoadPic.Height);

	CVWidth = m_CVImage.Width - 16;
	CVHeight = m_CVImage.Height - 40;
	m_YPVImage = GUI_YImage(NULL, CVWidth, CVHeight);
	m_CPVImage = GUI_CImage(NULL, NULL, NULL, CVWidth, CVHeight);
	m_CPVImageSC[0] = GUI_YImage(m_CPVImage.pR, CVWidth, CVHeight);
	m_CPVImageSC[1] = GUI_YImage(m_CPVImage.pG, CVWidth, CVHeight);
	m_CPVImageSC[2] = GUI_YImage(m_CPVImage.pB, CVWidth, CVHeight);

	if (m_PVPosX + CVWidth > GetCamWidth())
		m_PVPosX = GetCamWidth() - CVWidth;
	if (m_PVPosX < 0)
		m_PVPosX = 0;
	if (m_PVPosY + CVHeight > GetCamHeight())
		m_PVPosY = GetCamHeight() - CVHeight;
	if (m_PVPosY < 0)
		m_PVPosY = 0;

	FxnDisplayDlgAppInfo = NULL;
	strcpy(m_ApplicationName, "Application");

	GUI_WndFrameG(&m_CVImage, "Camera", &m_ColBack, m_GradCol);

	// Init Zoom
	m_pZoom = new CZoomFactor;
	m_pZoom->Init(m_YPVImage.Width, m_YPVImage.Height, GetCamWidth(), GetCamHeight());
	m_PVScale = 1.0;

	// Read/write all values from camera
	PrintInit("Init camera...");
	OpenCameraLink();
	OpenPflib();
	CCamFeatures::GetInstance()->UpdateAllFeatures();
	CloseCameraLink();
}

/*************************************************************************/
CPSApp::~CPSApp()
/*************************************************************************/
/*! @brief Destructor.
**************************************************************************/
{
	if (CDlgSaveImage::GetInstance()->GetDirty())
		m_VicPs->saveToIni(VIB_Drive[1], "vic_ps.ini");
	delete m_pZoom;
	fclose(m_fKB);

	if (m_EthError == 0)
	{
		m_pFtp->stop();
		GBIT_CloseDisplay(&m_VIBDisplay);
		fdCloseSession(TSK_self());
	}

	GUI_FreeCImage(&m_Image);
	for (int i = 0; i < IconId::XXLAST; i++)
		GUI_FreeCImage(&m_Icon[i]);
	ClosePflib();
}

/*************************************************************************/
CPSApp *CPSApp::CreateInstance()
/*************************************************************************/
/*! @brief Creates CPSApp instance.
	@return Pointer to CPSApp instance
**************************************************************************/
{
	if (m_pThis == NULL)
		m_pThis = new CPSApp();
	return m_pThis;
}

/*************************************************************************/
void CPSApp::DisposeInstance()
/*************************************************************************/
/*! @brief Disposes CPSApp instance.
**************************************************************************/
{
	if (m_pThis)
		delete m_pThis;
	m_pThis = NULL;
}

/*************************************************************************/
int CPSApp::MainUILoop()
/*************************************************************************/
/*! @brief Main user interface loop

	In this function all dialogs and display outputs are handled.
**************************************************************************/
{
	CSmtp *pMail = CSmtp::GetInstance();
	CDlgMain *pDlgMain = CDlgMain::GetInstance();
	CDlgCam *pDlgCam = CDlgCam::GetInstance();
	CDlgCamExpert *pDlgCamExpert = CDlgCamExpert::GetInstance();
	CDlgConfig *pDlgConfig = CDlgConfig::GetInstance();
	CDlgSaveImage *pDlgSaveImage = CDlgSaveImage::GetInstance();
	CDlgDigIO *pDlgDigIO = CDlgDigIO::GetInstance();
	CDlgPreviewSettings *pDlgPreviewSettings = CDlgPreviewSettings::GetInstance();
	CDlgTools *pDlgTools = CDlgTools::GetInstance();
	int AutoStart;
	char Text[256];

	StartGrabTask();

	m_bForceRedraw = true;
	m_PVScale = m_pZoom->GetValue();
	UpdateScaleFactors();
	pDlgPreviewSettings->UpdateScaleFactors();

	AutoStart = m_AppIni->getValue("AUTOSTART", 1);
	if (AutoStart)
	{
		m_DlgNewFocus = DialogId::APP;
		m_View = ViewType::APP;
		pDlgMain->Enable(0);
	}
	else
	{
		m_DlgNewFocus = DialogId::MAIN;
	}
	m_DlgFocus = DialogId::NONE;

	CLog::Print("MainUILoop started\n");
	printf("MainUILoop started\n");
	RecBackground();

	if (pMail)
	{
		time_t tTime;
		struct tm *sTime;

		VIB_time(&tTime);
		sTime = localtime(&tTime);
		sprintf(Text, "Program started at %02d:%02d:%02d\n", sTime->tm_hour, sTime->tm_min, sTime->tm_sec);
		pMail->Send(Text);
	}

	// *** Main loop ***
	while (m_bAppActive)
	{
		int nKey = getc(m_fKB);

		if (nKey == EOF)
			_UNSET(m_fKB, _STATEOF);

		if (m_FullScreen)
		{
			FullScreenMode(nKey);
		}
		else
		{
			DisplayDynamicElements();

			// The dialog which has the focus processes the user inputs
			// level 0
			pDlgMain->OnUserIO(nKey);
			// level 1
			pDlgSaveImage->OnUserIO(nKey);
			pDlgPreviewSettings->OnUserIO(nKey);
			pDlgCamExpert->OnUserIO(nKey);
			pDlgCam->OnUserIO(nKey);
			pDlgTools->OnUserIO(nKey);
			pDlgApp->OnUserIO(nKey);
			// level 2
			pDlgDigIO->OnUserIO(nKey);
			CDlgLog::GetInstance()->OnUserIO(nKey);
			pDlgConfig->OnUserIO(nKey);

			RecBackground();
			// The dialog which has the focus updates if there was any activity
			// level 0
			pDlgMain->Update();
			// level 1
			pDlgSaveImage->Update();
			pDlgPreviewSettings->Update();
			pDlgCamExpert->Update();
			pDlgCam->Update();
			pDlgApp->Update();
			pDlgTools->Update();
			// level 2
			pDlgDigIO->Update();
			pDlgConfig->Update();

			m_DlgFocus = m_DlgNewFocus;

			// The dialogs which are visible display themselves, if anything has to be displayed
			// level 0
			pDlgMain->Show();
			// level 1
			pDlgSaveImage->Show();
			pDlgPreviewSettings->Show();
			pDlgCamExpert->Show();
			pDlgCam->Show();
			pDlgApp->Show();
			// level 2
			pDlgTools->Show();
			pDlgDigIO->Show();
		}
		TSK_sleep(m_TaskDelay);
	}
	// Draw info screen area
	CopyInfoArea();
	// Draw graph screen area
	DisplayGraph(&m_Image);

	GUI_CopyArea(&m_CVImage, &m_Image, 0, 0,
		m_CVImage.Width, m_CVImage.Height, m_DlgAreaWidth, 0);
	return 0;
}

/*************************************************************************/
void CPSApp::DisplayDynamicElements()
/*************************************************************************/
/*! @brief Displays all dynamic elements on the screen.
**************************************************************************/
{
	static int s_ViewOld = 0;

	m_CMsg.Type = ImageType::UNDEFINED;
	m_CMsg.Image.pyI = NULL;
	m_GMsg.Type = ImageType::UNDEFINED;
	m_GMsg.Image.pyI = NULL;

	while (MBX_pend(m_MbxVis, &m_CMsg, 0)) ;
	while (MBX_pend(m_MbxCam, &m_GMsg, 0)) ;

	if (m_GMsg.Image.pyI == NULL && m_View == ViewType::CAMERA)
		return;

	m_pCamImage = m_GMsg.Image.pyI; // Posted Y image from TaskPSGrab()

	DisplayInfoArea();

	if (m_View != s_ViewOld)
	{
		const char *pText;

		s_ViewOld = m_View;
		if (m_View == ViewType::CAMERA)
			pText = "Camera";
		else
			pText = m_ApplicationName;
		GUI_ButtonEx(m_pDisplayImage, m_DlgAreaWidth + 4, 4, m_CVImage.Width - 8, GUI_GetBoxHeight(&m_TPSet[TextStyle::SHADOW]) - 2, pText, 1, m_GradCol, 0, &m_TPSet[TextStyle::SHADOW]);
	}

	switch (m_View)
	{
	case ViewType::CAMERA:
		DisplayCameraLiveImage();
		break;
	case ViewType::APP:
		DisplayResultImage();
		break;
	}

	if (m_GraphUpdateCounter >= GRAPH_UPDATE_CYCLE)
	{
		DisplayGraph();
		m_GraphUpdateCounter = 0;
	}
	m_GraphUpdateCounter++;
	CCpuLoad::IncrementDisplayCounter();
}

/*************************************************************************/
void CPSApp::DisplayCameraLiveImage()
/*************************************************************************/
/*! @brief Displays scaled image for live camera area.

	Depending on #m_PVScale different scale functions are used. 
**************************************************************************/
{
	if (m_GMsg.Image.pyI == NULL)
		return;

	if (m_PVScale == 1.0)
	{
		GUI_CopyYArea(m_pCamImage, &m_YPVImage,
			m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight, 0, 0);
	}
	else if (m_PVScale > 1.0)
	{
		GUI_ZoomAreaBLY(m_pCamImage, &m_YPVImage, m_VCamWidth,
			m_VCamHeight, m_VCamPosX, m_VCamPosY,
			m_YPVImage.Width, m_YPVImage.Height, 0, 0);
	}
	else
	{
		// Keep aspect ratio!
		GUI_ScaleArea2NNY(m_pCamImage, &m_YPVImage,
			m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight);
	}

	if (m_pDisplayImage)
	{
		GUI_CopyYArea2RGB(&m_YPVImage, &m_Image, 0, 0, m_YPVImage.Width, m_YPVImage.Height, m_DlgAreaWidth + 8, 32);
		GUI_Display(&m_Image, 0, 0);
	}
	else
	{
		GUI_DisplayY(&m_YPVImage, m_DlgAreaWidth + 8, 32);
	}
}

/*************************************************************************/
void CPSApp::DisplayResultImage()
/*************************************************************************/
/*! @brief Displays scaled image for live camera area.

	Depending on #m_PVScale different scale functions are used.\n
	If the result image is a color image, the gray image scale functions
	are called separately for each plain.
**************************************************************************/
{
	if (m_CMsg.Image.pyI == NULL)
		return;

	switch (m_CMsg.Type)
	{
	case ImageType::Y8:
		if (m_PVScale == 1.0)
		{
			GUI_CopyYArea(m_CMsg.Image.pyI, &m_YPVImage,
				m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight, 0, 0);
		}
		else if (m_PVScale > 1.0)
		{
			GUI_ZoomAreaBLY(m_CMsg.Image.pyI, &m_YPVImage, m_VCamWidth,
				m_VCamHeight, m_VCamPosX, m_VCamPosY,
				m_YPVImage.Width, m_YPVImage.Height, 0, 0);
		}
		else
		{
			// Keep aspect ratio!
			GUI_ScaleArea2NNY(m_CMsg.Image.pyI, &m_YPVImage,
				m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight);
		}
		if (m_pDisplayImage)
		{
			GUI_CopyYArea2RGB(&m_YPVImage, &m_Image, 0, 0, m_YPVImage.Width, m_YPVImage.Height, m_DlgAreaWidth + 8, 32);
			GUI_Display(&m_Image, 0, 0);
		}
		else
		{
			GUI_DisplayY(&m_YPVImage, m_DlgAreaWidth + 8, 32);
		}
		break;
	case ImageType::C:
		if (m_PVScale == 1.0)
		{
			GUI_CopyArea(m_CMsg.Image.pcI, &m_CPVImage,
				m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight, 0, 0);
		}
		else if (m_PVScale > 1.0)
		{
			GUI_ZoomAreaBL(m_CMsg.Image.pcI, &m_CPVImage, m_VCamWidth,
				m_VCamHeight, m_VCamPosX, m_VCamPosY,
				m_CPVImageSC[0].Width, m_CPVImageSC[0].Height, 0, 0);
		}
		else
		{
			YIMAGE iT;

			iT = GUI_YImage(m_CMsg.Image.pcI->pR,
				m_CMsg.Image.pcI->Width, m_CMsg.Image.pcI->Height);
			GUI_ScaleArea2NNY(&iT, &m_CPVImageSC[0],
				m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight);
			iT.pY = m_CMsg.Image.pcI->pG;
			GUI_ScaleArea2NNY(&iT, &m_CPVImageSC[1],
				m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight);
			iT.pY = m_CMsg.Image.pcI->pB;
			GUI_ScaleArea2NNY(&iT, &m_CPVImageSC[2],
				m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight);
		}
		
		if (m_pDisplayImage)
		{
			GUI_CopyArea(&m_CPVImage, &m_Image, 0, 0, m_CPVImage.Width, m_CPVImage.Height, m_DlgAreaWidth + 8, 32);
			GUI_Display(&m_Image, 0, 0);
		}
		else
		{
			GUI_Display(&m_CPVImage, m_DlgAreaWidth + 8, 32);
		}
		break;
	}
}

/*************************************************************************/
int CPSApp::PostCalc(CIMAGE *pImage, int Frame)
/*************************************************************************/
/*! @brief Posts a color image message from grab to main task.
	@param pImage Pointer to a color image
	@param Frame Frame counter
	@return Error code of \p MBX_post
**************************************************************************/
{
	static int i = 0;
	CAM_MSG msg;

	if (Frame >= 0)
		msg.FrameCount = Frame;
	else
		msg.FrameCount = i++;
	msg.Image.pcI = pImage;
	msg.Type = ImageType::C;
	return MBX_post(m_MbxVis, &msg, MBX_POST_TIMEOUT);
}

/*************************************************************************/
int CPSApp::PostCalc(YIMAGE *pImage, int Frame)
/*************************************************************************/
/*! @brief Posts a gray image message from grab to main task
	@param pImage Pointer to a gray-scale image
	@param Frame Frame counter
	@return Error code of \p MBX_post
**************************************************************************/
{
	static int i = 0;
	CAM_MSG Msg;

	if (Frame >= 0)
		Msg.FrameCount = Frame;
	else
		Msg.FrameCount = i++;
	Msg.Image.pyI = pImage;
	Msg.Type = ImageType::Y8;
	return MBX_post(m_MbxVis, &Msg, MBX_POST_TIMEOUT);
}

/*************************************************************************/
int CPSApp::PostCalc()
/*************************************************************************/
/*! @brief Posts an invalid image.

	Used to shut down the grab task.
**************************************************************************/
{
	CAM_MSG Msg;

	Msg.FrameCount = 0;
	Msg.Image.pyI = NULL;
	Msg.Type = ImageType::UNDEFINED;
	return MBX_post(m_MbxVis, &Msg, MBX_POST_TIMEOUT);
}

/*************************************************************************/
int CPSApp::SetTriggerSource()
/*************************************************************************/
/*! @brief Sets trigger source in FPGA.

	Because trigger source and strobe output are in the same FPGA register
	they have to be handled together.
	@return Error code
**************************************************************************/
{
	if (VIB_HWInfo.Type == VIB_TYPE_PSC)
	{
		CCamFeatures *pCF = CCamFeatures::GetInstance();
		int Source = pCF->m_pTriggerSource->GetValue();
		CL_IN_CC ccValue;

		if (Source == 4)	// Set software controlled exposure time
		{
			pCF->m_pTriggerLevelControlled->SetValue(1);
			VIB_SetReg(0x0A000000, pCF->m_pTriggerDivisor->GetValue() - 1);
			Source = 1;
		}
		m_TriggerSourceNStrobeOut = (m_TriggerSourceNStrobeOut & 0x3C) | (Source & 0x3);
		VIB_SetReg(0x01500000, m_TriggerSourceNStrobeOut);
		if (pCF->m_pTriggerLevelControlled->GetValue() == 1) // Software controlled exposure time?
			VIB_ClReceiverConfGenerator(&m_ClInReceiver, 0, 1000);	// yes => set CC1 for 1us
		else
			VIB_ClReceiverConfGenerator(&m_ClInReceiver, 0, 0);
		VIB_ClReceiverSetGenerator(&m_ClInReceiver, 3);

		ccValue.Bits.CC1 = CLIN_CC_GENERATOR;
		ccValue.Bits.CC2 = CLIN_CC_LOW;
		ccValue.Bits.CC3 = CLIN_CC_LOW;
		ccValue.Bits.CC4 = CLIN_CC_LOW;
		VIB_ClReceiverSetCC(&m_ClInReceiver, ccValue);
	}
	return 0;
}

/*************************************************************************/
int CPSApp::SetStrobeOutput(int Output)
/*************************************************************************/
/*! @brief Sets strobe output in FPGA.

	Because trigger source and strobe output are in the same FPGA register
	they have to be handled together.
	@param Output Destination for strobe signal
	@return Error code
**************************************************************************/
{
	m_TriggerSourceNStrobeOut = (Output & 0x1C) | (m_TriggerSourceNStrobeOut & 0xE3);

	if (VIB_HWInfo.Type == VIB_TYPE_PSC)
		VIB_SetReg(0x01500000, m_TriggerSourceNStrobeOut);
	return 0;
}

/*************************************************************************/
int CPSApp::PostCam()
/*************************************************************************/
/*! @brief Posts an invalid image.

	Used to shut down the grab task.
**************************************************************************/
{
	CAM_MSG Msg;

	Msg.FrameCount = 0;
	Msg.Image.pyI = NULL;
	Msg.Type = ImageType::UNDEFINED;
	return MBX_post(m_MbxCam, &Msg, MBX_POST_TIMEOUT);
}

/*************************************************************************/
int CPSApp::PostCam(YIMAGE *pImage, int Frame)
/*************************************************************************/
/*! @brief Posts a gray image message from grab to main task.
	@param pImage Pointer to a gray-scale image
	@param Frame Frame counter
	@return Error code of \p MBX_post
**************************************************************************/
{
	static int i = 0;
	unsigned int *pStatusline;
	CAM_MSG Msg;

	if (Frame>=0)
		Msg.FrameCount = Frame;
	else
		Msg.FrameCount = i++;

	pStatusline = (unsigned int*)&pImage->pY[pImage->Width * (pImage->Height - 1)];
	if (pStatusline[0] == 0x55AA00FF)
	{
		Msg.ImageCount = pStatusline[1];
		Msg.TimeCount = pStatusline[2];
		Msg.MissedTrigger = pStatusline[3];
		Msg.AverageValue = pStatusline[4];
		Msg.ExposureCyles = pStatusline[5];
	}
	Msg.Image.pyI = pImage;
	Msg.Type = ImageType::Y8;
	return MBX_post(m_MbxCam, &Msg, MBX_POST_TIMEOUT);
}

extern "C"
{
#ifdef _TMS320C6400_PLUS
 #include "IMG_histogram_8.h"
 #define IMG_histogram IMG_histogram_8
#else
 #include "img_histogram.h"
#endif
}
#define BLOCKSIZE (32 << 10)

/*************************************************************************/
int CPSApp::CalcHistogram(YIMAGE *pImage, int *pHistogram)
/*************************************************************************/
/*! @brief Calculates histogram.
	@param pImage Pointer to a gray-scale image
	@param pHistogram Pointer to the resulting histogram
	@return Error code
**************************************************************************/
{
	int Result = -1;
	int Size, Len;
	int DmaChannel;
	unsigned char *Buffer;
	short *pTempHist, *pHist;

	if (pImage->pY == NULL)
		return 0;

//	if ((m_CamPixelFormat >> 8) == 1)
//		return 0;

	Buffer = (unsigned char*)MEM_alloc(ISRAM, BLOCKSIZE, 8);
	pTempHist = (short*)MEM_alloc(ISRAM, 2 * 1024 + 2 * 256, 8);
	if (Buffer == MEM_ILLEGAL || pTempHist == MEM_ILLEGAL)
		goto Cleanup;
	pHist = pTempHist + 1024;

	memset(pHistogram, 0, 4 * 256);

	Size = pImage->Width * pImage->Height;
	Len = MIN(Size, BLOCKSIZE);
	DmaChannel = VIB_DMACopy(-1, 0, pImage->pY, Buffer, Len);
	for (int i = 0; Size > 0; i++)
	{
		memset(pTempHist, 0, 2 * 1024 + 2 * 256);
		VIB_DMAWait(DmaChannel);
		IMG_histogram(Buffer, Len, 1, pTempHist, pHist);
		Size -= BLOCKSIZE;
		Len = MIN(Size, BLOCKSIZE);
		if (Len > 0)
			DmaChannel = VIB_DMACopy(-1, 0, pImage->pY + (i + 1) * BLOCKSIZE, Buffer, Len);
		for (int j = 0; j < 256; j++)
			pHistogram[j] += (unsigned short)pHist[j];
	}

Cleanup:
	if (Buffer != MEM_ILLEGAL)
		MEM_free(ISRAM, Buffer, BLOCKSIZE);
	if (pTempHist != MEM_ILLEGAL)
		MEM_free(ISRAM, pTempHist, 2 * 1024 + 2 * 256);
	return Result;
}

/*************************************************************************/
void CPSApp::CalcLut(int Low, int High, int *pLut)
/*************************************************************************/
/*! @brief Creates Look-Up-Table with low and high value from histogram.

	This table is needed for reinforcement from contrast or stretching
	from histogram.
	@param Low Lower sill
	@param High Higher sill
	@param pLut Pointer to LUT values
**************************************************************************/
{
	int i;

	for (i = 0; i < Low; i++)
		pLut[i] = 0;
	for (i = Low; i < High; i++)
		pLut[i] = 255 * (i - Low) / (High - Low);
	for (i = High; i < 256; i++)
		pLut[i] = 255;
}

/*************************************************************************/
void CPSApp::DrawHistogram(CIMAGE *pImage, int *pHistogram, int Mode)
/*************************************************************************/
/*! @brief Draws histogram into Log window.
	@param pImage
	@param pHistogram
	@param Mode 1: linear\n
	2: logarithmic
**************************************************************************/
{
	int i;
	int x, y, y2, MaxValue, PeakDN;
	int xOff = 30;
	char Peak[16];
	COLOR rgbBack;

	MaxValue = -1;

	// Search for max value of histogram and peak grey value
	for (i = 0; i < 256; i++)
	{
		if (pHistogram[i] > MaxValue)
		{
			MaxValue = pHistogram[i];
			PeakDN = i;
		}
	}

	rgbBack.R = rgbBack.B = 0;
	rgbBack.G = 40;
	GUI_FRect(pImage, xOff,       40, 511, 101, &rgbBack);
	GUI_FRect(pImage, xOff + 512, 40, 60, 40, &m_ColBack);

	// Paint gray value of peak
	GUI_TextOut(pImage, 544, 45, "Peak:");
	sprintf(Peak, "%d DN", PeakDN);
	GUI_TextOut(pImage, 544, 60, Peak);

	y = 140;
	GUI_Line(pImage, xOff + 2 * PeakDN, y - 99, xOff + 2 * PeakDN, y, &rgbRed);

	if (Mode == HistogramMode::LINEAR)	// linear
	{
		for (i = 0; i < 256; i++)
		{
			x = (2 * i) + xOff;
			// Normalization of max values
			y = 140 - 100 * pHistogram[i] / MaxValue;
			if (i > 0)
				GUI_Line(pImage, x - 2, y2, x, y, &rgbGreen);
			y2 = y;
		}
	}
	else if (Mode == HistogramMode::LOGARITHM)	// logarithm
	{
		float fk = logf(99) / logf(MaxValue);

		for (i = 0; i < 256; i++)
		{
			x = (2 * i) + xOff;
			// logarithmic normalization by root function
			y = 140 - (int)(powf((float)pHistogram[i], fk) + 0.999f);
			if (i > 0)
				GUI_Line(pImage, x - 2, y2, x, y, &rgbGreen);
			y2 = y;
		}
	}
	DisplayImage(&m_HistImage, m_LogPosX, m_LogPosY);
}

/*************************************************************************/
void CPSApp::CalcLowHigh(int &Low, int &High, int *pHistogram)
/*************************************************************************/
/*! @brief Calculates \p Low and \p High from histogram value for each frame.

	Only histogram values higher than 10 are used.
	@param Low Value from histogram values
	@param High Value from histogram values
	@param pHistogram Pointer to histogram values
**************************************************************************/
{
	int i;
	int MinLow = 255;
	int MaxHigh= 0;

	for (i = 0; i < 256; i++)
	{
		if (pHistogram[i] >= 10)
			break;
	}
	if (i < 255)
		Low = i;
	else
		Low = i + 1;
	MinLow = _min2(Low, MinLow);
	Low = MinLow;
	for (i = 255; i > 0; i--)
	{
		if (pHistogram[i] >= 10)
			break;
	}
	if (i > 0)
		High = i;
	else
		High = i - 1;
	MaxHigh = _max2(High, MaxHigh);
	High = MaxHigh;
}

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