/*
	main.cpp - This file is a test of the CSBIGCam and CSBIGImage classes.

	1. This software (c)2004 Santa Barbara Instrument Group.
	2. This free software is provided as an example of how 
	    to communicate with SBIG cameras.  It is provided AS-IS
	    without any guarantees by SBIG of suitability for a 
	    particular purpose and without any guarantee to be 
	    bug-free.  If you use it you agree to these terms and
	    agree to do so at your own risk.
 3.  Any distribution of this source code to include these terms.

	Revision History
	Date	 - Modification
	
  2012/11/23 - Updated version of the testmain application tested on Debian 6.0.6 (JS)
               This application grabs number of requested images and stores them
               in FITS format. User has to install the cfitsio library. This
               application also uses a new SBIG's universal driver/library which
               utilizes a new libusb ver. 1.0 library. So, user has to install
               it too. On Debian & Ubuntu distros use the following command:
               sudo apt-get install libusb-1.0-0-dev

  2004/04/26 - Initial release - Matt Longmire (SBIG)

*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <iostream>
#include <string>
#include <libusb.h>

#include "lpardrv.h"
#include "csbigcam.h"
#include "csbigimg.h"

using namespace std;

#define LINE_LEN 	80

PAR_ERROR stfTest();
PAR_ERROR usbQueryTest();
PAR_ERROR cfwTest();
PAR_ERROR cfwInit (unsigned short cfwModel, CFWResults* pRes);
PAR_ERROR cfwGoto (unsigned short cfwModel, int cfwPosition, CFWResults* pRes);
void 			cfwShowResults(CFWResults* pRes);
PAR_ERROR grabImageTest(int argc, char *argv[]);
PAR_ERROR stiFpsTest(int argc, char *argv[]);

int main(int argc, char *argv[])
{
	//stfTest();
	//usbQueryTest();
  cfwTest();
  //grabImageTest(argc, argv);
  //stiFpsTest(argc, argv);
}
//==============================================================
// TODO GRAB IMAGE TEST
//==============================================================
PAR_ERROR grabImageTest(int argc, char *argv[])
{
	PAR_ERROR 			err = CE_NO_ERROR;
	string					filePath, filePathName;
	bool						bFitsType;
	int							numOfImages;
	bool						bLightFrame;
	double					expTime;
	int							rm;
	SBIG_FILE_ERROR	ferr;

  if (argc != 7)
	{
		cout << "Application startup error: Bad number of input parameters. " << endl;
		cout << "Format : ./testapp filePath               fileType imgCount imgType   expTime readoutMode" << endl;
		cout << "Example: ./testapp /observations/img/raw/  FITS     100       LF        0.001       1x1"     << endl;
		cout << "Example: ./testapp /observations/img/raw/  SBIG      5        DF        15.5        2x2"     << endl;
		return CE_BAD_PARAMETER;
	}

	filePath = argv[1];
	int len  = filePath.length();
	if (filePath[len-1] != '/')
	{
		filePath.append("/");
	}

	//bFileType
	bFitsType = false;
	if (strcmp(argv[2], "FITS") == 0)
	{
		bFitsType = true;
	}

	numOfImages = atoi(argv[3]);

	//imgType
	bLightFrame = false; //suppose DF
	if (strcmp(argv[4], "LF") == 0)
	{
		bLightFrame = true;
	}

	expTime = atof(argv[5]);

	//readout mode
	rm = 0; //suppose 1x1
	if (strcmp(argv[6], "2x2") == 0)
	{
		rm = 1;
	}
	else if (strcmp(argv[6], "3x3") == 0)
	{
		rm = 2;
	}

	cout << "Input parameters      : "
	     << "filePath = " 	    << filePath
	     << ", bFitsType = "    << bFitsType
	     << ", numOfImages = " 	<< numOfImages
	     << ", bLightFrame = "  << bLightFrame
	     << ", exposure time = "<< expTime
	     << ", readout mode = " << rm
	     << endl;

	// update filepath:
	if (bLightFrame)
	{
        filePath += "LF_";
	}
	else
	{
        filePath += "DF_";
	}

	// create SBIG Img object
	CSBIGImg* pImg = new CSBIGImg;

	// create SBIG camera object
	CSBIGCam* pCam = new CSBIGCam(DEV_USB1);

	if ((err = pCam->GetError()) != CE_NO_ERROR)
	{
		cout << "CSBIGCam error        : " << pCam->GetErrorString(err) << endl;
		return err;
	}

	// establish link to the camera
	if ((err = pCam->EstablishLink()) != CE_NO_ERROR)
	{
		cout << "Establish link error  : " << pCam->GetErrorString(err) << endl;
		return err;
	}
	cout << "Link established to   : " << pCam->GetCameraTypeString() << endl;

	// set camera params
	pCam->SetActiveCCD(CCD_IMAGING);
	pCam->SetExposureTime(expTime);
	pCam->SetReadoutMode(rm);
	pCam->SetABGState(ABG_LOW7);

	// get full frame
	int left   = 0;
	int	top    = 0;
	int	width  = 100;
	int	height = 100;
	pCam->GetFullFrame(width, height);
	pCam->SetSubFrame(left, top, width, height);
	pImg->AllocateImageBuffer(height, width);

  // take series of images
	for (int i = 1; i <= numOfImages; i++)
	{
		do
		{
			if (bLightFrame)
			{

				#ifndef CHECK_STI_FRAME_RATES
				cout << "Taking light frame no.: " << i << endl;
				#endif

				if ((err = pCam->GrabImage(pImg, SBDF_LIGHT_ONLY)) != CE_NO_ERROR)
				{
					cout << "CSBIGCam error        : " << pCam->GetErrorString(err) << endl;
					break;
				}
			}
			else
			{

				#ifndef CHECK_STI_FRAME_RATES
				cout << "Taking dark  frame no.: " << i << endl;
				#endif

				if ((err = pCam->GrabImage(pImg, SBDF_DARK_ONLY)) != CE_NO_ERROR)
				{
					cout << "CSBIGCam error        : " << pCam->GetErrorString(err) << endl;
					break;
				}
			}

			char             timeBuf[128];
			struct  tm*      pTm;

			struct  timeval  tv;
			struct  timezone tz;
			gettimeofday(&tv, &tz);
			pTm = localtime(&(tv.tv_sec));
	        sprintf(timeBuf, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld",
	                pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday,
	                pTm->tm_hour,        pTm->tm_min,     pTm->tm_sec, (tv.tv_usec/1000));

	        // create filePathName:
	        filePathName = filePath;
	        filePathName += timeBuf;

	        if (bFitsType)
	        {
	        	filePathName += ".fits";
				if ((ferr = pImg->SaveImage(filePathName.c_str(), SBIF_FITS)) != SBFE_NO_ERROR)
				{
					cout << "SBIF_FITS format save error: " << ferr << endl;
					break;
				}
	        }
	        else
	        {
	        	filePath += ".sbig";
	        	if ((ferr = pImg->SaveImage(filePathName.c_str(), SBIF_COMPRESSED)) != SBFE_NO_ERROR)
				{
					cout << "SBIF_COMPRESSED format save error: " << ferr << endl;
					break;
				}
	        }

			#ifndef CHECK_STI_FRAME_RATES
			cout << "File saved as         : " << filePathName << endl;
			#endif

		}
		while(0);

		if (err != CE_NO_ERROR)
		{
			break;
		}

	} // for

	// close sbig device
	if ((err = pCam->CloseDevice()) != CE_NO_ERROR)
	{
		cout << "CSBIGCam error: " << pCam->GetErrorString(err) << endl;
	}

	// close sbig driver
	if((err = pCam->CloseDriver()) != CE_NO_ERROR)
	{
		cout << "CSBIGCam error: " << pCam->GetErrorString(err) << endl;
	}

	// delete objects
	if(pImg)
	{
		delete pImg;
	}

	if(pCam)
	{
		delete pCam;
	}

	cout << "The End..." << endl;
	return (err);
}
//==============================================================
// TODO STI FPS TEST
//==============================================================
PAR_ERROR stiFpsTest(int argc, char *argv[])
{
	PAR_ERROR 			err = CE_NO_ERROR;
	string					filePath, filePathName;
	bool						bFitsType;
	int							numOfImages;
	bool						bLightFrame;
	double					expTime;
	int							rm;
	SBIG_FILE_ERROR	ferr;
	double 					fps;
  struct timeval 	begin, end;

  if (argc != 7)
	{
		cout << "Application startup error: Bad number of input parameters. " << endl;
		cout << "Format : ./testapp filePath               fileType imgCount imgType   expTime readoutMode" << endl;
		cout << "Example: ./testapp /observations/img/raw/  FITS     100       LF        0.001       1x1"     << endl;
		cout << "Example: ./testapp /observations/img/raw/  SBIG      5        DF        15.5        2x2"     << endl;
		return CE_BAD_PARAMETER;
	}
	
	filePath = argv[1];
	int len  = filePath.length();
	if (filePath[len-1] != '/')
	{
		filePath.append("/");
	}
	
	//bFileType
	bFitsType = false;
	if (strcmp(argv[2], "FITS") == 0)
	{
		bFitsType = true;
	}

	numOfImages = atoi(argv[3]);
	
	//imgType
	bLightFrame = false; //suppose DF
	if (strcmp(argv[4], "LF") == 0)
	{
		bLightFrame = true;
	}
	
	expTime = atof(argv[5]);
		
	//readout mode
	rm = 0; //suppose 1x1
	if (strcmp(argv[6], "2x2") == 0)
	{
		rm = 1;
	}
	else if (strcmp(argv[6], "3x3") == 0)
	{
		rm = 2;
	}
	
	cout << "Input parameters      : "
	     << "filePath = " 	    << filePath
	     << ", bFitsType = "    << bFitsType
	     << ", numOfImages = " 	<< numOfImages
	     << ", bLightFrame = "  << bLightFrame
	     << ", exposure time = "<< expTime
	     << ", readout mode = " << rm
	     << endl;
	
	// update filepath:
	if (bLightFrame)
	{
        filePath += "LF_";
	}
	else
	{
        filePath += "DF_";
	}

	// create SBIG Img object
	CSBIGImg* pImg = new CSBIGImg;

	// create SBIG camera object
	CSBIGCam* pCam = new CSBIGCam(DEV_USB1);

	if ((err = pCam->GetError()) != CE_NO_ERROR)
	{
		cout << "CSBIGCam error        : " << pCam->GetErrorString(err) << endl;
		return err;
	}

	// establish link to the camera
	if ((err = pCam->EstablishLink()) != CE_NO_ERROR)
	{	
		cout << "Establish link error  : " << pCam->GetErrorString(err) << endl;
		return err;
	}						
	cout << "Link established to   : " << pCam->GetCameraTypeString() << endl;
	
	// set camera params
	pCam->SetActiveCCD(CCD_IMAGING);
	pCam->SetExposureTime(expTime);
	pCam->SetReadoutMode(rm);
	pCam->SetABGState(ABG_LOW7);

	// get full frame
	int left   = 0;
	int	top    = 0;
	int	width  = 100;
	int	height = 100;
	pCam->GetFullFrame(width, height);
	pCam->SetSubFrame(left, top, width, height);
	pImg->AllocateImageBuffer(height, width);

  gettimeofday(&begin, NULL);

  // take series of images
	for (int i = 1; i <= numOfImages; i++)
	{			
		do
		{ 
			if (bLightFrame)
			{
				if ((err = pCam->GrabImage(pImg, SBDF_LIGHT_ONLY)) != CE_NO_ERROR)
				{
					cout << "CSBIGCam error        : " << pCam->GetErrorString(err) << endl;
					break;
				}
			}
			else
			{
				if ((err = pCam->GrabImage(pImg, SBDF_DARK_ONLY)) != CE_NO_ERROR)
				{
					cout << "CSBIGCam error        : " << pCam->GetErrorString(err) << endl;
					break;
				}
			}
		
			char             timeBuf[128];
			struct  tm*      pTm;

			struct  timeval  tv;
			struct  timezone tz;
			gettimeofday(&tv, &tz);
			pTm = localtime(&(tv.tv_sec));
	        sprintf(timeBuf, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld",
	                pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday,
	                pTm->tm_hour,        pTm->tm_min,     pTm->tm_sec, (tv.tv_usec/1000));

	        // create filePathName:
	        filePathName = filePath;
	        filePathName += timeBuf;

	        if (bFitsType)
	        {
	        	filePathName += ".fits";
				if ((ferr = pImg->SaveImage(filePathName.c_str(), SBIF_FITS)) != SBFE_NO_ERROR)
				{
					cout << "SBIF_FITS format save error: " << ferr << endl;
					break;
				}
	        }
	        else
	        {
	        	filePath += ".sbig";
	        	if ((ferr = pImg->SaveImage(filePathName.c_str(), SBIF_COMPRESSED)) != SBFE_NO_ERROR)
				{
					cout << "SBIF_COMPRESSED format save error: " << ferr << endl;
					break;
				}
	        }
		}
		while(0);
		
		if (err != CE_NO_ERROR)
		{
			break;
		}
		
	} // for
	
	gettimeofday(&end, NULL);

	// close sbig device
	if ((err = pCam->CloseDevice()) != CE_NO_ERROR)
	{
		cout << "CSBIGCam error: " << pCam->GetErrorString(err) << endl;
	}
		
	// close sbig driver	
	if((err = pCam->CloseDriver()) != CE_NO_ERROR)
	{
		cout << "CSBIGCam error: " << pCam->GetErrorString(err) << endl;
	}

	// delete objects
	if(pImg)
	{
		delete pImg;
	}

	if(pCam)
	{
		delete pCam;
	}
		
  double timeDiff = (((end.tv_sec  - begin.tv_sec) * 1000000) +
  		                (end.tv_usec - begin.tv_usec) + 500) / 1000000;

  fps = 1.0 / (timeDiff / numOfImages);

  cout << "-----------------------------------------------"      << endl;
  cout << "Application name                   : testmain utilizing sbigudrv C library" << endl;
  cout << "ST-i frame rates measurement       : NO ERROR"        << endl;
  cout << "Average of                         : " << numOfImages << " FITS frames saved to disk." << endl;
  cout << "Exposure time                      : " << expTime     << endl;
  switch (rm)
  {
     case RM_1X1:
       	 	cout << "Readout mode                       : 1x1"              << endl;
          break;
     case RM_2X2:
    	 		cout << "Readout mode                       : 2x2"              << endl;
          break;
     default:
    	 		cout << "Readout mode                       : bad parameter"    << endl;
          break;
  }

  std::_Ios_Fmtflags originalFlags = cout.flags();
  int  originalPrecision = cout.precision(1);
  cout.setf(ios::fixed,ios::floatfield);

  cout << "ST-i frame rates                   : " << fps << " [fps]" << endl;

  cout.flags(originalFlags);
  cout.precision(originalPrecision);

	cout << "The End..." << endl;
	return (err);
}
//==============================================================
// TODO CFW TEST
//==============================================================
PAR_ERROR cfwTest()
{
	PAR_ERROR	 err;
	CFWResults cfwr;

	// open driver
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL);
	fprintf(stderr, "----------------------------------------------\n");
	fprintf(stderr, "CC_OPEN_DRIVER err: %d\n", err);
	if (err != CE_NO_ERROR)
	{
			return err;
	}

	// open device
	OpenDeviceParams odp;
	odp.deviceType = DEV_USB1;

	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_OPEN_DEVICE, &odp, NULL);
	fprintf(stderr, "----------------------------------------------\n");
	fprintf(stderr, "CC_OPEN_DEVICE err: %d\n", err);
	if (err != CE_NO_ERROR)
	{
			return err;
	}

	// establish link
	EstablishLinkParams  elp;
	EstablishLinkResults elr;
	elp.sbigUseOnly = 0;

	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_ESTABLISH_LINK, &elp, &elr);
	if (err != CE_NO_ERROR)
	{
			fprintf(stderr, "----------------------------------------------\n");
			fprintf(stderr, "CC_ESTABLISH_LINK err: %d\n", err);
			return err;
	}
	fprintf(stderr, "----------------------------------------------\n");
	fprintf(stderr, "CC_ESTABLISH_LINK err: %d, cameraType: %d\n", err, elr.cameraType);

	unsigned long  position;
	unsigned short cfwModel;

	switch (elr.cameraType)
	{
			case 	STL_CAMERA:
						cfwModel = CFWSEL_CFWL;
						break;

			case	STF_CAMERA:
						cfwModel = CFWSEL_FW5_8300;
						break;

			default:
						cfwModel = CFWSEL_AUTO;
						break;
	}

	do
	{
		// cfwInit
		err = cfwInit(cfwModel, &cfwr);
		if (err != CE_NO_ERROR)
		{
				break;
		}
		fprintf(stderr, "----------------------------------------------\n");
		fprintf(stderr, "cfwInit err: %d\n", err);

		cfwModel = cfwr.cfwModel;

		// cfwGoto
		position = 3;
		err = cfwGoto(cfwModel, position, &cfwr);
		if (err != CE_NO_ERROR)
		{
				break;
		}
		fprintf(stderr, "----------------------------------------------\n");
		fprintf(stderr, "cfwGoto requested position: %ld, err: %d\n", position, err);
		cfwShowResults(&cfwr);

		// cfwGoto
		position = 1;
		err = cfwGoto(cfwModel, position, &cfwr);
		if (err != CE_NO_ERROR)
		{
				break;
		}
		fprintf(stderr, "----------------------------------------------\n");
		fprintf(stderr, "cfwGoto requested position: %ld, err: %d\n", position, err);
		cfwShowResults(&cfwr);

		// cfwGoto
		position = 4;
		err = cfwGoto(cfwModel, position, &cfwr);
		if (err != CE_NO_ERROR)
		{
				break;
		}
		fprintf(stderr, "----------------------------------------------\n");
		fprintf(stderr, "cfwGoto requested position: %ld, err: %d\n", position, err);
		cfwShowResults(&cfwr);
	}
	while (0);

	// close device
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CLOSE_DEVICE, NULL, NULL);
	fprintf(stderr, "----------------------------------------------\n");
	fprintf(stderr, "CC_CLOSE_DEVICE err: %d\n", err);

	// close driver
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CLOSE_DRIVER, NULL, NULL);
	fprintf(stderr, "----------------------------------------------\n");
	fprintf(stderr, "CC_CLOSE_DRIVER err: %d\n", err);

	fprintf(stderr, "----------------------------------------------\n");
	fprintf(stderr, "The End...\n");
	return err;
}
//==============================================================
PAR_ERROR cfwInit(unsigned short cfwModel, CFWResults* pRes)
{
	PAR_ERROR	err;
	CFWParams cfwp;

	cfwp.cfwModel   = cfwModel;
	cfwp.cfwCommand = CFWC_INIT;
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CFW, &cfwp, pRes);

	if (err != CE_NO_ERROR)
	{
			return err;
	}

	do
	{
			cfwp.cfwCommand = CFWC_QUERY;
			err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CFW, &cfwp, pRes);

			if (err != CE_NO_ERROR)
			{
					continue;
			}

			if (pRes->cfwStatus != CFWS_IDLE)
			{
	  			sleep(1);
			}
	}
	while (pRes->cfwStatus != CFWS_IDLE);

	return err;
}
//==============================================================
PAR_ERROR cfwGoto(unsigned short cfwModel, int cfwPosition, CFWResults* pRes)
{
	PAR_ERROR err;
	CFWParams cfwp;

	cfwp.cfwModel = cfwModel;

	do
	{
			cfwp.cfwCommand = CFWC_QUERY;
			err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CFW, &cfwp, pRes);

			if (err != CE_NO_ERROR)
			{
					continue;
			}

			if (pRes->cfwStatus != CFWS_IDLE)
			{
	  			sleep(1);
			}
	}
	while (pRes->cfwStatus != CFWS_IDLE);

	cfwp.cfwCommand = CFWC_GOTO;
	cfwp.cfwParam1  = cfwPosition;
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CFW, &cfwp, pRes);
	if (err != CE_NO_ERROR)
	{
			return err;
	}

	do
	{
			cfwp.cfwCommand = CFWC_QUERY;
			err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CFW, &cfwp, pRes);

			if (err != CE_NO_ERROR)
			{
					continue;
			}

			if (pRes->cfwStatus != CFWS_IDLE)
			{
	  			sleep(1);
			}
	}
	while (pRes->cfwStatus != CFWS_IDLE);

	return err;
}
//==============================================================
void cfwShowResults(CFWResults* pRes)
{
	fprintf(stderr, "CFWResults->cfwModel      : %d\n", pRes->cfwModel);
	fprintf(stderr, "CFWResults->cfwPosition   : %d\n", pRes->cfwPosition);
	fprintf(stderr, "CFWResults->cfwStatus     : %d\n", pRes->cfwStatus);
	fprintf(stderr, "CFWResults->cfwError      : %d\n", pRes->cfwError);
}
//==============================================================
// TODO USB QUERY TEST
//==============================================================
PAR_ERROR usbQueryTest()
{
	PAR_ERROR				err;
	QueryUSBResults res;
	QUERY_USB_INFO	usbInfo;

	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL);
	printf ("CC_OPEN_DRIVER status: %d\n", err);
	if (err != CE_NO_ERROR)
	{
		return err;
	}

	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_QUERY_USB, NULL, &res);
	if (err == CE_NO_ERROR)
	{
		printf ("camerasFound: %d\n\n", res.camerasFound);

		for (int i = 0; i < 4; i++)
		{
			usbInfo = res.usbInfo[i];
			if (usbInfo.cameraFound)
			{
				printf ("\n");
				printf ("QUERY_USB_INFO no: %d\n", i);
				printf ("cameraFound      : %d\n", usbInfo.cameraFound);
				printf ("cameraType       : %d\n", usbInfo.cameraType);
				printf ("name             : %s\n", usbInfo.name);
				printf ("S/N              : %s\n", usbInfo.serialNumber);
			}
		}
	}

	printf ("The End...\n");
	return err;
}
//==============================================================
// TODO STF TEST
//==============================================================
PAR_ERROR stfTest()
{
	PAR_ERROR err;

	// open driver
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL);
	printf ("CC_OPEN_DRIVER    err: %d\n", err);
	if (err != CE_NO_ERROR)
	{
		return err;
	}

	// open device
	OpenDeviceParams odp;
	odp.deviceType = DEV_USB1;

	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_OPEN_DEVICE, &odp, NULL);
	printf ("CC_OPEN_DEVICE    err: %d\n", err);
	if (err != CE_NO_ERROR)
	{
		return err;
	}

	// establish link
	EstablishLinkParams  elp;
	EstablishLinkResults elr;
	elp.sbigUseOnly = 0;

	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_ESTABLISH_LINK, &elp, &elr);
	if (err != CE_NO_ERROR)
	{
		printf ("CC_ESTABLISH_LINK err: %d\n", err);
		return err;
	}
	printf ("CC_ESTABLISH_LINK err: %d, cameraType: %d\n", err, elr.cameraType);

	do
	{
	}
	while (0);

	// close device
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CLOSE_DEVICE, NULL, NULL);
	printf ("CC_CLOSE_DEVICE   err: %d\n", err);

	// close driver
	err = (PAR_ERROR)SBIGUnivDrvCommand(CC_CLOSE_DRIVER, NULL, NULL);
	printf ("CC_CLOSE_DRIVER   err: %d\n", err);

	printf ("The End...\n");
	return err;
}
//==============================================================
//==============================================================
//==============================================================
//==============================================================

