/*************************************************************************\
* "$RCSfile: CImageBrowser.cpp,v $"                                       *
***************************************************************************
* Information by CVS or VSS:                                              *
*     Last check in on: $Date: 2010-06-16 10:12:16 $                      *
*     by                $Author: schluckebier $
*     Revision        : $Revision: 1.5 $                                 *
\*************************************************************************/
/*! \file CImageBrowser.cpp
*	\brief Implementation of CImageBrowser Class
**************************************************************************/

#include <string.h>
#include "VIC_PS.h"
#include "CLog.hpp"
#include "CRegion.h"
#include "CPSApp.hpp"
#include "CImageBrowser.hpp"
#include "CDlgFileMgr.hpp"

//!@cond SHOWALL
extern "C"
{
 void VIB_SortDir(char *DirText);
 void GUI_DrawScrollMark(CIMAGE *pImage, int x, int y, int Dir, int Size, COLOR *pColor);
}
//!@endcond

/*************************************************************************/
void CImageBrowser::Run(CIMAGE *pImage, int Device, const char *pPath, FILE *fKB)
/*************************************************************************/
/*! @brief Main function.
	@param pImage Pointer to background image
	@param Device FAT device
	@param pPath Browse in directory
	@param fKB Keyboard handle
**************************************************************************/
{
	CDlgFileMgr *pDlgFileMgr = CDlgFileMgr::GetInstance();
	CPSApp *pApp = CPSApp::GetInstance();
	ENTRY *Entries;
	char *DirText;
	int i, j;
	int MaxNumber, Number, MaxThumb;
	int nKey, nDispKey;
	int Dirty;
	bool bActive = true;
	bool bIsParentDir;

	m_pImage = pImage;
	m_fKB = fKB;
	m_Device = Device;

	// *** Init ***
	if (pPath == NULL || strcmp(pPath, "..          ") == 0)
	{
		bIsParentDir = true;
	}
	else
	{
		VIB_ChDir(Device, pPath);
		bIsParentDir = false;
	}
	i = 32; // String length
	DirText = (char*)VIB_MemAlloc(MAXFILES * i);

	// *** Read directory ***
	MaxThumb = VIB_ReadDirFilterEx(Device, DirText, 0, "BMP|PNG|JPG");
	if (MaxThumb <= 0)
	{
		pApp->MessageBox("\nNo image files found!\n\n", 0, "!");
		VIB_MemFree(DirText);
		return;
	}
	VIB_SortDir(DirText);

	// *** Create empty thumbnail images ***
	Entries = (ENTRY*)VIB_MemAlloc(MaxThumb * (unsigned int)sizeof(ENTRY));
	if (Entries == NULL)
	{
		VIB_MemFree(DirText);
		return;
	}
	for (i = 0; i < MaxThumb; i++)
	{
		Entries[i].Label[0] = '\0';
		Entries[i].Image = GUI_CImage(NULL, NULL, NULL, THUMBSIZE, THUMBSIZE);
		if (Entries[i].Image.Width == 0)
		{
			MaxThumb = i;
			break;
		}
	}
	MaxNumber = MaxThumb;

	GUI_WndFrameG(pImage, "Image Browser", &rgbDkBlue, pApp->GetGradCol());

	// *** Fill structure ***
	i = Number = 0;
	while (DirText[i])
	{
		j = 0;
		while (DirText[i] != '\n')
			Entries[Number].Label[j++] = DirText[i++];
		Entries[Number].Label[j] = '\0';
		GetThumb(&Entries[Number]);
		DrawThumbs(Entries, Number, MaxNumber);
		GUI_Display(pImage, 0, 0);
		Number++;
		i++;
		nKey = getc(fKB);
		if (nKey == EOF)
		{
			_UNSET(fKB, _STATEOF);
		}
		else if (nKey == KB_ESC)
		{
			MaxNumber = Number;
			break;
		}
	}

	VIB_MemFree(DirText);

	Number = 0;

	DrawThumbs(Entries, Number, MaxNumber);
	GUI_Display(pImage, 0, 0);

	// *** Loop ***
	while (bActive)
	{
		nKey = GetKeyOrTouch(&Number, MaxNumber, &Dirty);

		switch (nKey)
		{
		case KB_ENTER:
			do
			{
				switch (Entries[Number].Type)
				{
				case ImageFormat::BMP:
					nDispKey = pDlgFileMgr->DisplayBMPFile(Entries[Number].Label);
					break;
				case ImageFormat::PNG:
					nDispKey = pDlgFileMgr->DisplayPNGFile(Entries[Number].Label);
					break;
				case ImageFormat::JPG:
					nDispKey = pDlgFileMgr->DisplayJPGFile(Entries[Number].Label);
					break;
				}

				switch (nDispKey)
				{
				case ' ':
					Number++;
					if (Number >= MaxNumber)
						Number = 0;
					break;
				case KB_BACKSP:
					Number--;
					if (Number < 0)
						Number = MaxNumber;
					break;
				}
			} while (nDispKey == ' ' || nDispKey == KB_BACKSP);

			GUI_WndFrameG(pImage, "Image Browser", &rgbDkBlue, pApp->GetGradCol());
			Dirty = 1;
			break;
		case 'b':
		case 'B':
		case KB_ESC:
			bActive = false;
			break;
		}

		if (Dirty) // Redraw
		{
			DrawThumbs(Entries, Number, MaxNumber);
			GUI_Display(pImage, 0, 0);
			Dirty = 0;
		}
	}

	// *** Clean up ***
	for (i = 0; i < MaxThumb; i++)
		GUI_FreeCImage(&Entries[i].Image);
	if (Entries)
		VIB_MemFree(Entries);
	if (bIsParentDir == false)
		VIB_ChDir(Device, "..");
}

