/*************************************************************************\
* "$RCSfile: CDlgAppVCR.cpp,v $"                                          *
***************************************************************************
* Information by CVS or VSS:                                              *
*     Last check in on: $Date: 2010-03-11 15:29:43 $                      *
*     by                $Author: schluckebier $
*     Revision        : $Revision: 1.5 $                                 *
\*************************************************************************/
/*! \file CDlgApp.cpp
*	\brief Implementation CDlgApp Class
*	\author (c) 2008 - 2013 IMAGO Technologies GmbH
*
*	$Revision: 1.5 $
*	$Date: 2010-03-11 15:29:43 $
**************************************************************************/

#include <string.h>
#include "VIC_PS.h"
#include "CRegion.h"
#include "CZoomFactor.hpp"
#include "CLog.hpp"
#include "CPSApp.hpp"
#include "CDlg.hpp"
#include "CDlgMain.hpp"
#include "CDlgPreviewSettings.hpp"
#include "CDlgAppVCR.hpp"
#include <clk.h>

/*************************************************************************/
CDlgApp::CDlgApp(int x, int y, int Width, int Height)
		: CDlg(x, y, Width, Height, DialogId::APP)
/*************************************************************************/
/*! @brief Constructor of CDlgApp.
	@param x Horizontal position of dialog
	@param y Vertical position of dialog
	@param Width Width of dialog
	@param Height Height of dialog
**************************************************************************/
{
	extern int DDR2;
	MEM_Stat Statbuf;

	m_Recording = 0;
	m_Info = 1;
	m_First = 1;
	m_Polarity = 0;
	m_Leading = 0;
	m_Trailing = 0;
	m_TrigMode = TRG_NO;
	m_ModeChangeCmd = 0;
	m_FirstInt = 1;
	m_SType = 1;
	m_SImg = 1;

	GUI_DlgSetTitle(&m_Dlg, "VCR");

	// Icons are loaded from directory
	VIB_ChDir(VIB_Drive[1], m_pApp->GetSkinPath());
	m_Icon[ICON(ID_APP_SETUP)]   = ReadIcon("Setup.png", 98, 98);
	m_Icon[ICON(ID_APP_RESTART)] = ReadIcon("Restart.png", 98, 98);
	m_Icon[ICON(ID_APP_PLAYER)]  = ReadIcon("Player.png", 98, 98);
	m_Icon[ICON(ID_APP_LOAD)]    = ReadIcon("LoadSeq.png", 98, 98);
	m_Icon[ICON(ID_APP_PREVIEW)] = ReadIcon("B_PREV.PNG", 98, 98);
	m_Icon[ICON(ID_APP_APP)]     = ReadIcon("B_VCRApp.PNG", 98, 98);
	m_Icon[ICON(ID_APP_FIRST)]   = ReadIcon("FIRST.PNG", 48, 48);
	m_Icon[ICON(ID_APP_LAST)]    = ReadIcon("LAST.PNG", 48, 48);
	m_Icon[ICON(ID_APP_IFIRST)]  = ReadIcon("IFIRST.PNG", 48, 48);
	m_Icon[ICON(ID_APP_ILAST)]   = ReadIcon("ILAST.PNG", 48, 48);
	m_Icon[ICON(ID_APP_PLUS)]    = ReadIcon("PLUS.PNG", 48, 48);
	m_Icon[ICON(ID_APP_MINUS)]   = ReadIcon("MINUS.PNG", 48, 48);
	m_Icon[ICON(ID_APP_PLAY)]    = ReadIcon("PLAY.PNG", 80, 48);
	m_Icon[ICON(ID_APP_PAUSE)]   = ReadIcon("PAUSE.PNG", 80, 48);
	m_Icon[ICON(ID_APP_BACK)]    = ReadIcon("PBACK.PNG", 80, 48);
	m_Icon[ICON(ID_APP_SAVE)]    = ReadIcon("PSAVE.PNG", 80, 48);
	VIB_ChDir(VIB_Drive[1], "..");
	m_pApp->SetApplicationIcon(&m_Icon[ICON(ID_APP_APP)], "VCR _App");
	m_pApp->SetApplicationName("Video Recorder");

	m_CIcon[0] = *m_pApp->GetIcon(IconId::CANCEL);
	m_CIcon[1] = *m_pApp->GetIcon(IconId::OK);

	y = 32;
	GUI_DlgSetFlags(&m_Dlg, DLG_ALTDISABLE | DLG_GRADIENT);
	m_pRec = GUI_DlgAddCheckIcon(&m_Dlg,   8, y, 98, 98, "_Record", m_CIcon, &m_Recording, 10);
	GUI_DlgAddCheckIcon(&m_Dlg, 110, y, 98, 98, "_Info", m_CIcon, &m_Info, 11);

	y += 106;
	m_pDlgItem[INDEX(ID_APP_SETUP)] = GUI_DlgAddIconButton(&m_Dlg, 8, y, 98, 98,
		"_Setup", &m_Icon[ICON(ID_APP_SETUP)], ID_APP_SETUP);
	GUI_DlgAddIconButton(&m_Dlg, 110, y, 98, 98, "R_estart", &m_Icon[ICON(ID_APP_RESTART)], ID_APP_RESTART);

	y += 106;
	m_pDlgItem[INDEX(ID_APP_PLAYER)] = GUI_DlgAddIconButton(&m_Dlg, 8, y, 98, 98,
		"_Player", &m_Icon[ICON(ID_APP_PLAYER)], ID_APP_PLAYER);
	m_pDlgItem[INDEX(ID_APP_LOAD)] = GUI_DlgAddIconButton(&m_Dlg, 110, y, 98, 98,
		"_Load Seq", &m_Icon[ICON(ID_APP_LOAD)], ID_APP_LOAD);

	int iHeight = m_Icon[ICON(ID_APP_SETUP)].Height;
	m_pDlgItem[INDEX(ID_APP_PREVIEW)] = GUI_DlgAddIconButton(&m_Dlg, 8, Height - (iHeight + 8),
		98, 98, "_View", &m_Icon[ICON(ID_APP_PREVIEW)], ID_APP_PREVIEW);

	GUI_DlgAddIconButton(&m_Dlg, 110, Height - (iHeight + 8),
		98, 98, "_Back", m_pApp->GetIcon(IconId::BACK), DLG_ABORT);

	LoadIni();

	MEM_stat(DDR2, &Statbuf);
	if (m_pApp->GetCamWidth() == 1024)
		m_MaxMem = Statbuf.length - 1024 * 1024 * 32; // 32 MB free
	else
		m_MaxMem = Statbuf.length - 1024 * 1024 * 40; // 40 MB free
	m_pBaseAddr = (unsigned char*)MEM_alloc(DDR2, m_MaxMem, 128);
	if (m_pBaseAddr == NULL)
	{
		m_MaxImages = 0;
		m_UsedImages = 0;
		m_CurImage = 0;
		m_Width = 0;
		m_Height = 0;
	}
	else
	{
		m_BitsPerPixel = 8;
		m_ColorCoding = GUI_MONO8;
		Reset();
	}
}

