//---------------------------------------------------------------------------

#include <limits.h>
#include "camfile.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)


//---------------------------------------------------------------------------
TCAMFile::TCAMFile()
{
	RegisterFileType("bmp");
	Description = "Camera direct frame output";
	Implemented = iDIBGetFrameA + iDIBGetFrameB +
				  iGetFrameA + iGetFrameA2 + iGetFrameB + iGetFrameB2;
	Bitmap = NULL;
	FrameBuffer = NULL;
	nCamera = 0;
	VideoPos = 0;
}


//---------------------------------------------------------------------------
TCAMFile::TCAMFile(int camera)
{
	RegisterFileType("bmp");
	Description = "Camera direct frame output";
	Implemented = iDIBGetFrameA + iDIBGetFrameB +
				  iGetFrameA + iGetFrameA2 + iGetFrameB + iGetFrameB2;
	Bitmap = NULL;
	FrameBuffer = NULL;
	VideoPos = 0;
	InitCamera(camera);
}


//---------------------------------------------------------------------------
TCAMFile::~TCAMFile()
{
	Close();
}


//---------------------------------------------------------------------------
void TCAMFile::InitCamera(int camera)
{
	nCamera = (camera > 0) ? camera : 0;
}


//---------------------------------------------------------------------------
void TCAMFile::OpenA(const char *FileName, int Mode, int Type, bool ThrowExceptions)
{
	Close();

	if (!CameraDll->AddCamera(FileName, nCamera))
	{
		Close();
		return;
	}

	VideoInfo.Width = CameraDll->GetFrameWidth(nCamera);
	VideoInfo.Height = CameraDll->GetFrameHeight(nCamera);
	VideoInfo.FrameRate.Rate = 15;
	VideoInfo.FrameRate.Scale = 1;
	ScanLineWidth = VideoInfo.Width * 4;
	AlignedScanLineWidth = DIB_SCAN_LINE(ScanLineWidth);

	FrameBuffer = (BYTE*)GetMem(ScanLineWidth * VideoInfo.Height);
	ZeroMemory(FrameBuffer, ScanLineWidth * VideoInfo.Height);

	if (FrameBuffer == NULL)
	{
		if (ThrowExceptions)
		{
			Close();
			throw AVFileErrorMemory();
		}
	}
	else
	{
		FrameInBuffer = 0;
		VideoPos = 0;
		OpenMode = Mode;
		OpenType |= otVideo;
	}
//		CameraDll->StartCamera(nCamera); //!!!//

	if (ThrowExceptions)
	{
		if (Mode != OpenMode)
		{
			Close();
			throw AVFileErrorOpenInvalidMode();
		}
		if ((Type & otVideo) && !(OpenType & otVideo))
		{
			Close();
			throw AVFileErrorOpenVideo();
		}
		if ((Type & otAudio) && !(OpenType & otAudio))
		{
			Close();
			throw AVFileErrorOpenAudio();
		}
	}
}


//---------------------------------------------------------------------------
void TCAMFile::Close()
{
	OpenMode = omClosed;
	OpenType = otNone;

	if (Bitmap != NULL)
	{
		FreeMem(Bitmap);
		Bitmap = NULL;
	}

	if (FrameBuffer != NULL)
	{
		FreeMem(FrameBuffer);
		FrameBuffer = NULL;
	}
}