/*************************************************************************/
void CImageBrowser::GetThumb(ENTRY *pEntry)
/*************************************************************************/
/*! @brief Creates thumbnail image.
	@param pEntry Thumbnail entry structure
**************************************************************************/
{
	const int tWidth = pEntry->Image.Width;
	const int tHeight = pEntry->Image.Height;
	int FSize;
	CIMAGE Image;

	pEntry->Width = 0;
	pEntry->Height = 0;
	pEntry->Depth = 0;

	if (strncmp(&pEntry->Label[9], "BMP", 3) == 0)
	{
		pEntry->Type = ImageFormat::BMP;
		FSize = GUI_GetBMPInfo(m_Device, pEntry->Label, &pEntry->Width, &pEntry->Height, &pEntry->Depth);
		if (FSize <= 0)
		{
			GUI_FRect(&pEntry->Image, 0, 0, tWidth, tHeight, &rgbCoolBlue);
		}
		else
		{
			Image = GUI_ReadNewCImage(m_Device, pEntry->Label);
			ScaleThumb(&Image, &pEntry->Image);
			GUI_FreeCImage(&Image);
		}
	}
	else if (strncmp(&pEntry->Label[9], "PNG", 3) == 0)
	{
		pEntry->Type = ImageFormat::PNG;
		FSize = PNG_GetInfo(m_Device, pEntry->Label, &pEntry->Width, &pEntry->Height, &pEntry->Depth);
		if (pEntry->Depth == 0)
		{
			GUI_FRect(&pEntry->Image, 0, 0, tWidth, tHeight, &rgbCoolBlue);
		}
		else
		{
			Image = PNG_ReadNewCImage(m_Device, pEntry->Label);
			ScaleThumb(&Image, &pEntry->Image);
			GUI_FreeCImage(&Image);
		}
	}
	else if (strncmp(&pEntry->Label[9], "JPG", 3) == 0)
	{
		pEntry->Type = ImageFormat::JPG;
		FSize = JPEG_GetInfo(m_Device, pEntry->Label, &pEntry->Width, &pEntry->Height, &pEntry->Depth);
		if (FSize <= 0)
		{
			GUI_FRect(&pEntry->Image, 0, 0, tWidth, tHeight, &rgbCoolBlue);
		}
		else
		{
			Image = JPEG_ReadNewCImage(m_Device, pEntry->Label);
			ScaleThumb(&Image, &pEntry->Image);
			GUI_FreeCImage(&Image);
		}
	}
	pEntry->FSize = FSize;
}