/*************************************************************************/
CDlgApp::~CDlgApp()
/*************************************************************************/
/*! @brief Destructor.
**************************************************************************/
{
	extern int DDR2;

	for (int i = 0; i < ICON(ID_APP_XXX); i++)
		GUI_FreeCImage(&m_Icon[ICON(i)]);
	if (m_pBaseAddr)
		MEM_free(DDR2, m_pBaseAddr, m_MaxMem);
}

/******************************************************************************/
int CDlgApp::OnUserIO(int nKey)
/******************************************************************************/
/*! @brief Function processes the user inputs.
	@param nKey Last key code the user entered
	@return \p m_Activity (!= \p DLG_EOF if the user has made an input)
**************************************************************************/
{
	int Id;

	m_Activity = -1;

	if (m_pApp->IsDlgGettingFocus(m_ID))
		m_First = 1;

	if (m_pApp->IsDlgInFocus(m_ID))
	{
		// Check image size
		if (m_First)
		{
			if ((m_Width != m_pApp->GetCamWidth()) || (m_Height != m_pApp->GetCamHeight()))
				Reset();
			m_First = 0;
			m_bUpdate = true;
		}

		m_Activity = GUI_DlgWork(&m_Dlg, nKey); // m_Activity contains an event ID of the last user input
		switch (m_Activity)
		{
		case DLG_VALUE_CHANGED:
			Id = GUI_DlgGetSelectedID(&m_Dlg);
			if (Id == 10)
			{
				RecordMode();
			}
			break;
		case ID_APP_SETUP:
			Enable(0);
			Show();
			Setup();
			Enable(1);
			break;
		case ID_APP_RESTART:
			Restart();
			break;
		case ID_APP_PLAYER:
			m_pApp->StopGrabTask();
			m_pApp->m_Info = InfoType::LOGFILE;
			Enable(0);
			Show();
			Player();
			Enable(1);
			m_pApp->StartGrabTask();
			m_pApp->DisplayInfoArea(1);
			break;
		case ID_APP_LOAD:
			m_pApp->StopGrabTask();
			Enable(0);
			Show();
			Load();
			if (m_UsedImages > 0)
				GUI_DlgSetDItemState(m_pDlgItem[ICON(ID_APP_PLAYER)], DLG_I_ENABLED);
			Enable(1);
			m_pApp->StartGrabTask();
			m_pApp->DisplayInfoArea(1);
			break;
		case ID_APP_PREVIEW:	// Switch to preview window
			Enable(0);
			m_pApp->SetDlgNewFocus(DialogId::PREV);
			m_bIsVisible = false;
			Enable(1);
			break;
		case DLG_MOUSE_PRESSED:	// Handle mouse clicks in info and graph windows
			m_pApp->ChangeInfoArea(&m_Dlg);
			break;
		case DLG_ABORT:	// Return to main dialog
			m_Recording = 0;
			m_pApp->SetDlgNewFocus(DialogId::MAIN);
			m_pApp->m_View = m_pApp->m_ViewOld;
			m_bIsVisible = false;
			break;
		}

		switch (m_ModeChangeCmd)
		{
		case REC_START:
			if (m_Recording == 0 && GUI_DlgGetDItemState(m_pRec) == DLG_I_ENABLED)
			{
				m_Recording = 1;
				RecordMode();
				m_Activity = 1;
			}
			break;
		case REC_STOP:
			if (m_Recording == 1)
			{
				m_Recording = 0;
				RecordMode();
				m_Activity = 1;
			}
			break;
		}
		m_ModeChangeCmd = REC_NOTHING;

		if (m_Activity)
			GUI_DlgRefresh(&m_Dlg);
	}
	return m_Activity;
}

/******************************************************************************/
int CDlgApp::OnImage(YIMAGE *pSrc, CIMAGE *pDst)
/******************************************************************************/
/*! @brief Application process function.

	The function is called from the grab task for every grabbed image. The
	function runs simultaneous to the grabbing of the next image. The image
	\p pDst is displayed by the main task by Ethernet.\n
	The priority of the grab task is higher than of the main task, so a slow
	network connection will not slow down the grab task. If the main task
	becomes to slow to display all it will drop images.
	@param pSrc Pointer to source image (Grabbed)
	@param pDst Pointer to display image (Displayed)
	@return Image format\n(eAPP_IMG_TYPE)
*******************************************************************************/
{
	int ret;
	YIMAGE yImage;

	if (m_Recording)
	{
		ret = PutImage(pSrc->pY);
		if (m_Info)
			DrawInfo(pSrc);
		if (ret < 0)
			RecordEnd();
	}

	if (m_pApp->IsAppView() == false)	// Application window is not active
		return -1;

	// Copy the image from pSrc to pDst
	yImage = GUI_YImage(pDst->pR, pDst->Width, pDst->Height);
	GUI_CopyYArea(pSrc, &yImage, 0, 0, pSrc->Width, pSrc->Height, 0, 0);
	return AppImageType::GRAY;
}

