/* 
*	File: nikonHandler.cpp
*	Author: Eric Martin
*	
*	Copyright: 2013 MBARI 
*	
*	The nikonHandler class implements USB PTPP interactions
*	leveraging the freely available Nikon SDK and the 
*	MAID communication library. In this accessible C++ class. 
*	
*	Construction of the class attempt to address a proprietary 
*	md3 module included with the sdk. If this file in not in the
*	path of final executable, all following interactions will fail. 
*	
*	See https://sdk.nikonimaging.com/apply/ for more information
*	or a copy of the sdk. 
*	
*/

#include "nikonHandler.h"

/* 
Constructor
*/
nikonHandler::nikonHandler(void)
{
	//Pointer initializations
	pRefMod = NULL;
	pRefSrc = NULL;

	//Status Initializers
	bCameraConnected = false;
	bModuleConnected = false;

	//CONNECT TO MODULE
	if (!connectModule()|| pRefMod == NULL) {
		fprintf(stderr,"nikonHandler::nikonHandler() ERROR: Cannot connect to module.\n");
		return;
	}

}


nikonHandler::~nikonHandler(void)
{
	fprintf(stdout, "Destructing nikonHandler()...\n");
	if (!closeModule()) {
		fprintf(stderr, "nikonHandler::~nikonHandler() ERROR: Failed to close module.\n");
	}

	return;
}

bool nikonHandler::connectCamera() {
	// Test if module is loaded
	if (!bModuleConnected) {
		if( !connectModule()){
			return false;
		}
	}
	ULONG ulSrcID = 0;
	if (bCameraConnected) {
		fprintf(stderr,"nikonHandler::connectCamera(): ERROR Camera already connected.\n");
		return false;
	}

	if (!SelectSource(pRefMod,&ulSrcID)) {
		return false;
	}
	pRefSrc = GetRefChildPtr_ID( pRefMod, ulSrcID );
	if ( pRefSrc == NULL ) {
		// Create Source object and RefSrc structure.
		if ( AddChild( pRefMod, ulSrcID ) ) {
			bCameraConnected = true;
		} else {
			fprintf(stderr,"nikonHandler::connectCamera() ERROR: Source object can't be opened.\n");
			return false;
		}
		pRefSrc = GetRefChildPtr_ID( pRefMod, ulSrcID );
		//fprintf(stdout,"Camera object is opened.\n");
	}

	return true;
}

bool nikonHandler::connectModule(void)
{
	if (bModuleConnected) {
		fprintf(stderr,"nikonHandler::connectModule(): ERROR Module already loaded.\n");
		return false;
	}
#if defined( _WIN32 )
	char	ModulePath[MAX_PATH];
#elif defined(__APPLE__)
	FSRef ModuleRef;
#endif
	//LPRefObj	pRefSrc = NULL, RefItm = NULL, pRefDat = NULL;
	//char	buf[256];
	ULONG	ulModID = 0;
	//ULONG   ulSrcID = 0;
	//UWORD	wSel;
	BOOL	bRet;

	// Search for a Module-file like "Type0001.md3".
#if defined( _WIN32 )
	bRet = Search_Module( ModulePath );
#elif defined(__APPLE__)
	bRet = Search_Module( &ModuleRef );
#endif
	if ( bRet == false ) {
		fprintf(stderr,"nikonHandler::connectModule() ERROR: \"Type0001 Module\" is not found.\n" );
		return false;
	}

	// Load the Module-file.
#if defined( _WIN32 )
	bRet = Load_Module( ModulePath );
#elif defined(__APPLE__)
	bRet = Load_Module( &ModuleRef );
#endif
	if ( bRet == false ) {
		fprintf(stderr, "nikonHandler::connectModule() ERROR: Failed in loading \"Type0001 Module\".\n" );
		return false;
	}

	// Allocate memory for reference to Module object.
	pRefMod = (LPRefObj)malloc(sizeof(RefObj));
	if ( pRefMod == NULL ) {
		fprintf(stderr, "nikonHandler::connectModule() ERROR: There is not enough memory." );
		return false;
	}
	InitRefObj( pRefMod );

	// Allocate memory for Module object.
	pRefMod->pObject = (LPNkMAIDObject)malloc(sizeof(NkMAIDObject));
	if ( pRefMod->pObject == NULL ) {
		fprintf(stderr, "nikonHandler::connectModule() ERROR: There is not enough memory." );
		if ( pRefMod != NULL )	free( pRefMod );
		return false;
	}

	//	Open Module object
	pRefMod->pObject->refClient = (NKREF)pRefMod;
	bRet = Command_Open(	NULL,					// When Module_Object will be opend, "pParentObj" is "NULL".
		pRefMod->pObject,	// Pointer to Module_Object 
		ulModID );			// Module object ID set by Client
	if ( bRet == false ) {
		fprintf(stderr, "nikonHandler::connectModule() ERROR: Module object can't be opened.\n" );
		if ( pRefMod->pObject != NULL )	free( pRefMod->pObject );
		if ( pRefMod != NULL )	free( pRefMod );
		return false;
	}

	//	Enumerate Capabilities that the Module has.
	bRet = EnumCapabilities( pRefMod->pObject, &(pRefMod->ulCapCount), &(pRefMod->pCapArray), NULL, NULL );
	if ( bRet == false ) {
		fprintf(stderr, "nikonHandler::connectModule() ERROR: Failed in enumeration of capabilities." );
		if ( pRefMod->pObject != NULL )	free( pRefMod->pObject );
		if ( pRefMod != NULL )	free( pRefMod );
		return false;
	}

	//	Set the callback functions(ProgressProc, EventProc and UIRequestProc).
	bRet = SetProc( pRefMod );
	if ( bRet == false ) {
		fprintf(stderr, "nikonHandler::connectModule() ERROR: Failed in setting a call back function." );
		if ( pRefMod->pObject != NULL )	free( pRefMod->pObject );
		if ( pRefMod != NULL )	free( pRefMod );
		return false;
	}

	//	Set the kNkMAIDCapability_ModuleMode.
	if( CheckCapabilityOperation( pRefMod, kNkMAIDCapability_ModuleMode, kNkMAIDCapOperation_Set )  ){
		bRet = Command_CapSet( pRefMod->pObject, kNkMAIDCapability_ModuleMode, kNkMAIDDataType_Unsigned, 
			(NKPARAM)kNkMAIDModuleMode_Controller, NULL, NULL);
		if ( bRet == false ) {
			fprintf(stderr,"nikonHandler::connectModule() ERROR: Failed in setting kNkMAIDCapability_ModuleMode." );
			return false;
		}
	}
	//fprintf(stdout,"Module Loaded.\n");
	bModuleConnected = true;
	return true;
}