/*************************************************************************/
void CImageBrowser::ScaleThumb(CIMAGE *pIn, CIMAGE *pOut)
/*************************************************************************/
/*! @brief Scales image to thumbnail size.
	@param pIn Input image (original size)
	@param pOut Output image (scaled size)
**************************************************************************/
{
	const int iWidth = pIn->Width;
	const int iHeight = pIn->Height;
	const int oWidth = pOut->Width;
	const int oHeight = pOut->Height;
	int ScaleX, ScaleY, Scale;
	int x, y;
	int maxX, maxY;
	int iOffset, oOffset;
	unsigned char *restrict pOR = pOut->pR;
	unsigned char *restrict pOG = pOut->pG;
	unsigned char *restrict pOB = pOut->pB;
	unsigned char *restrict pIR = pIn->pR;
	unsigned char *restrict pIG = pIn->pG;
	unsigned char *restrict pIB = pIn->pB;

	// *** Calculate scale factor ***
	if (iWidth > oWidth)
		ScaleX = 256 * iWidth / oWidth;
	else
		ScaleX = 256;
	if (iHeight > oHeight)
		ScaleY = 256 * iHeight / oHeight;
	else
		ScaleY = 256;
	Scale = (ScaleX > ScaleY) ? ScaleX : ScaleY;

	maxX = 256 * iWidth / Scale;
	if (maxX > oWidth)
		maxX = oWidth;
	maxY = 256 * iHeight /Scale;
	if (maxY > oHeight)
		maxY = oHeight;

	x = (oWidth - maxX) / 2;
	if (x < 0)
		x = 0;
	y = (oHeight - maxY) / 2;
	if (y < 0)
		y = 0;

	GUI_Fill(pOut, &rgbGray50);

	// *** Scale image ***
	for (int j = 0; j < maxY; j++)
	{
		for (int i = 0; i < maxX; i++)
		{
			iOffset = (((short)j * (short)Scale) >> 8) * (short)iWidth + (((short)i * (short)Scale) >> 8);
			oOffset = (short)(j + y) * (short)oWidth + i + x;
			pOR[oOffset] = pIR[iOffset];
			pOG[oOffset] = pIG[iOffset];
			pOB[oOffset] = pIB[iOffset];
		}
	}
}