/*************************************************************************/
int CDlgApp::LoadIni()
/*************************************************************************/
/*! @brief Loads VCR-Parameter.
**************************************************************************/
{
	Config *pVCRConfig = NULL;

	pVCRConfig = Config::loadFromIni(VIB_Drive[1], "GVCR.ini", "VCR");
	if (pVCRConfig == NULL)
		pVCRConfig = new Config("VCR");

	//=== Get values ===
	m_TrigMode  = pVCRConfig->getValue("VCR_MODE", TRG_NO);
	m_ImageType = pVCRConfig->getValue("VCR_IMAGETYPE", I_RJPG);
	m_Ring      = pVCRConfig->getValue("VCR_RING", 0);
	m_Polarity  = pVCRConfig->getValue("VCR_POLARITY", 0);
	m_Leading   = pVCRConfig->getValue("VCR_LEADING", 0);
	m_Trailing  = pVCRConfig->getValue("VCR_TRAILING", 0);

	delete pVCRConfig;
	return 0;
}

/*************************************************************************/
int CDlgApp::SaveIni()
/*************************************************************************/
/*! @brief Saves VCR-Parameter.
**************************************************************************/
{
	Config *pVCRConfig = NULL;
	int Err;
	
	pVCRConfig = Config::loadFromIni(VIB_Drive[1], "GVCR.ini", "VCR");
	if (pVCRConfig == NULL)
		pVCRConfig = new Config("VCR");

	//=== Set values ===
	pVCRConfig->setValue("VCR_MODE", m_TrigMode);
	pVCRConfig->setValue("VCR_IMAGETYPE", m_ImageType);
	pVCRConfig->setValue("VCR_RING", m_Ring);
	pVCRConfig->setValue("VCR_POLARITY", m_Polarity);
	pVCRConfig->setValue("VCR_LEADING", m_Leading);
	pVCRConfig->setValue("VCR_TRAILING", m_Trailing);
	Err = pVCRConfig->saveToIni(VIB_Drive[1], "GVCR.ini");
	
	delete pVCRConfig;
	return Err;
}

/*************************************************************************/
void CDlgApp::Reset(int Width, int Height)
/*************************************************************************/
/*! @brief Resets (clears) all VCR images.
**************************************************************************/
{
	if (Width == -1)
		m_Width = m_pApp->GetCamWidth();
	if (Height == -1)
		m_Height = m_pApp->GetCamHeight();

	m_CurImage = 0;
	m_UsedImages = 0;
	if (m_ImageType == I_RJPG)
		m_MaxImages = 1;
	else
		m_MaxImages = m_MaxMem / (m_Width * m_Height * (m_BitsPerPixel / 8));
/*	for (int i=0; i<VCR_MAXIMAGE; i++)
	{
		if (m_Ring)
			m_Image[i].pAddr = m_pBaseAddr + m_MaxMem;
		else
			m_Image[i].pAddr = NULL;
		m_Image[i].ImageSize = 0;
		m_Image[i].AktTime = 0;
	}*/
	m_Image[0].ImageSize = 0;
	m_Image[0].AktTime = 0;

	m_Image[0].pAddr = m_pBaseAddr;
	m_StartTime = CLK_getltime();
	GUI_DlgSetDItemState(m_pDlgItem[ICON(ID_APP_PLAYER)], DLG_I_DISABLED);
	GUI_DlgSetDItemState(m_pRec, DLG_I_ENABLED);
}

/*************************************************************************/
void CDlgApp::Setup()
/*************************************************************************/
/*! @brief Calls Setup dialog.
**************************************************************************/
{
	const int iHeight = m_pApp->GetIcon(IconId::OK)->Height + 8;
	const int MaxImages = m_MaxMem / (m_Width * m_Height * (m_BitsPerPixel / 8));
	int nSelect;
	int ret;
	int PosX, PosY;
	GUI_DLG *pDialog;

	if (m_ImageType == I_RJPG)
		nSelect = 2;
	else
		nSelect = 1;

	pDialog = new GUI_DLG;
	GUI_DlgGetZeroZero(&m_Dlg, &PosX, &PosY);
	GUI_DlgCreate2(pDialog, m_Dlg.rgbDlg.Width, m_Dlg.rgbDlg.Height - PosY, "Setup");
	GUI_DlgSetGradCol(pDialog, m_pApp->GetGradCol());
	GUI_DlgAddLabel(pDialog,   8, 30, "_Trigger mode", 10);
	GUI_DlgAddLabel(pDialog, 110, 30, "_Image type", 11);
	GUI_DlgAddSelectBox(pDialog,   8, 50, 98, 68, "No\nEdge\nState\n", &m_TrigMode, 10);
	GUI_DlgAddSelectBox(pDialog, 110, 50, 98, 68, "RAW\nJPEG\n", &nSelect, 11);
	GUI_DlgAddCheckBox2(pDialog,   8, 126, 98, 32, "_Polarity", &m_Polarity, 13);
	GUI_DlgAddCheckBox2(pDialog, 110, 126, 98, 32, "_Ring", &m_Ring, 12);
	GUI_DlgAddLabel(pDialog,   8, 166, "_Leading", 14);
	GUI_DlgAddLabel(pDialog, 110, 166, "Tr_ailing", 15);
	GUI_DlgAddVEditBox(pDialog,  8, 186, 80, &m_Leading, 0, MaxImages, 14);
	GUI_DlgAddVEditBox(pDialog, 110, 186, 80, &m_Trailing, 0, MaxImages, 15);
	GUI_DlgAddButton(pDialog, 8, 218, 200, 38, "_Save .INI", 100);
	GUI_DlgAddIconButton(pDialog,   8, pDialog->rgbDlg.Height - iHeight,
		98, 98, "_OK", m_pApp->GetIcon(IconId::OK), 20);
	GUI_DlgAddIconButton(pDialog, 110, pDialog->rgbDlg.Height - iHeight,
		98, 98, "_Cancel", m_pApp->GetIcon(IconId::CANCEL), DLG_ABORT);
	ret = GUI_DlgModal(pDialog, m_pApp->GetDispImage(), 0, PosY + m_Dlg.y, m_fKB);

	GUI_DlgFree(pDialog);
	delete pDialog;

	if (ret != DLG_ABORT)
	{
		if (pDialog->Changed)
		{
			m_ImageType = (nSelect == 1) ? I_RRAW : I_RJPG;
			Reset();
		}

		//=== Trigger ===
		if (m_TrigMode>TRG_NO)
		{
			m_FirstInt = 1;
			VIB_OpenDin(6, (VIB_FxnDioIntHandler)TriggerISR);
		}
		else
		{
			VIB_CloseDin();
		}
	}
	if (ret == 100)
		SaveIni();
}

