// curtain_test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "nblib.h"
#include "nblib_internal_use.h"

#include <iostream>
#include <fstream>

#define MAX_IP_ADDR_STRING 16
#define MAX_BOARD_WIDTH   1000


//curtain parsing constants
#define BITS_PER_WORD	16
#define CURTAIN_SPACING_MILS 125

struct SensorMappingInfo
{
    bool isInverted;
};

using namespace std;
bool OpenScanner();

ostream* g_stream;
HANDLE g_hEvent = INVALID_HANDLE_VALUE; // Global notification event;
BXHEADDATAINFO g_dataInfo[NB_MAXHEADS];

/*
    The following initializes a sensor system 
*/
const SensorMappingInfo g_physicalSensorMap[NB_MAXHEADS] = 
{
    { false },{ false },{ false },{ false },
    { false },{ false },{ false },{ false },
    { false },{ false },{ false },{ false },
    { false },{ false },{ false },{ false },
    { false },{ false },{ false },{ false },
    { false },{ false },{ false },{ false }
};


/****************************************************************************************/
static BOOL getfactor(SCANNERPARAMS& factors, char * ipAddr)
    {
    FILE * inp;
	int res;
	factors.lead_spots  = 5;
    factors.lead_wait   = 4;
	factors.trail_spots = 5;
    factors.trail_wait  = 4;
    factors.history		= 0;
    factors.maxwidth	= MAX_BOARD_WIDTH;
    factors.maxreverse  = 25;
    factors.mode        = 0;
    factors.pcindex_trg = 0;
    factors.escale		= 0;
    factors.pcindex_len = 1;
    factors.segments    = 0;
	factors.trail_holdscan = 0;
	factors.pcindex_stopscan = 2;
	factors.timer_freq = 0;
    factors.encoder_mode = 0;

    inp = fopen("FACTOR.DAT","rt");
    if (inp)
        {
        res = fscanf(inp,"%s\n",ipAddr);
        if(strlen(ipAddr) < 7)
        {
            // factor.dat does not contain IP address....
            factors.lead_spots = atoi(ipAddr);
            strcpy(ipAddr,"0.0.0.0");
        }
        else
		{
            res = fscanf(inp,"%d\n",&factors.lead_spots);
		}

        fscanf(inp,"%d\n",&factors.lead_wait);
        fscanf(inp,"%d\n",&factors.trail_spots);
        fscanf(inp,"%d\n",&factors.trail_wait);
        fscanf(inp,"%d\n",&factors.history);
        fscanf(inp,"%d\n",&factors.maxwidth);
        fscanf(inp,"%d\n",&factors.maxreverse);
        fscanf(inp,"%d\n",&factors.mode);
        fscanf(inp,"%d\n",&factors.pcindex_trg);
        fscanf(inp,"%d\n",&factors.escale);
        fscanf(inp,"%d\n",&factors.pcindex_len);
        fscanf(inp,"%d\n",&factors.segments);
		fscanf(inp,"%d\n",&factors.trail_holdscan);
		fscanf(inp,"%d\n",&factors.pcindex_stopscan);
		fscanf(inp,"%d\n",&factors.timer_freq);
        fscanf(inp,"%d\n",&factors.encoder_mode);    
        fclose(inp);
        }
    return TRUE;
    }