/*************************************************************************/
void CImageBrowser::DrawThumbs(ENTRY *pEntry, int n, int MaxNumber)
/*************************************************************************/
/*! @brief Draws thumbnails.
	@param pEntry List with thumbnail elements
	@param n Number of active thumbnail
	@param MaxNumber Maximum number of thumbnails
**************************************************************************/
{
	const int Width = m_pImage->Width;
	const int Height = m_pImage->Height;
	int x, y;
	int xOff, yOff, xOff2;
	int Divisor;
	int ThumbSpace;
	int Line, MaxLine, VisLines, StartLine;
	int StartT, StopT;
	int i;
	int nBarLen, nOff;
	char String[256];
	TEXTPARAMETER TP;
	CPSApp *pApp = CPSApp::GetInstance();

	// ***
	GUI_GetTxtParam(&TP);
	ThumbSpace = (12 * 8 > THUMBSIZE) ? 12*8 : THUMBSIZE;
	ThumbSpace += 32;
	Divisor = (Width - 40 - 20) / ThumbSpace;
	xOff = (Width - 20 - (short)Divisor * (short)ThumbSpace) / 2;
	xOff2 = (ThumbSpace - THUMBSIZE) / 2;
	yOff = 32;

	// *** Calculate start Thumbnail ***
	Line = n / Divisor + 1;
	VisLines = (Height - 64) / (THUMBSIZE + 28);
	if (Line>VisLines)
	{
		StartLine = Line - VisLines;
		StartT = (short)StartLine * (short) Divisor;
	}
	else
	{
		StartLine = 0;
		StartT = 0;
	}

	// *** Calculate stop Thumbnail ***
	MaxLine = MaxNumber / Divisor;
	if (MaxNumber % Divisor)
		MaxLine++;
	StopT = StartT + (short)VisLines * (short)Divisor;

	// *** Clear end line ***
	if (StopT > MaxNumber)
	{
		y = (short)(VisLines - 1) * (short)(THUMBSIZE + 28);
		for (i = MaxNumber; i < StopT; i++)
		{
			x = i % Divisor;
			GUI_FRect(m_pImage, (short)x * (short)ThumbSpace + xOff, y + yOff - 2, ThumbSpace, THUMBSIZE + 28, &rgbDkBlue);
		}
		StopT = MaxNumber;
	}

	// *** Draw Thumbnails ***
	for (i = StartT; i < StopT; i++)
	{
		x = i % Divisor;
		y = ((i - StartT) / Divisor) * (short)(THUMBSIZE + 28);

		if (pEntry[i].Label[0])
		{
			GUI_Icon(m_pImage, (short)x * (short)ThumbSpace + xOff + xOff2, y + yOff, &pEntry[i].Image, (i == n) ? 3 : 1);
			if (i != n)
			{
				GUI_SetTxtBackCol(rgbDkBlue);
				GUI_Button2(m_pImage, (short)x * (short)ThumbSpace + xOff, y + THUMBSIZE + 2 + yOff, ThumbSpace, 24, pEntry[i].Label, 0);
				GUI_SetTxtBackCol(TP.BackCol);
			}
			else
			{
				GUI_Button2(m_pImage, (short)x * (short)ThumbSpace + xOff + 2, y+THUMBSIZE + 4 + yOff, ThumbSpace - 4, 20, pEntry[i].Label, 17);
			}
		}
	}

	// *** Draw scroll bar ***
	if (MaxLine > VisLines)
	{
		GUI_FRect(m_pImage, Width - 24, 32, 16, Height - 64, &rgbCoolBlue);
		GUI_Border(m_pImage, Width - 24, yOff, 16, 16, 1);
		GUI_Border(m_pImage, Width - 24, Height - yOff - 16, 16, 16, 1);
		GUI_DrawScrollMark(m_pImage, Width - 16, yOff + 5, 0, 5, NULL);
		GUI_DrawScrollMark(m_pImage, Width - 16, Height - yOff - 5, 1, 5, NULL);

		nBarLen = (short)VisLines * (short)(Height - 64 - 32) / MaxLine;
		nOff = (short)StartLine * (short)(Height - 64 - 32) / MaxLine;
		GUI_Border(m_pImage, Width - 24, nOff + 32 + 16, 16, nBarLen, 1);
	}

	// *** Draw status line ***
	if (pEntry)
		sprintf(String, "%d/%d | %d x %d x %d | %d KB",
			n + 1, MaxNumber, pEntry[n].Width, pEntry[n].Height, pEntry[n].Depth, pEntry[n].FSize / 1024 + 1);
	else
		strcpy(String, "-/- | - x - x - | - KB");
	GUI_ButtonG(m_pImage, 4, Height - 24, Width - 8 - 200, 20, String, 1, pApp->GetGradCol());
	GUI_ButtonG(m_pImage, 8 + (Width - 8 - 200), Height - 24, 200 - 4, 20, pApp->GetStr(StringTable::BACK), 1, pApp->GetGradCol());
}