/*************************************************************************/
int CDlgApp::PutImage(void *pBuffer)
/*************************************************************************/
/*! @brief Adds an image to VCR buffer.
**************************************************************************/
{
	int ImageSize;
	int i, j;
	int iWidth;
	unsigned char *pAddr;
	unsigned char *pTemp;
	YIMAGE yImage;

	if (m_pBaseAddr == NULL)
		return -1;

	pAddr = m_Image[m_CurImage].pAddr;
	
	//=== Ringbuffer ===
	if (m_Ring)
	{
		switch (m_ImageType)
		{
		case I_RJPG:
			pTemp = (unsigned char*) VIB_MemAlloc(2 * m_Width * m_Height);

			yImage = GUI_YImage((unsigned char*)pBuffer, m_Width, m_Height);
			ImageSize = JPEG_WriteYImageMem(pTemp, &yImage);
			ImageSize = (ImageSize & ~3) + 4; // alignment
			break;
		default:
			ImageSize = m_Width * m_Height * (m_BitsPerPixel / 8);
			break;
		}
		if (pAddr+ImageSize >= m_pBaseAddr + m_MaxMem)
			m_Image[m_CurImage].pAddr = m_pBaseAddr;
		if (m_Image[m_CurImage].pAddr <= m_Image[0].pAddr && m_CurImage)
		{
			if (m_Image[1].pAddr < m_Image[0].pAddr)
			{
				i = 1;
			}
			else
			{
				i = 0;
				while (m_Image[m_CurImage].pAddr + ImageSize > m_Image[i].pAddr)
					i++;
			}
			if (i)
			{
				for (j = 0; j <= m_CurImage; j++)
				{
					m_Image[j].pAddr     = m_Image[j + 1].pAddr;
					m_Image[j].ImageSize = m_Image[j + 1].ImageSize;
					m_Image[j].AktTime   = m_Image[j + 1].AktTime;
				}
				m_CurImage -= i;
				m_UsedImages = m_CurImage;
				m_Image[m_CurImage + 1].pAddr = m_pBaseAddr + m_MaxMem;
			}
		}
		pAddr = m_Image[m_CurImage].pAddr;

		if (m_UsedImages < VCR_MAXIMAGE)
		{
			switch (m_ImageType)
			{
			case I_RJPG:
				VIB_DMACopy(3, 1, pTemp, pAddr, ImageSize);
				VIB_MemFree(pTemp);
				m_MaxImages = m_UsedImages + 1;
				ImageSize = (ImageSize & ~3) + 4; // alignment
				break;
			default:
				//VIB_MemCopy(pBuffer, pAddr, ImageSize);
				iWidth = m_Width * (m_BitsPerPixel / 8);
				VIB_MemCopy2D(pBuffer, pAddr, iWidth, m_Height, iWidth, iWidth);
				break;
			}
			m_Image[m_CurImage].ImageSize = ImageSize;
			m_Image[m_CurImage].AktTime = CLK_getltime() - m_StartTime;
			m_CurImage++;
			m_UsedImages = m_CurImage;
			m_Image[m_CurImage].pAddr = pAddr + ImageSize;
			if (m_Image[m_CurImage].pAddr >= m_pBaseAddr + m_MaxMem)
				m_Image[m_CurImage].pAddr = m_pBaseAddr;
			return m_CurImage;
		}
	}
	//=== Normal buffer ===
	else
	{
		ImageSize = m_Width * m_Height * (m_BitsPerPixel / 8); // provisorisch
		if ((pAddr + ImageSize < m_pBaseAddr + m_MaxMem) && (m_CurImage < VCR_MAXIMAGE))
		{
			switch (m_ImageType)
			{
			case I_RJPG:
				yImage = GUI_YImage((unsigned char*)pBuffer, m_Width, m_Height);
				ImageSize = JPEG_WriteYImageMem(pAddr, &yImage);
				m_MaxImages++;
				ImageSize = (ImageSize & ~3) + 4; // alignment
				break;
			default:
				ImageSize = m_Width * m_Height * (m_BitsPerPixel / 8);
				iWidth = m_Width * (m_BitsPerPixel / 8);
				//VIB_MemCopy(pBuffer, pAddr, ImageSize);
//				memcpy(pAddr, pBuffer, ImageSize);
				VIB_MemCopy2D(pBuffer, pAddr, iWidth, m_Height, iWidth, iWidth);
				break;
			}
			m_Image[m_CurImage].ImageSize = ImageSize;
			m_Image[m_CurImage].AktTime = CLK_getltime() - m_StartTime;
			m_CurImage++;
			m_UsedImages++;
			if (m_CurImage < VCR_MAXIMAGE)
				m_Image[m_CurImage].pAddr = pAddr + ImageSize;
			return m_CurImage;
		}
	}
	return -1;
}

/*************************************************************************/
void CDlgApp::DrawInfo(YIMAGE *pyImage)
/*************************************************************************/
/*! @brief Prints image and time information into \p pyImage.
	@param pyImage Image from recorder
**************************************************************************/
{
	int Min, Sek, MSek, AktTime;
	unsigned int msec, fps;
	float fPer;
	char Text[64];

	if (m_CurImage > 0)
	{
		AktTime = m_Image[m_CurImage-1].AktTime;
		MSek =  (AktTime % 1000) / 10;
		Sek  =  (AktTime / 1000) % 60;
		Min  = ((AktTime / 1000) / 60) % 60;

		msec = VIB_Clock() / (VIB_Freq / 8);
		fps = (int)(1000000.0 / msec + 0.5);

		if (m_Ring)
			sprintf(Text, "%3d [%d] t: %02d:%02d.%02d %d fps",
				m_CurImage, m_MaxImages, Min, Sek, MSek, fps);
		else
		{
			fPer = (100.0 / m_MaxMem) * (m_Image[m_CurImage].pAddr - m_pBaseAddr);
			sprintf(Text, "%3d [%4.2f %%] t: %02d:%02d.%02d %d fps",
				m_CurImage, fPer, Min, Sek, MSek, fps);
		}
		GUI_TextOutY(pyImage, m_pApp->m_PVPosX + 2, m_pApp->m_PVPosY + 2, Text);
	}
}

