/******************************************************************************
*                                                                             *
*      Copyright(c) 2018 ScanPrint                                            *
*                                                                             *
*      Sample code for connecting to ScanPrint SP5001                         *
*                                                                             *
*******************************************************************************/
#include "stdafx.h"
#include "CyberSC.h"
#include <iostream>
#include <map>

// Used to block the main thread until the scan has finished.
HANDLE ghScanOver = NULL;

using namespace std;

// Forward declaration of functions.
void SaveAsBitmap(_CImg* pCyberImage);
void ErrorNotify(DWORD dwError, DWORD dwType);
void ImageScanned(_CImg *IIn);

int main()
{

	// Create the synchronisation object.
	ghScanOver = CreateEvent(NULL, TRUE, FALSE, NULL);

	// Initialise the scanner and register the callback functions.
	BOOL bRet = ScInit((FARPROC)ImageScanned, (FARPROC)ErrorNotify);
	if(!bRet)
	{
		cout << "No Scanner Found." << endl;
		return -1;
	}
	cout << "Found Scanner." << endl;

	//This line sets the scanner for black and white.
	ScSCalibrate(CV_CAL_SET_RESO, EACRmFULL2);

	
	//Uncomment these lines to have the scanner scan in colour.
	//static const int RGB_COLOUR = 3 << 5;
	//ScSCalibrate(CV_CAL_SET_RESO, EACRmFULL256 | RGB_COLOUR);


	// Tell the scanner to get ready to start a scan.
	ScSCalibrate(CV_CAL_STARTSCAN, 0);

	// Uncomment this to have the scanner ignore any jams and just keep scanning.
	//ScSCalibrate(CV_CAL_TOGGLE_JAM, 0);

	// Make sure the scanner starts scanning when the paper is detected.
	ScSCalibrate(CV_CAL_FORCESCAN, 0);

	// Wait for the scan to finish or a fatal error.
	cout << "Waitng for Paper." << endl;
	WaitForSingleObject(ghScanOver, INFINITE);

	// Shutdown and pause for a second so any messages can be read.
	CloseHandle(ghScanOver);
	Sleep(1000);
    return 0;
}


// Receive a debug message or an error code.
// This is in a different thread.
void ErrorNotify(DWORD dwError, DWORD dwType)
{
	// With a debug message report the message and keep going
	if(dwError == CV_DEBUG)
	{
		cout << "Debug Msg: " << (LPCSTR)dwType << endl;
		// Tell the scanner to keep going if there's been a paper jam.
		ScSCalibrate(CV_CAL_FORCESCAN, 0);
		return;
	}
	else
	{
		// Something has gone wrong so stop 
   		SetEvent(ghScanOver);
	}
}

// Get the scan data.
// This is on a different thread.
void ImageScanned(_CImg *IIn)
{
	if(IIn)
	{
		cout << "Scaned Image." << endl; 
		SaveAsBitmap(IIn);
	}
	SetEvent(ghScanOver);
}


// Convert the _CImg into a bitmap and write it to a file.
// The pIm data member is already in the correct format, so you just need
// to create the bitmap header.
void SaveAsBitmap(_CImg* pCyberImage)
{

	// Convert from bits to the mumber of palette entries.
	static map<int, SIZE_T> bitsToColours =
	{
	{ 1 , 2 },
	{ 4, 16, },
	{ 8, 256 },
	{ 24, 0 }
	};

	BYTE bitsBerPixel = (BYTE)(pCyberImage->wNoClr * pCyberImage->bPlanes);
	SIZE_T paletteSize = bitsToColours[bitsBerPixel];

	SIZE_T headerSize = sizeof(BITMAPINFO) + sizeof(RGBQUAD) * paletteSize;
	DWORD dataSize = pCyberImage->wHei * pCyberImage->wWidBy;

	// Fill in the bitmap header structure.
	BITMAPINFO* pInfo = (PBITMAPINFO)LocalAlloc(LPTR, headerSize);
	pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	pInfo->bmiHeader.biWidth = pCyberImage->wWid;
	pInfo->bmiHeader.biHeight = pCyberImage->wHei;
	pInfo->bmiHeader.biPlanes = 1;
	pInfo->bmiHeader.biBitCount = bitsBerPixel;
	pInfo->bmiHeader.biCompression = BI_RGB;
	pInfo->bmiHeader.biSizeImage = dataSize;
	pInfo->bmiHeader.biXPelsPerMeter = 0;
	pInfo->bmiHeader.biYPelsPerMeter = 0;
	pInfo->bmiHeader.biClrUsed = paletteSize;
	pInfo->bmiHeader.biClrImportant = 0;

	// If not full RGB values add the grey shades.
	for(SIZE_T i = 0; i < paletteSize; i++)
	{
		BYTE greyShade = (BYTE)(i * (255 / (paletteSize - 1)));

		pInfo->bmiColors[i].rgbBlue = greyShade;
		pInfo->bmiColors[i].rgbGreen = greyShade;
		pInfo->bmiColors[i].rgbRed = greyShade;
		pInfo->bmiColors[i].rgbReserved = 0;
	}


	// When a bitmap is saved to a file it needs an extra header,
	BITMAPFILEHEADER   bmfHeader;
	bmfHeader.bfType = 0x4D42; //Must always be set to bitmap BM   
	bmfHeader.bfSize = headerSize + dataSize + sizeof(BITMAPFILEHEADER);
	bmfHeader.bfOffBits = headerSize + sizeof(BITMAPFILEHEADER);
	bmfHeader.bfReserved1 = 0;
	bmfHeader.bfReserved2 = 0;

	// Write to a file.
	DWORD dwBytesWritten = 0;
	HANDLE hFile = CreateFile(L"testing.bmp",
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL, NULL);
	WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
	WriteFile(hFile, (LPSTR)pInfo, headerSize, &dwBytesWritten, NULL);
	WriteFile(hFile, (LPSTR)pCyberImage->pIm, dataSize, &dwBytesWritten, NULL);

	// Tidy up all resources.
	CloseHandle(hFile);
	LocalFree((HLOCAL)pInfo);


}