// This returns true if the exchange was sucessfull, not if there is an item available...
bool nikonHandler::itemSelect(void) {

	ulCurrentItemID = 0;

	if (SelectItem(pRefSrc, &ulCurrentItemID)) {
		if (ulCurrentItemID > 0)
			bItemSelected = true;
		else 
			bItemSelected = false;
		return true;
	}
	else {
		bItemSelected = false;
		return false;
	}
}

bool nikonHandler::itemDownloadDefault()
{
	ULONG		ulDataType	= 0;
	LPRefObj	pRefItm		= NULL;
	BOOL		bRet		= true;

	// First we see if there is an item available.
	//printf("Downloading Item\n");
	//bRet = SelectItem(pRefSrc, &ulCurrentItemID);
	//if (!bRet || ulCurrentItemID == 0)
	if (!bItemSelected) {
		fprintf(stderr,"nikonHandler::itemDownloadDefault ERROR: Function called without Item ID Collected, use itemSelect first ()\n");
		return false;
	}

	pRefItm = GetRefChildPtr_ID( pRefSrc, ulCurrentItemID );
	if ( pRefItm == NULL ) {
		// Create Item object and RefSrc structure.
		if ( AddChild( pRefSrc, ulCurrentItemID ) == TRUE ) {
			//printf("Item object is opened.\n");
		} else {
			printf("nikonHandler::itemDownloadDefault() ERROR: Item object can't be opened.\n");
			return false;
		}
		pRefItm = GetRefChildPtr_ID( pRefSrc, ulCurrentItemID );
	}
	// Select Data Object
	//printf("Selecting Data\n");

	bRet = SelectImageData( pRefItm, &ulDataType );
	if ( bRet == false )	return false;
	if ( ulDataType == kNkMAIDDataObjType_Image )
	{
		// reset file removed flag
		g_bFileRemoved = false;
		//printf("issuing acquire\n");

		bRet = imageCommandAcquire( pRefItm, ulDataType );
		// If the image data was stored in DRAM, the item has been removed after reading image.
		if ( g_bFileRemoved ) {
			RemoveChild( pRefSrc, ulCurrentItemID );
			pRefItm = NULL;
		}
	} 

	return true;
}

bool nikonHandler::imageCommandAcquire( LPRefObj pRefItm, ULONG ulDatID )
{
	LPRefObj	pRefDat = NULL;
	bool	bRet = true;

	pRefDat = GetRefChildPtr_ID( pRefItm, ulDatID );
	if ( pRefDat == NULL ) {
		// Create Image object and RefSrc structure.
		if ( AddChild( pRefItm, ulDatID ) == TRUE ) {
			printf("Image object is opened.\n");
		} else {
			printf("Image object can't be opened.\n");
			return false;
		}
		pRefDat = GetRefChildPtr_ID( pRefItm, ulDatID );
	}

	bRet = IssueAcquire( pRefDat )?true:false;
	if ( bRet == false ) {
		fprintf( stderr, "nikonHandler::imageCommandAcquire(): An Error occured.\n" );

	}

	// Close Image_Object
	bRet = RemoveChild( pRefItm, ulDatID )?true:false;

	return bRet;
}

bool nikonHandler::closeModule(void)
{

	// Close Module_Object
	if (!Close_Module( pRefMod )){
		fprintf(stderr, "nikonHandler::closeModule() ERROR: Module object can not be closed.\n" );
		return false;
	}
	// Unload Module
#if defined( _WIN32 )
	FreeLibrary( g_hInstModule );
	g_hInstModule = NULL;
#elif defined(__APPLE__)
	CloseConnection( &g_ConnID );
	g_ConnID = 0;
	g_nModRefNum = -1;
#endif

	// Free memory blocks allocated in this function.
	if ( pRefMod->pObject != NULL )	free( pRefMod->pObject );
	if ( pRefMod != NULL )	free( pRefMod );

	bCameraConnected = false;
	bModuleConnected = false;
	return true;
}

bool nikonHandler::isConnected(void) {

	return (bCameraConnected && bModuleConnected);
}

bool nikonHandler::itemSelected(void) {

	return (bItemSelected);
}

bool nikonHandler::setImageName(char * instr, int len) {
	strncpy_s(sNextFileName,instr,len);

	return true;

}