/*************************************************************************/
void CDlgApp::Player()
/*************************************************************************/
/*! @brief Creates and executes player dialog.
**************************************************************************/
{
	const int xPos = m_pApp->m_LogPosX;
	const int yPos = m_pApp->m_LogPosY;
	int ret, oldVal, refresh = 0;
	int nKey;
	int StartPos, StopPos, AktPos, TempPos;
	int bPlay = 1;
	int MaxNumber = m_UsedImages-1;
	unsigned int msec, fps;
	volatile int Delay = 0;
	GUI_DLG *pDialog;
	YIMAGE yCamImage, yImageTemp;
	CIMAGE PIcon[2];
	CIMAGE *pImage = m_pApp->GetDispImage();
	char Text[64];

	StartPos = 0;
	AktPos = 0;
	TempPos = -1;
	StopPos = MaxNumber;

	PIcon[0] = m_Icon[ICON(ID_APP_PLAY)];
	PIcon[1] = m_Icon[ICON(ID_APP_PAUSE)];

	pDialog = new GUI_DLG;
	GUI_DlgCreate2(pDialog, m_pApp->m_LogImage.Width, m_pApp->m_LogImage.Height, "Player");
	GUI_DlgSetGradCol(pDialog, m_pApp->GetGradCol());
	GUI_DlgAddCheckIcon(pDialog, 176, 30, 80, 48, "", PIcon, &bPlay, 10);
	GUI_DlgAddSlider(pDialog, 64, 86, 296, 48, &AktPos, 0, MaxNumber, 13);
	GUI_DlgAddVEditBox(pDialog, 192, 134, 40, &AktPos, 0, MaxNumber, 14);
	GUI_DlgAddIconButton(pDialog, 264, 30, 48, 48, "", &m_Icon[ICON(ID_APP_PLUS)], 23);
	GUI_DlgAddAccel(pDialog, '+', 23);
	GUI_DlgAddIconButton(pDialog, 320, 30, 48, 48, "", &m_Icon[ICON(ID_APP_FIRST)], 24);
	GUI_DlgAddAccel(pDialog, '>', 24);
	GUI_DlgAddIconButton(pDialog, 376, 30, 48, 48, "", &m_Icon[ICON(ID_APP_ILAST)], 25);
	GUI_DlgAddVEditBox(pDialog, 376, 82, 40, &StopPos, 0, MaxNumber, 15);
	GUI_DlgAddIconButton(pDialog, 440, 30, 80, 48, "", &m_Icon[ICON(ID_APP_SAVE)], 11);
	GUI_DlgAddIconButton(pDialog, 528, 30, 80, 48, "", &m_Icon[ICON(ID_APP_BACK)], DLG_ABORT);
	GUI_DlgAddEditSlider(pDialog, 440, 86, 124, "_Delay", (int*)&Delay, 0, 500, 16);
	GUI_DlgAddIconButton(pDialog, 8, 30, 48, 48, "", &m_Icon[ICON(ID_APP_IFIRST)], 22);
	GUI_DlgAddVEditBox(pDialog, 8, 82, 40, &StartPos, 0, MaxNumber, 12);
	GUI_DlgAddIconButton(pDialog, 64, 30, 48, 48, "", &m_Icon[ICON(ID_APP_LAST)], 20);
	GUI_DlgAddAccel(pDialog, '<', 20);
	GUI_DlgAddIconButton(pDialog, 120, 30, 48, 48, "", &m_Icon[ICON(ID_APP_MINUS)], 21);
	GUI_DlgAddAccel(pDialog, '-', 21);
	GUI_DlgAddText(pDialog, 64, 134, "0");
	sprintf(Text, "%d", MaxNumber);
	GUI_DlgAddText(pDialog, 336, 134, Text);

	GUI_DlgUpdate(pDialog, pImage, xPos, yPos);

	yCamImage = GUI_YImage(NULL, m_Width, m_Height);

	m_pApp->SetTaskDelay(10);

	//=== Main loop ===
	do
	{
		nKey = getc(m_fKB);
		oldVal = AktPos;
		ret = GUI_DlgWork(pDialog, nKey);
		refresh = (oldVal != AktPos);

		if (nKey == ' ')
			bPlay = !bPlay;
		switch (ret)
		{
		case DLG_FOCUS_CHANGED:
		case DLG_VALUE_CHANGED:
			refresh = 1;
			break;
		case DLG_MOUSE_PRESSED:	// Handle mouse clicks in info and graph windows
			m_pApp->ChangeGraphArea(pDialog);
			m_pApp->DisplayGraph();
			break;
		case 11:
			GUI_DlgEnable(pDialog, 0);
			GUI_DlgUpdate(pDialog, pImage, xPos, yPos);
			Save(StartPos, StopPos, pDialog, &AktPos);
			GUI_DlgEnable(pDialog, 1);
			refresh = 1;
			break;
		case 20: // '<-'
			AktPos = 0;
			refresh = 1;
			break;
		case 21: // '-'
			AktPos--;
			if (AktPos < 0)
				AktPos = 0;
			refresh = 1;
			break;
		case 22:
			StartPos = AktPos;
			refresh = 1;
			break;
		case 23: // '+'
			AktPos++;
			if (AktPos >= MaxNumber)
				AktPos = MaxNumber;
			refresh = 1;
			break;
		case 24: // '->'
			AktPos = MaxNumber;
			refresh = 1;
			break;
		case 25:
			StopPos = AktPos;
			refresh = 1;
			break;
		}
		if (nKey == EOF)
			_UNSET(m_fKB, _STATEOF);

		if (bPlay)
		{
			AktPos++;
			if (AktPos > StopPos)
				AktPos = StartPos;
		}

		if (bPlay || refresh)
		{
			int Min, Sek, MSek, AktTime;
			
			AktTime = m_Image[AktPos].AktTime;
			MSek  =  (AktTime % 1000) / 10;
			Sek   =  (AktTime / 1000) % 60;
			Min   = ((AktTime / 1000) / 60) % 60;

			msec = VIB_Clock() / (VIB_Freq / 8);
			fps = (int)(1000000.0 / msec + 0.5);
			sprintf(Text, "%4d [%d] t: %02d:%02d.%02d %d fps", AktPos, MaxNumber, Min, Sek, MSek, fps);

			if (AktPos != TempPos)
			{
				if (m_ImageType == I_RRAW)
				{
					yImageTemp = GUI_YImage(m_Image[AktPos].pAddr, m_Width, m_Height);
					GUI_CopyYArea(&yImageTemp, &yCamImage, 0, 0, m_Width, m_Height, 0, 0);
				}
				else if (m_ImageType == I_RJPG)
				{
					JPEG_ReadYImageMem(m_Image[AktPos].pAddr, &yCamImage);
				}
			}
			TempPos = AktPos;
			if (m_Info)
				GUI_TextOutY(&yCamImage, m_pApp->m_PVPosX + 2, m_pApp->m_PVPosY + 2, Text);
			m_pApp->PostCalc(&yCamImage);
			m_pApp->PostCam(&yCamImage);
			m_pApp->DisplayDynamicElements();

			GUI_DlgRefresh(pDialog);
			if (Delay > 0)
				GUI_Rect(&pDialog->rgbDlg, 440 - 4, 86 - 4, 174, 48, &rgbRed);
			GUI_DlgPaint(pDialog, pImage, xPos, yPos);
			refresh = 0;
		}
		if (bPlay && Delay)
			TSK_sleep(Delay);
		if (!bPlay)
			TSK_sleep(50);
	} while (ret != DLG_ABORT);

	m_pApp->SetTaskDelay(20);
	GUI_FreeYImage(&yCamImage);

	GUI_DlgFree(pDialog);
	delete pDialog;
}