/****************************************************************************************/
void print_scans(ostream& stream, SCANNEDOBJECT2ST* scans){
	if (scans == NULL)
		return;

    stream<<"Allocated lines:" << scans->allocatedlines <<endl;
	stream<< "First line:" << scans->firstscanline <<endl;
	stream<< "Last line:" << scans->lastscanline <<endl;
	stream<< "Valid lines:" << scans->validlines <<endl << endl;
} 
bool findBoundingBits(unsigned short * curtain,int & lowBound,int & highBound,bool bInvertCurtain)
{
	/*
		lowBound and highBound are reset by the caller
		so that the boundary can be calculated over multiple scanlines
	*/
    int slotValue;
    int bufferTestIndex;
	bool boundFound = false;
	lowBound = NB_LIGHTCURTAIN_CELLS;
	highBound = 0;
	for(int i=0;i < NB_LIGHTCURTAIN_CELLS;i++)
	{
		if(!bInvertCurtain)
            bufferTestIndex = i;
        else
            bufferTestIndex = NB_LIGHTCURTAIN_CELLS - i - 1;

        slotValue = curtain[bufferTestIndex/BITS_PER_WORD] & (0x8000 >> (bufferTestIndex % BITS_PER_WORD));
        /*
            If the sensor is inverted then 'slotValue' represents ith bit from the point 3.0" to the outside of laser 7
        */

        if(slotValue != 0)
		{
			boundFound = true;
			lowBound = min(lowBound,i);
			highBound = max(highBound,i);
		}
	}
	return boundFound;
}
void printScanCurtain(ostream& stream,SCANNEDOBJECT2ST * scans)
{
	stream << "Curtain data:" << endl;
	for(int nSensor = 0;nSensor < NB_MAXHEADS;nSensor++)
	{
		int sensorLowBit = NB_LIGHTCURTAIN_CELLS;
		int sensorHighBit = 0;
		bool edgeFound = false;
		if(g_dataInfo[nSensor].isValid && g_dataInfo[nSensor].hasLightCurtainData)
		{
			for(int nScan = scans->firstscanline;nScan <= scans->lastscanline;nScan++)
			{
				unsigned short * sensorCurtain = &scans->scanline[nScan].lightcurtain[nSensor * NB_LIGHTCURTAIN_SIZE];
                if(findBoundingBits(sensorCurtain,sensorLowBit,sensorHighBit,g_physicalSensorMap[nSensor].isInverted))
					edgeFound = true;		
			}
			if(edgeFound)
			{
				stream << "Sensor " <<nSensor <<". Left edge at: " << (sensorLowBit* CURTAIN_SPACING_MILS)/1000.0 << " inches"
					<< ". Right edge at: " << ((sensorHighBit + 1)* CURTAIN_SPACING_MILS)/1000.0 << " inches" << endl;
				/*
					One is added to the right edge bit because that space is covered by the laser line.
					The invalid data begins after that bit.
				*/			
			}
			else
			{
				stream << "Sensor " <<nSensor <<". No valid data " << endl;
			
			}
		}
	}
}
/****************************************************************************************/
void displaySystemInfo(ostream& stream,BXHEADDATAINFO * info,int maxHeads)
{
    /*
        Display information about the system data structures.
    */
	for(int i=0;i < maxHeads;i++)
	{
		if(info[i].isValid)
		{
			stream << "Sensor " << i 
				<<".Lasers:" << (int)info[i].numLasers  
				<<". Curtain:" << (info[i].hasLightCurtainData ? "Yes" : "No")
				<< ". Range scan offset:"<<info[i].scannedObjRangesOffset <<
				". Curtain scan offset:" << info[i].scannedObjCurtainOffset
				<< endl; 
		}
	}
}
// event hadler thread routine
DWORD WINAPI  _notificationroutine(LPVOID data)
{
	int nBoardReadyNum = 0;
	// Allocate storage for scanned object
	SCANNEDOBJECT2ST* scans = (SCANNEDOBJECT2ST*) malloc(sizeof(SCANNEDOBJECT2ST)+sizeof(ONELINE2ST)*MAX_BOARD_WIDTH);
    bool needReconnect = false;
	while(g_hEvent!=INVALID_HANDLE_VALUE && scans != NULL)
    {
        while(needReconnect)
        {
            if(OpenScanner())
            {
                needReconnect = false;
            }        
        }

		if(WAIT_OBJECT_0 == WaitForSingleObject(g_hEvent, INFINITE)){
			ASYNCEVENTST apt;
			apt.code = 0;
			apt.data = 0;
			nbGetLastAsyncEvent(&apt);
			switch(apt.code){
				case ASYNC_EVENT_CONNECTION_LOST:
					printf("Connection lost event\n");
                    needReconnect = true;
					break;
				case ASYNC_EVENT_HEADS_CHANGED:
					printf("Heads changed event\n");
					break;
				case ASYNC_EVENT_BOARD_READY:
					printf("\nBoard ready event #%d\n", nBoardReadyNum++);
					memset(scans, 0, sizeof(SCANNEDOBJECT2ST)+sizeof(ONELINE2ST)* MAX_BOARD_WIDTH);
                    scans->allocatedlines = MAX_BOARD_WIDTH;
					if(nbGetScannedObject2(scans))
					{
							print_scans(*g_stream, scans);
							printScanCurtain(*g_stream,scans);
							(*g_stream) << endl;
					}

					break;
				case ASYNC_EVENT_LOG_PC_INFO:
					printf("pc info event\n");
					break;
				default:
					break;			
			}			
			ResetEvent(g_hEvent);
		}else{	
			break;
		}
	}
	if(scans!=NULL)
		free(scans);
	printf("Notofication thread finished: Invalid event handle\n"); 
	return 0;
};  
/****************************************************************************************/
bool OpenScanner()
{
	OPENSCANNERST openscannerst;
	SCANNERPARAMS scannerparams;
	memset(&openscannerst, 0, sizeof(OPENSCANNERST) );
	memset(&scannerparams, 0, sizeof(SCANNERPARAMS) );
	char ipAddrString[MAX_IP_ADDR_STRING] = "0.0.0.0";
	openscannerst.nicIPAddress	= NULL;					//If NULL then searches network for IP
	openscannerst.hEvent		= g_hEvent;				//event handler for asynchronous events
	int nblib_version = 0;
	char nbfw_version[MINVERSIONSZ] = {0};
	int error = 0;
	const char **errorstr = 0;

	getfactor(scannerparams,ipAddrString);							//Gets scanner parameters from FACTOR.DAT
	if(!strcmp(ipAddrString,"0.0.0.0"))
	{
		openscannerst.IPAddress		= NULL;		//If NULL then searches network for IP
	}
	else
	{
		openscannerst.IPAddress		= ipAddrString;		//If NULL then searches network for IP
	}

	if(!nbOpenScanner(&openscannerst, &scannerparams))
	{	
		error = nbGetLastError((const char **)&errorstr);
		printf("ERROR %2d, Scanner not open\n %s ", error, errorstr);
		return false;
	}
	else
		printf("Connected to IP: %s ", openscannerst.IPAddress);

	if(!nbGetFwVersion(nbfw_version))
		printf("Can't get FW version");
	else
		printf("NPH FW version %s ",nbfw_version);

	nblib_version = nbGetVersion();
	printf("NbLib Version %d\n",nblib_version);	

	nbGetSystemDataInfo(g_dataInfo,NB_MAXHEADS);
	displaySystemInfo(*g_stream,g_dataInfo,NB_MAXHEADS);

	return true;
};
/****************************************************************************************/
int main(int argc, char **argv, char **envp){
	g_stream = &cout;

	ofstream  fstr;

	if(argc>1 ){
		fstr.open((char*)argv[1]);
		g_stream= &fstr;
	}

	DWORD threadId = 0;
	HANDLE hThread = INVALID_HANDLE_VALUE;
	g_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	OpenScanner();
	hThread =  CreateThread(NULL,0,_notificationroutine,NULL,0,&threadId);
	WaitForSingleObject(hThread,INFINITE);	
	CloseHandle(hThread);
	nbCloseScanner();	
	CloseHandle(g_hEvent);
	g_hEvent = INVALID_HANDLE_VALUE;
	
	fstr.close();

	return 0;
}