//---------------------------------------------------------------------------
BITMAPINFO* TCAMFile::DIBGetFrameA()
{
	CheckRead();
	CheckVideoStream();

	if (Bitmap != NULL)
	{
		if (VideoPos != FrameInBuffer)
		{
			FreeMem(Bitmap);
			Bitmap = NULL;
		}
	}

	if (Bitmap == NULL)
	{
		BITMAPINFOHEADER BitmapHeader = {0};
		BitmapHeader.biSize = sizeof(BITMAPINFOHEADER);
		BitmapHeader.biWidth = VideoInfo.Width;
		BitmapHeader.biHeight = VideoInfo.Height;
		BitmapHeader.biPlanes = 1;
		BitmapHeader.biBitCount = 32;
		BitmapHeader.biCompression = BI_RGB;
		BitmapHeader.biSizeImage = BitmapHeader.biHeight * DIB_SCAN_LINE(BitmapHeader.biWidth * 4);
		BitmapHeader.biXPelsPerMeter = 0;
		BitmapHeader.biYPelsPerMeter = 0;
		BitmapHeader.biClrUsed = 0;
		BitmapHeader.biClrImportant = 0;

		BYTE *NewBitmap = (BYTE*)GetMem(sizeof(BitmapHeader) + BitmapHeader.biSizeImage);
		if (NewBitmap == NULL)
		{
			throw AVFileErrorMemory();
		}
		memcpy(NewBitmap, &BitmapHeader, sizeof(BitmapHeader));

		// read frame from camera
		BYTE *buffer = (BYTE*)GetMem(BitmapHeader.biSizeImage);
		CameraDll->GetFrame(nCamera, VideoPos, buffer,
			(unsigned int*)&VideoInfo.Width, (unsigned int*)&VideoInfo.Height);
		if (buffer == NULL)
		{
			if (NewBitmap != NULL)
			{
				FreeMem(NewBitmap);
			}
			throw AVFileErrorGetVideo();
		}
		memcpy(NewBitmap + sizeof(BitmapHeader), buffer, BitmapHeader.biSizeImage);
		if (buffer != NULL)
		{
			FreeMem(buffer);
			buffer = NULL;
		}

		Bitmap = (BITMAPINFO*)NewBitmap;
//		FrameBuffer = (BYTE*)(Bitmap + sizeof(BitmapHeader));
		FrameInBuffer = VideoPos;
	}
	VideoPos++;
	return (BITMAPINFO*)Bitmap;
}


//---------------------------------------------------------------------------
BITMAPINFO* TCAMFile::DIBGetFrameB(unsigned int Position)
{
	CheckRead();
	CheckVideoStream();

	if (Bitmap != NULL)
	{
		if (VideoPos != FrameInBuffer)
		{
			FreeMem(Bitmap);
			Bitmap = NULL;
		}
	}

	if (Bitmap == NULL)
	{
		BITMAPINFOHEADER BitmapHeader = {0};
		BitmapHeader.biSize = sizeof(BITMAPINFOHEADER);
		BitmapHeader.biWidth = VideoInfo.Width;
		BitmapHeader.biHeight = VideoInfo.Height;
		BitmapHeader.biPlanes = 1;
		BitmapHeader.biBitCount = 32;
		BitmapHeader.biCompression = BI_RGB;
		BitmapHeader.biSizeImage = BitmapHeader.biHeight * DIB_SCAN_LINE(BitmapHeader.biWidth * 4);
		BitmapHeader.biXPelsPerMeter = 0;
		BitmapHeader.biYPelsPerMeter = 0;
		BitmapHeader.biClrUsed = 0;
		BitmapHeader.biClrImportant = 0;

		BYTE *NewBitmap = (BYTE*)GetMem(sizeof(BitmapHeader) + BitmapHeader.biSizeImage);
		if (NewBitmap == NULL)
		{
			throw AVFileErrorMemory();
		}
		memcpy(NewBitmap, &BitmapHeader, sizeof(BitmapHeader));

		// read frame from camera
		BYTE *buffer = (BYTE*)GetMem(BitmapHeader.biSizeImage);
		if (buffer == NULL)
		{
			if (NewBitmap != NULL)
			{
				FreeMem(NewBitmap);
			}
			throw AVFileErrorMemory();
		}
		memset(buffer, 0x00, BitmapHeader.biSizeImage);

		CameraDll->GetFrame(nCamera, Position, buffer,
			(unsigned int*)&VideoInfo.Width, (unsigned int*)&VideoInfo.Height);

		if (buffer == NULL)
		{
			if (NewBitmap != NULL)
			{
				FreeMem(NewBitmap);
			}
			throw AVFileErrorGetVideo();
		}
		memcpy(NewBitmap + sizeof(BitmapHeader), buffer, BitmapHeader.biSizeImage);
		if (buffer != NULL)
		{
			FreeMem(buffer);
			buffer = NULL;
		}

		Bitmap = (BITMAPINFO*)NewBitmap;
//		FrameBuffer = (BYTE*)(Bitmap + sizeof(BitmapHeader));
		FrameInBuffer = Position;
	}
	VideoPos = Position + 1;
	return (BITMAPINFO*)Bitmap;
}