/*************************************************************************/
void CDlgApp::Save(int First, int Last, GUI_DLG *pDialog, int *pAktPos)
/*************************************************************************/
/*! @brief Creates and executes Save image/sequence dialog.
**************************************************************************/
{
	int i, j;
	int FSize;
	char Filename[16];
	char Path[256];
	char Text[256];
	char *FType;
	YIMAGE yCamImage, yImageTemp;
	CIMAGE *pImage = m_pApp->GetDispImage();
	GUI_DLG DlgSave;
	bool bFlag;

	DlgSave = GUI_DlgCreate(220, 226, "Save");
	GUI_DlgSetGradCol(&DlgSave, m_pApp->GetGradCol());
	GUI_DlgAddLabel(&DlgSave,   8, 32, "_Format", 10);
	GUI_DlgAddLabel(&DlgSave, 114, 32, "_Type", 11);
	GUI_DlgAddSelectBox(&DlgSave, 8, 52, 98, 60, ".BMP\n.PNG\n.JPG\n", &m_SType, 10);
	GUI_DlgAddSelectBox(&DlgSave, 114, 52, 98, 60, "Image\nSequence\nAVI\n", &m_SImg, 11);
	GUI_DlgAddIconButton(&DlgSave, 8, 120, 98, 98, "_OK", m_pApp->GetIcon(IconId::OK), 20);
	GUI_DlgAddIconButton(&DlgSave, 114, 120, 98, 98, "_Cancel", m_pApp->GetIcon(IconId::CANCEL), DLG_ABORT);
	i = GUI_DlgModal(&DlgSave, pImage, -1, -1, m_fKB);
	GUI_DlgFree(&DlgSave);
	
	if (i == DLG_ABORT)
		return;

	switch (m_SType)
	{
	case ImageFormat::BMP:
		FType = ".BMP";
		break;
	case ImageFormat::PNG:
		FType = ".PNG";
		break;
	case ImageFormat::JPG:
		FType = ".JPG";
		break;
	}

	bFlag = true;

	switch (m_SImg)
	{
	case 1:	//=== Single image ============================================
		First = *pAktPos;
		Last = First;
		sprintf(Filename, "I%04d%s", *pAktPos, FType);
		if (VIB_FindFile2(VIB_Drive[1], Filename) > 0)
		{
			sprintf(Text, "\nReplace file\n%s?\n\n", Filename);
			if (m_pApp->MessageBox(Text, 1, "!") == 1)
				VIB_DeleteFile(VIB_Drive[1], Filename);
			else
				bFlag = false;
		}
		break;
	case 2:	//=== Sequence ================================================
		if (GUI_SelDirBox(VIB_Drive[1], NULL, "Select directory", Path, m_fKB))
		{
			FSize = VIB_FindFile2(VIB_Drive[1], Path);

			switch (FSize)
			{
			case FAT_DIRECTORY:	// Dir exists
				if (m_pApp->MessageBox("\nOverwrite directory?\n\n", 1, "!") == 1)
				{
					GUI_MessageBox(NULL, "\n\nDeleting sequence ...\n\n\n", NULL, GUI_MBSIMPLE);
					VIB_ChDir(VIB_Drive[1], Path);
					for (i = 0; i < m_UsedImages; i++)
					{
						sprintf(Filename, "I%04d%s", i, FType);
						VIB_DeleteFile(VIB_Drive[1], Filename);
					}
					VIB_ChDir(VIB_Drive[1], "..");
				}
				else
				{
					bFlag = false;
				}
				break;
			case FAT_NOFILE: // New dir
				VIB_MkDir(VIB_Drive[1], Path);
				break;
			default: // File exists
				m_pApp->MessageBox("\nFile selected\n\n", 0, "!");
				bFlag = false;
				break;
			}
		}
		else
		{
			bFlag = false;
		}
		m_pApp->RecBackground(1);
		Show();
		break;
	case 3:	//=== AVI =====================================================
		bFlag = false;
		m_pApp->SetTP(TextStyle::MONOSPACE);
		i = GUI_SaveFileBox(VIB_Drive[1], pImage, "Save AVI File", Path, "AVI", m_fKB);
		m_pApp->SetTP(TextStyle::NORMAL);
		m_pApp->RecBackground(1);
		if (i == 1)
		{
			FSize = VIB_FindFile2(VIB_Drive[1], Path);
			if (FSize > 0)
			{
				Show();
				if (m_pApp->MessageBox("\nOverwrite file?\n\n", 1, "!") != 1)
					break;
				else
					VIB_DeleteFile(VIB_Drive[1], Path);
			}

			j = 0;
			yCamImage = GUI_YImage(NULL, m_Width, m_Height);

			GUI_AVICreateFile(VIB_Drive[1], Path, m_Width, m_Height, 8, Last - First + 1);

			for (i = First; i <= Last; i++)
			{
				*pAktPos = i;
				GUI_DlgUpdate(pDialog, pImage, m_pApp->m_LogPosX, m_pApp->m_LogPosY);

				// Create Image
				switch (m_ImageType)
				{
				case I_RJPG:
					JPEG_ReadYImageMem(m_Image[i].pAddr, &yCamImage);
					break;
				case I_RRAW:
					yImageTemp = GUI_YImage(m_Image[i].pAddr, m_Width, m_Height);
					GUI_CopyYArea(&yImageTemp, &yCamImage, 0, 0, m_Width, m_Height, 0, 0);
					break;
				}

				// Display image
				m_pApp->PostCalc(&yCamImage);
				m_pApp->PostCam();
				m_pApp->DisplayDynamicElements();
				GUI_MessageBox(NULL, "\nWriting sequence ...\n\n", NULL, GUI_MBSIMPLE);

				// save image
				GUI_AVIWriteImageY(VIB_Drive[1], Path, &yCamImage, j);
				j++;
			}
			GUI_FreeYImage(&yCamImage);
		}
		break;
	}

	//=== Write file(s) ===================================================
	if (bFlag)
	{
		VIB_ChDir(VIB_Drive[1], Path);
		GUI_MessageBox(NULL, "\nWriting sequence ...\n\n", NULL, GUI_MBSIMPLE);
		j = 0;

		yCamImage = GUI_YImage(NULL, m_Width, m_Height);

		for (i = First; i <= Last; i++)
		{
			*pAktPos = i;
			GUI_DlgUpdate(pDialog, pImage, m_pApp->m_LogPosX, m_pApp->m_LogPosY);

			// create Image
			switch (m_ImageType)
			{
			case I_RJPG:
				JPEG_ReadYImageMem(m_Image[i].pAddr, &yCamImage);
				break;
			case I_RRAW:
				yImageTemp = GUI_YImage(m_Image[i].pAddr, m_Width, m_Height);
				GUI_CopyYArea(&yImageTemp, &yCamImage, 0, 0, m_Width, m_Height, 0, 0);
				break;
			}
			if (m_SImg != 1)
				sprintf(Filename, "I%04d%s", j, FType);

			// Display image
			m_pApp->PostCalc(&yCamImage);
			m_pApp->PostCam();
			m_pApp->DisplayDynamicElements();
			GUI_MessageBox(NULL, "\nWriting sequence ...\n\n", NULL, GUI_MBSIMPLE);

			// Save image
			switch (m_SType)
			{
			case ImageFormat::BMP:
				GUI_WriteYImage(VIB_Drive[1], Filename, &yCamImage);
				break;
			case ImageFormat::PNG:
				PNG_WriteYImage(VIB_Drive[1], Filename, &yCamImage);
				break;
			case ImageFormat::JPG:
				JPEG_WriteYImage(VIB_Drive[1], Filename, &yCamImage);
				break;
			}
			j++;
		}
		GUI_FreeYImage(&yCamImage);
		VIB_ChDir(VIB_Drive[1], "\\");
	}
}