/*************************************************************************/
int CImageBrowser::GetKeyOrTouch(int *pNumber, int MaxNumber, int *pDirty)
/*************************************************************************/
/*! @brief Checks keyboard and touch or mouse.
	@param pNumber Number of active thumbnail
	@param MaxNumber Maximum number of thumbnails
	@param pDirty Redraw screen
	@return Pressed key
**************************************************************************/
{
	const int Width = m_pImage->Width;
	const int Height = m_pImage->Height;
	int xOff, yOff;
	int ThumbsPerLine;
	int ThumbSpace;
	int Line, MaxLine, VisLines, StartLine;
	int StartT, nBarLen, nOff, nThresh;
	int x, y, n;
	int key;

	ThumbSpace = (12 * 8 > THUMBSIZE) ? 12 * 8 : THUMBSIZE;
	ThumbSpace += 32;

	ThumbsPerLine = (Width - 28 - 20) / ThumbSpace;
	xOff = (Width - 20 - (short)ThumbsPerLine * (short)ThumbSpace) / 2;
	yOff = 32;

	Line = (*pNumber) / ThumbsPerLine + 1;
	VisLines = (Height - 64) / (THUMBSIZE + 28);

	if (Line>VisLines)
	{
		StartLine = Line - VisLines;
		StartT = (short)StartLine * (short)ThumbsPerLine;
	}
	else
	{
		StartLine = 0;
		StartT = 0;
	}

	MaxLine = MaxNumber / ThumbsPerLine;
	if (MaxNumber % ThumbsPerLine)
		MaxLine++;

	nBarLen = (short)VisLines * (short)(Height - 64 - 32) / MaxLine;
	nOff = (short)StartLine * (short)(Height - 64 - 32) / MaxLine;
	nThresh = nOff + 32 + 16 + nBarLen / 2;

	if (GUI_DlgGetCoord) // check GBit
	{
		int txp, typ;

		if (GUI_DlgGetCoord(&txp, &typ) > 0)
		{
			// > IDS_CANCEL - Button <
			if ((txp > (Width - 200)) && (typ >= (Height - 24)))
				return KB_ESC;

			// > Scrolbar <
			if (txp >= Width - 28)
			{
				if (typ < nThresh)
				{
					*pNumber = (*pNumber) - (ThumbsPerLine * VisLines);
					if ((*pNumber) < 0)
						*pNumber = 0;
				}
				else
				{
					*pNumber = (*pNumber) + (ThumbsPerLine * VisLines);
					if ((*pNumber) >= MaxNumber)
						*pNumber = MaxNumber - 1;
				}
				*pDirty = 1;
				return EOF;
			}

			if (txp > xOff)
				x = (txp - xOff) / ThumbSpace;
			else
				return EOF;

			if (typ > yOff)
				y = (typ - yOff) / ThumbSpace;
			else
				return EOF;

			// > Active Area <
			n = StartT + x + y * ThumbsPerLine;
			if (*pNumber == n)
			{
				return KB_ENTER;
			}
			else
			{
				*pNumber = n;
				if ((*pNumber) >= MaxNumber)
					*pNumber = MaxNumber - 1;
				*pDirty = 1;
				return EOF;
			}
		}
		else
		{
			key = getc(m_fKB);
		}
	}
	else
	{
		key = getc(m_fKB);
	}

	switch (key)
	{
	case EOF:
		_UNSET(m_fKB, _STATEOF);
		VIB_WaitBios(10);
		break;
	//=== selected ===
	case KB_LEFT:
		(*pNumber)--;
		if ((*pNumber) < 0)
			*pNumber = 0;
		*pDirty = 1;
		break;
	case KB_RIGHT:
		(*pNumber)++;
		if ((*pNumber) >= MaxNumber)
			*pNumber = MaxNumber - 1;
		*pDirty = 1;
		break;
	case KB_UP:
		if ((*pNumber) - ThumbsPerLine >= 0)
		{
			(*pNumber) -= ThumbsPerLine;
			*pDirty = 1;
		}
		break;
	case KB_DOWN:
		if (*pNumber + ThumbsPerLine < MaxNumber)
		{
			*pNumber += ThumbsPerLine;
			*pDirty = 1;
		}
		break;
	case KB_PGUP:
		*pNumber = *pNumber - (ThumbsPerLine * VisLines);
		if (*pNumber < 0)
			*pNumber = 0;
		*pDirty = 1;
		break;
	case KB_PGDOWN:
		*pNumber = *pNumber + (ThumbsPerLine * VisLines);
		if (*pNumber >= MaxNumber)
			*pNumber = MaxNumber - 1;
		*pDirty = 1;
		break;
	case KB_HOME:
		*pNumber = 0;
		*pDirty = 1;
		break;
	case KB_END:
		*pNumber = MaxNumber - 1;
		*pDirty = 1;
		break;
	}

	return key;
}

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