//---------------------------------------------------------------------------
BYTE* TCAMFile::GetFrameA()
{
	BITMAPINFO *Bitmap = DIBGetFrameA();
	BYTE *Src = (BYTE*)Bitmap + Bitmap->bmiHeader.biSize;// + Bitmap->bmiHeader.biSizeImage;
	BYTE *Dst = FrameBuffer;
	for (int i = 0; i < VideoInfo.Height; i++)
	{
		memcpy(Dst, Src, ScanLineWidth);
		Src += AlignedScanLineWidth;
		Dst += ScanLineWidth;
	}
/*
	CameraDll->GetFrame(nCamera, VideoPos, FrameBuffer,
		(unsigned int*)&VideoInfo.Width, (unsigned int*)&VideoInfo.Height);
*/
	return FrameBuffer;
}


//---------------------------------------------------------------------------
void TCAMFile::GetFrameA2(BYTE *Buffer)
{
	BITMAPINFO *Bitmap = DIBGetFrameA();
	BYTE *Src = (BYTE*)Bitmap + Bitmap->bmiHeader.biSize;// + Bitmap->bmiHeader.biSizeImage;
	BYTE *Dst = Buffer;
	for (int i = 0; i < VideoInfo.Height; i++)
	{
		memcpy(Dst, Src, ScanLineWidth);
		Src += AlignedScanLineWidth;
		Dst += ScanLineWidth;
	}
}


//---------------------------------------------------------------------------
BYTE* TCAMFile::GetFrameB(unsigned int Position)
{
	BITMAPINFO *Bitmap = DIBGetFrameB(Position);
	BYTE *Src = (BYTE*)Bitmap + Bitmap->bmiHeader.biSize;// + Bitmap->bmiHeader.biSizeImage;
	BYTE *Dst = FrameBuffer;
	for (int i = 0; i < VideoInfo.Height; i++)
	{
		memcpy(Dst, Src, ScanLineWidth);
		Src += AlignedScanLineWidth;
		Dst += ScanLineWidth;
	}
/*
	CameraDll->GetFrame(nCamera, Position, FrameBuffer,
		(unsigned int*)&VideoInfo.Width, (unsigned int*)&VideoInfo.Height);
*/
	return FrameBuffer;
}


//---------------------------------------------------------------------------
void TCAMFile::GetFrameB2(BYTE *Buffer, unsigned int Position)
{
	BITMAPINFO *Bitmap = DIBGetFrameB(Position);
	BYTE *Src = (BYTE*)Bitmap + Bitmap->bmiHeader.biSize;// + Bitmap->bmiHeader.biSizeImage;
	BYTE *Dst = Buffer;
	for (int i = 0; i < VideoInfo.Height; i++)
	{
		memcpy(Dst, Src, ScanLineWidth);
		Src += AlignedScanLineWidth;
		Dst += ScanLineWidth;
	}
/*
	CameraDll->GetFrame(nCamera, Position, Buffer,
		(unsigned int*)&VideoInfo.Width, (unsigned int*)&VideoInfo.Height);
*/
}


//---------------------------------------------------------------------------
void TCAMFile::Seek(unsigned int Frame)
{
	CheckRead();
	CheckVideoStream();

	VideoPos = Frame;
}


//---------------------------------------------------------------------------
unsigned int TCAMFile::GetVideoLength()
{
	CheckOpened();
	CheckVideoStream();

	return CameraDll->GetLastFrameIndex(nCamera);
}