/*************************************************************************/
void CDlgApp::Load()
/*************************************************************************/
/*! @brief Loads a sequence.
**************************************************************************/
{
	int i;
	int nType;
	int Width, Height, Depth;
	int FSize;
	bool bFlag = false;
	char Path[256];
	char Filename[16];
	char Text[64];
	char *FType;
	YIMAGE yImage;

	m_pApp->SetTP(TextStyle::MONOSPACE);
	i = GUI_SelDirBox(VIB_Drive[1], m_pApp->GetDispImage(), "Select directory", Path, m_fKB);
	m_pApp->SetTP(TextStyle::NORMAL);
	m_pApp->RecBackground(1);
	Show();
	if (i == 0)
		return;

	FSize = VIB_FindFile2(VIB_Drive[1], Path);

	switch (FSize)
	{
	case FAT_DIRECTORY:	// Dir exists
		break;
	case FAT_NOFILE: // New dir
		strcat(Path, ".AVI");
		FSize = VIB_FindFile2(VIB_Drive[1], Path);
		if (FSize > 0)
			LoadAVI(Path);
		return;
//		break;
	default: // File exists
		return;
//		break;
	}

	VIB_ChDir(VIB_Drive[1], Path);

	for (nType = ImageFormat::BMP; nType <= ImageFormat::JPG; nType++)
	{
		switch (nType)
		{
		case ImageFormat::BMP:
			FType = ".BMP";
			break;
		case ImageFormat::PNG:
			FType = ".PNG";
			break;
		case ImageFormat::JPG:
			FType = ".JPG";
			break;
		}

		sprintf(Filename, "I%04d%s", 0, FType);
		if (VIB_FindFile2(VIB_Drive[1], Filename) > 0)
		{
			bFlag = true;
			break;
		}
	}

	if (bFlag == false)
	{
		m_pApp->MessageBox("\nNo file found!\n\n", 0, "!!");
		VIB_ChDir(VIB_Drive[1], "\\");
		return;
	}

	switch (nType)
	{
	case ImageFormat::BMP:
		GUI_GetBMPInfo(VIB_Drive[1], Filename, &Width, &Height, &Depth);
		break;
	case ImageFormat::PNG:
		PNG_GetInfo(VIB_Drive[1], Filename, &Width, &Height, &Depth);
		break;
	case ImageFormat::JPG:
		JPEG_GetInfo(VIB_Drive[1], Filename, &Width, &Height, &Depth);
		break;
	}
	if (Width < 0 || Height < 0)
	{
		m_pApp->MessageBox("\nWrong image size!\n\n", 0, "!!");
		VIB_ChDir(VIB_Drive[1], "\\");
		return;
	}

	Reset(Width, Height);
	i = 0;
	do
	{
		sprintf(Filename, "I%04d%s", i, FType);
		if (VIB_FindFile2(VIB_Drive[1], Filename) > 0)
		{
			switch (nType)
			{
			case ImageFormat::BMP:
				yImage = GUI_ReadNewYImage(VIB_Drive[1], Filename);
				break;
			case ImageFormat::PNG:
				yImage = PNG_ReadNewYImage(VIB_Drive[1], Filename);
				break;
			case ImageFormat::JPG:
				yImage = JPEG_ReadNewYImage(VIB_Drive[1], Filename);
				FType = ".JPG";
				break;
			}
			if (yImage.Width == m_Width && yImage.Height == m_Height)
			{
				PutImage(yImage.pY);

				// Display image
				m_pApp->PostCalc(&yImage);
				m_pApp->PostCam();
				m_pApp->DisplayDynamicElements();
				sprintf(Text, "\nReading %s ...\n\n", Filename);
				GUI_MessageBox(NULL, Text, NULL, GUI_MBSIMPLE);
			}
			else
			{
				bFlag = false;
			}
			GUI_FreeYImage(&yImage);
		}
		else
		{
			bFlag = false;
		}
		i++;
	} while (bFlag);
	VIB_ChDir(VIB_Drive[1], "\\");
}

/*************************************************************************/
void CDlgApp::LoadAVI(char *pPath)
/*************************************************************************/
/*! @brief Loads AVI-file.
**************************************************************************/
{
	int Width, Height, Depth, Frames;
	YIMAGE yImage;
	char Text[64];

	GUI_AVIReadHeader(VIB_Drive[1], pPath, &Width, &Height, &Depth, &Frames);
	if (Depth != 8 || Frames <= 0)
		return;

	Reset(Width, Height);

	yImage = GUI_YImage(NULL, Width, Height);
	for (int i = 0; i < Frames; i++)
	{
		GUI_AVIReadImageY(VIB_Drive[1], pPath, &yImage, i);
		if (PutImage(yImage.pY) < 0)
			break;

		// Display image
		m_pApp->PostCalc(&yImage);
		m_pApp->PostCam();
		m_pApp->DisplayDynamicElements();
		sprintf(Text, "\nReading %d/%d ...\n\n", i, Frames);
		GUI_MessageBox(NULL, Text, NULL, GUI_MBSIMPLE);
	}
	GUI_FreeYImage(&yImage);
}

/*************************************************************************/
void CDlgApp::RecordMode()
/*************************************************************************/
/*! @brief Switches dialog to record mode.
**************************************************************************/
{
	if (m_UsedImages == 0)
	{
		m_StartTime = CLK_getltime();
		GUI_DlgSetDItemState(m_pDlgItem[ICON(ID_APP_PLAYER)], DLG_I_ENABLED);
	}
	GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_SETUP)],
		m_Recording ? DLG_I_DISABLED : DLG_I_ENABLED);
	GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_PREVIEW)],
		m_Recording ? DLG_I_DISABLED : DLG_I_ENABLED);
	GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_LOAD)],
		m_Recording ? DLG_I_DISABLED : DLG_I_ENABLED);
}

/*************************************************************************/
void CDlgApp::RecordEnd()
/*************************************************************************/
/*! @brief Switches dialog to end of recording state.
**************************************************************************/
{
	m_Recording = 0;
	GUI_DlgSetDItemState(m_pRec, DLG_I_DISABLED);
	GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_SETUP)], DLG_I_ENABLED);
	GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_PREVIEW)], DLG_I_ENABLED);
	GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_LOAD)], DLG_I_ENABLED);
	if (m_UsedImages > 0)
		GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_PLAYER)], DLG_I_ENABLED);
	m_bUpdate = true;
}

/*************************************************************************/
void CDlgApp::Restart()
/*************************************************************************/
/*! @brief Restart recorder.
**************************************************************************/
{
	if (m_Dlg.Enabled)
	{
//		m_Recording = 0;
		Reset();
		GUI_DlgSetDItemState(m_pDlgItem[INDEX(ID_APP_SETUP)], DLG_I_ENABLED);
		GUI_DlgSetDItemState(m_pRec, DLG_I_ENABLED);
	}
}

/*************************************************************************/
void CDlgApp::Trigger(unsigned int DinValue)
/*************************************************************************/
/*! @brief Handling of Trigger.
**************************************************************************/
{
	const int Polarity = pDlgApp->m_Polarity;
	const int Mode = pDlgApp->m_TrigMode;

	if (pDlgApp->m_FirstInt)
	{
		pDlgApp->m_FirstInt = 0;
		return;
	}

	//=== Rising edge =====================================================
	//=== Input 2 ===
	if (((DinValue >> 8) & 2) && (((DinValue & 2) >> 1) == Polarity))
	{
		if (Mode == TRG_STATE) // Start
		{
			pDlgApp->m_ModeChangeCmd = REC_START;
		}
		else
		{
			if (pDlgApp->m_Recording == 1)
				pDlgApp->m_ModeChangeCmd = REC_STOP;
			else
				pDlgApp->m_ModeChangeCmd = REC_START;
		}
	}
	//=== Input 3 ===
	if (((DinValue >> 8) & 4) && (((DinValue & 4) >> 2) == Polarity)) // Restart
	{
		pDlgApp->Restart();
	}

	//=== Falling edge ====================================================
	if (((DinValue >> 8) & 2) && (((DinValue & 2) >> 1) != Polarity))
	{
		if (Mode == TRG_STATE) // Stop
			pDlgApp->m_ModeChangeCmd = REC_STOP;
	}
}

/*************************************************************************/
void CDlgApp::TriggerISR(unsigned int DinValue)
/*************************************************************************/
/*! @brief Digital input interrupt service routine.
**************************************************************************/
{
	pDlgApp->Trigger(DinValue);
}

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