/********************************************************************
**                Image Component Library (ICL)                    **
**                                                                 **
** Copyright (C) 2006-2010 CITEC, University of Bielefeld          **
**                         Neuroinformatics Group                  **
** Website: www.iclcv.org and                                      **
**          http://opensource.cit-ec.de/projects/icl               **
**                                                                 **
** File   : ICLIO/src/SonyFwGrabber.cpp                            **
** Module : ICLIO                                                  **
** Authors: Christof Elbrechter, Felix Reinhard                    **
**                                                                 **
**                                                                 **
** Commercial License                                              **
** ICL can be used commercially, please refer to our website       **
** www.iclcv.org for more details.                                 **
**                                                                 **
** GNU General Public License Usage                                **
** Alternatively, this file may be used under the terms of the     **
** GNU General Public License version 3.0 as published by the      **
** Free Software Foundation and appearing in the file LICENSE.GPL  **
** included in the packaging of this file.  Please review the      **
** following information to ensure the GNU General Public License  **
** version 3.0 requirements will be met:                           **
** http://www.gnu.org/copyleft/gpl.html.                           **
**                                                                 **
** The development of this software was supported by the           **
** Excellence Cluster EXC 277 Cognitive Interaction Technology.    **
** The Excellence Cluster EXC 277 is a grant of the Deutsche       **
** Forschungsgemeinschaft (DFG) in the context of the German       **
** Excellence Initiative.                                          **
**                                                                 **
*********************************************************************/

#ifdef WIN32
#ifdef WITH_SONYIIDC

#include <ICLIO/SonyFwGrabber.h>

namespace icl {

	SonyFwGrabber::SonyFwGrabber(void) {
      m_pppImgBuffer = 0;
      m_lNumCameras = 0;
		// init();
	}

	SonyFwGrabber::~SonyFwGrabber() {
		//---- Destroy all camera handle ----
		for (int i=0; i<m_lNumCameras; i++) {
			iidc_idle(m_hCamera[i]);
			iidc_releasebuffer(m_hCamera[i]);
		}
		iidc_uninit();

      //---- Release Grabbing Buffers ----
      if (m_pppImgBuffer) {
         for (int i=0; i < m_lNumCameras; i++) {
            delete [] m_pppImgBuffer[i][0];
            delete [] m_pppImgBuffer[i];
         }
         delete [] m_pppImgBuffer;
      }
	}

	bool SonyFwGrabber::init() {
		char* cameraID[10];

		IIDC_FORMATINFO *formatinfo;
		IIDC_FRAMERATEINFO *rateinfo;
		IIDC_FEATUREINFO *featureInfo;
		IIDC_FEATURE feature;

		int nRetCode = 0;
		int i,j;
		long lRet, lFormatCnt;
		long **ppFormatIndexList;
		bool bRet;

		//---- Establish camera connection ----
		bRet = iidc_init();

		if (!bRet)
		{
			cout << "FATAL error during init!" << endl;
			return false;
		}
		else
		{
			//---- Check for number of available cameras ----
			m_lNumCameras = iidc_getcameracount();
			cout << "Number of cameras found: " << m_lNumCameras << endl;
			ppFormatIndexList = new long*[m_lNumCameras];
			formatinfo = new IIDC_FORMATINFO[m_lNumCameras];
			rateinfo = new IIDC_FRAMERATEINFO[m_lNumCameras];
			featureInfo = new IIDC_FEATUREINFO[m_lNumCameras];

			//---- Setup ----
			for (i=0;i<m_lNumCameras;i++)
			{
				//---- Read/ build unique camera id ----
				cameraID[i] = new char[100];
				GetCamAllString(i, cameraID[i]);
				//cout << "Unique ID for camera " << i << ": " << cameraID[i] << endl;

				//---- Open camera ----
				m_hCamera[i] = iidc_open(i);
				if (!m_hCamera[i] )
					cout << "Get camera handle of camera " << i << " FAILED!" << endl;

				//---- How many formats are supported ----
				lFormatCnt = iidc_getformatcount( m_hCamera[i] );
				cout << "Number of supported formats for camera " << i << ": " << lFormatCnt << endl;

				//---- Read the supported formats from camera ----
				ppFormatIndexList[i] = new long[lFormatCnt];
				lRet = iidc_getformatidlist( m_hCamera[i], ppFormatIndexList[i], sizeof(long)*lFormatCnt );

				if (!lRet)
				{
					printf("Cannot read the supported formats from camera\n");
					exit(1);
				}

				for(int k=0;k<lFormatCnt;k++)
					lRet = iidc_getformatinfo(m_hCamera[i],
					&formatinfo[i],
					sizeof(IIDC_FORMATINFO),
					ppFormatIndexList[i][k]);

				//---- SET FRAMERATE (has to be set non-zero, and before iidc_setformat is called!) ----
				//iidc_setframerate(m_hCamera[i], 15.0);
				iidc_setframerate(m_hCamera[i], 30.0);
				//iidc_setframerate(m_hCamera[i], 60.0);

				//---- Set image format ----
				bRet = iidc_setformat(m_hCamera[i], ppFormatIndexList[i][0]);
				
				long lCurFormat = iidc_currentformat(m_hCamera[i]);
				cout << "color coding: " << GET_COLORCODING(lCurFormat);
				//cout << iidc_setformat(m_hCamera[i], IIDCID_COLORCODING_RGB8) << endl;

				switch(GET_COLORCODING(lCurFormat)) {
					case IIDCID_COLORCODING_MONO8:
						cout << "  MONO8" << endl;
						break;
					case IIDCID_COLORCODING_YUV411:
						cout << "  YUV411"<< endl;
						break;
					case IIDCID_COLORCODING_YUV422:
						cout << "  YUV422"<< endl;
						break;
					case IIDCID_COLORCODING_YUV444:
						cout << "  YUV444"<< endl;
						break;
					case IIDCID_COLORCODING_RGB8:
						cout << "  RGB8"<< endl;
						break;
					case IIDCID_COLORCODING_MONO16:
						cout << "  MONO16"<< endl;
						break;
					case IIDCID_COLORCODING_RGB16:
						cout << "  RGB8"<< endl;
						break;
					case IIDCID_COLORCODING_RAW8:
						cout << "  RAW8"<< endl;
						break;
					case IIDCID_COLORCODING_RAW16:
						cout << "  RAW16"<< endl;
						break;
				}

				if (!bRet)
				{
					cout << "FATAL error - could not set FORMAT" << endl;
					return false;
				}

				//---- Get image width/ height  ----
				cout << "Image width : " << formatinfo[i].width << endl;
				cout << "Image height: " << formatinfo[i].height << endl;

				m_iWidth = (int) formatinfo[i].width;
				m_iHeight = (int) formatinfo[i].height;

				//---- Get/ Set framerate info ----
				lRet = iidc_getframerateinfo(m_hCamera[i],
					&rateinfo[i],
					sizeof(IIDC_FRAMERATEINFO),
					ppFormatIndexList[i][0] );

				//---- Get/ Set feature info ----
				lRet = iidc_currentfeatureinfo(m_hCamera[i], &featureInfo[i], sizeof(IIDC_FEATUREINFO) );
			}

			m_pppImgBuffer = (BYTE***) new BYTE**[m_lNumCameras];

			for (i=0;i<m_lNumCameras;i++)
			{
				//if (iidc_getstatus(m_hCamera[i]) == IIDC_STATUS_UNSTABLE)
				//	cout << "status unstable, call preparecapture" << endl;
				//---- Prepare capture (IIDC_STATUS_UNSTABLE) ----
				bRet = iidc_preparecapture( m_hCamera[i]);

				if( bRet == FALSE ) 
				{
					cout << "FATAL error - Prepare capture failed" << endl;
					cout << "error code: ";
					printf("%x\r\n", iidc_getlasterror(iidc_handletoindex(m_hCamera[i])));
					Sleep(5000);
					exit(1);
				}

				//---- Attach image buffer (IIDC_STATUS_STABLE) ----
				long lBufSz;
				lBufSz = iidc_currentdataframebytes( m_hCamera[i] );

				m_pppImgBuffer[i] = new BYTE*[1];
				m_pppImgBuffer[i][0] = new BYTE[lBufSz];
				lRet = iidc_attachbuffer( m_hCamera[i], (void**)m_pppImgBuffer[i], sizeof(LPBYTE) );

				//---- Start sequential grab ----
				bRet = iidc_capture( m_hCamera[i], TRUE);

				cout << "Set feature values for CAMERA " << i << endl;
				cout << "--------------------------------" << endl;

				//---- Gain feature ----
				memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
				feature.feature_index = IIDCID_FEATURE_GAIN;
				iidc_currentfeature( m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				feature.value = (int)SONY_GAIN;
				iidc_setfeature(m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				cout << "GAIN feature value: " << (int)feature.value << endl;

				//---- Shutter ----
				memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
				feature.feature_index = IIDCID_FEATURE_SHUTTER;
				feature.flags = iidcfeature_value;
				iidc_currentfeature( m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				feature.value = (float)SONY_SHUTTER;
				iidc_setfeature(m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				cout << "Shutter       : " << (float)feature.value << endl;

				//---- Get color feature overview ----
				//---- White balance U----
				memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
				feature.feature_index = IIDCID_FEATURE_WHITE_BALANCE;
				iidc_currentfeature( m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				feature.value2 = (int)SONY_WHITEBALANCE_U;
				iidc_setfeature(m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				cout << "White Balance U: " << (int)feature.value2 << endl;

				//---- White balance V----
				memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
				feature.feature_index = IIDCID_FEATURE_WHITE_BALANCE;
				feature.flags = iidcfeature_value;
				iidc_currentfeature( m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				feature.value = (int)SONY_WHITEBALANCE_V;
				iidc_setfeature(m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				cout << "White Balance V: " << (int)feature.value << endl;

				//---- HUE ----
				memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
				feature.feature_index = IIDCID_FEATURE_HUE;
				feature.flags = iidcfeature_value;
				iidc_currentfeature( m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				feature.value = (int)SONY_HUE;
				iidc_setfeature(m_hCamera[i], &feature, sizeof(IIDC_FEATURE) );
				cout << "Hue            : " << (int)feature.value << endl;
				cout << endl;
			}  
		}

		m_iDevice = 0;
		setDesiredParams(ImgParams(Size(m_iWidth, m_iHeight), icl::formatMatrix));

		return true;
	}

	bool SonyFwGrabber::initTrigger() {
		IIDC_SONY_STRUCT_SOFTTRIGGER *trigger = new IIDC_SONY_STRUCT_SOFTTRIGGER[m_lNumCameras];
		for (int i=0; i<m_lNumCameras; i++) {
			//stop sequence mode
			iidc_idle(m_hCamera[i]);
			//start snap mode
			iidc_capture(m_hCamera[i], false);

			//check soft-trigger capability
			//iidc_extended(m_hCamera[i], IIDC_EXTEND_SONY_SOFTTRIGGER_GET_CAP, 
			//			  &trigger[i], sizeof(IIDC_SONY_STRUCT_SOFTTRIGGER);
			//set soft-trigger mode
			trigger[i].mode = 0;
			trigger[i].timer = 0;
			iidc_extended(m_hCamera[i], IIDC_EXTEND_SONY_SOFTTRIGGER_SET,
                       &trigger[i], sizeof(IIDC_SONY_STRUCT_SOFTTRIGGER));
			//check if mode actually set
			// IIDC_EXTEND_SONY_SOFTTRIGGER_SET
		}
		return true;
	}

	std::string SonyFwGrabber::getValue(const std::string &name) {
		//TODO return values for all cameras or get num of camera as argument!
		IIDC_FEATURE feature;
		if(name == "size"){
			return translateSize(Size(m_iWidth, m_iHeight));
		}else if(name == "gain"){
			memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
			feature.feature_index = IIDCID_FEATURE_GAIN;
			iidc_currentfeature( m_hCamera[0], &feature, sizeof(IIDC_FEATURE) );
			feature.value = (int)SONY_GAIN;
			char buf[20];
			sprintf(buf,"%d",int(feature.value));
			return buf;
		}else if(name == "white balance red" || name == "white balance blue" || name == "white balance mode"){
			if(name == "white balance red"){ 
			}else if(name == "white balance blue"){}
			return "TODO";
		}else if(name == "format"){
			return "TODO";
		}else if(name == "shutter speed"){
			memset( (void*)&feature, 0, sizeof(IIDC_FEATURE) );
			feature.feature_index = IIDCID_FEATURE_SHUTTER;
			feature.flags = iidcfeature_value;
			iidc_currentfeature( m_hCamera[0], &feature, sizeof(IIDC_FEATURE) );
			feature.value = (float)SONY_SHUTTER;
			char buf[20];
			sprintf(buf,"%d",int(feature.value));
			return buf;
		}
		return "undefined";
	}

	void SonyFwGrabber::setProperty(const std::string &property, const std::string &value) {
		if(property == "size"){
			Size newSize = translateSize(value);
			//setGrabbingSize(newSize);
		}else if(property == "format"){
			/*if(value != "YUV 4-2-0 planar"){
				ERROR_LOG("invalid format \"" << value <<"\"");
			}*/
		}else if(property == "gain"){
			int val = atoi(value.c_str());
			//setGain(clip(val,0,65535));
		}else if(property == "save user settings"){
			//saveUserSettings(); // value is ignored
		}else if(property == "restore user settings"){
			//restoreUserSettings();
		}else if(property == "white balance mode"){
			/*int mode = -1; // unknown default
			if      (value == "indoor") mode = 0;
			else if (value == "outdoor") mode = 1;
			else if (value == "fl-tube") mode = 2;
			else if (value == "auto") mode = 4;
			if (mode != -1) {
				setWhiteBalance(mode);
			}else{
				ERROR_LOG("unknown white balance mode \"" << value << "\"");
			}*/
		}else if(property == "white balance"){
			/*vector<double> vec = Grabber::translateDoubleVec (value);
			if (vec.size() != 2) {
				ERROR_LOG("two white balance values required (red + blue)");
			} else {
				setWhiteBalance((int)vec[0], (int)vec[1]);
			}*/
		}else if(property == "white balance red"){
			int val = atoi(value.c_str());
			//setWhiteBalance(val,-1);
		}else if(property == "white balance blue"){
			int val = atoi(value.c_str());
			//setWhiteBalance(-1,val);
		}else if(property == "shutter speed"){
			int val = atoi(value.c_str());
			//setShutterSpeed(val);
		}else{
			ERROR_LOG("nothing known about a property " << property ); 
		}
	}

	std::vector<std::string> SonyFwGrabber::getPropertyList() {
		std::vector<std::string> v;
		v.push_back("size");
		v.push_back("format");
		v.push_back("gain");
		v.push_back("save user settings");
		v.push_back("restore user settings");
		v.push_back("white balance mode");
		v.push_back("white balance red");
		v.push_back("white balance blue");
		v.push_back("shutter speed");
		return v;
	}

   void SonyFwGrabber::copyGrabbingBuffer (int iDevice, ImgBase *poDst) {
      LPBYTE pImgBuf = (LPBYTE) iidc_lockdata( m_hCamera[iDevice], -1 );
      if(pImgBuf) {
         // create temporary wrapper image around current data buffer
         // TODO: currently this assume single-channel data
         // If a grabbing buffer for a single frame is used only (as currently done)
         // one could also allocate the appropriate wrapper image in the init() method
         // already.
         std::vector<icl8u*> vData (&pImgBuf, (&pImgBuf)+1);
         Img<icl8u> imgBuf (getSize(), formatMatrix, vData);
         flippedCopy (axisHorz, &imgBuf, &poDst);
				
         //---- remove data lock ----
         iidc_unlockdata( m_hCamera[iDevice] );
      }
   }

	const ImgBase* SonyFwGrabber::grabUD(ImgBase **ppoDst) {
		ImgBase *poOutput = prepareOutput (ppoDst);
		
		//---- Initialize variables ----
		IIDC_WAIT waitparam;
		waitparam.event = IIDCID_EVENT_FRAMEEND;
		waitparam.timeout = 1000;

		//---- wait for complete image frame from grabbing ----
		bool bRet = iidc_wait( m_hCamera[m_iDevice], &waitparam, sizeof(IIDC_WAIT) );
		
		if (bRet) copyGrabbingBuffer (m_iDevice, poOutput);
		return poOutput;
	}

	void SonyFwGrabber::grabStereo(ImgBase*& poDstLeft, ImgBase*& poDstRight) {
		if (m_lNumCameras < 2) return;
 
		ensureCompatible(&poDstLeft,  m_eDesiredDepth, m_oDesiredParams);
		ensureCompatible(&poDstRight, m_eDesiredDepth, m_oDesiredParams);

		//---- Initialize variables ----
		IIDC_WAIT waitparam;
		waitparam.event = IIDCID_EVENT_FRAMEEND;
		waitparam.timeout = 1000;

		//---- wait for complete image frames from grabbing ----
		bool bRetLeft  = iidc_wait( m_hCamera[0], &waitparam, sizeof(IIDC_WAIT) );
		bool bRetRight = iidc_wait( m_hCamera[1], &waitparam, sizeof(IIDC_WAIT) );

      if (bRetLeft)  copyGrabbingBuffer (0, poDstLeft);
      if (bRetRight) copyGrabbingBuffer (1, poDstRight);
	}

	void SonyFwGrabber::grabStereoTrigger(ImgBase*& poDstLeft, ImgBase*& poDstRight) {
		if (m_lNumCameras < 2) return;
 
		ensureCompatible(&poDstLeft,  m_eDesiredDepth, m_oDesiredParams);
		ensureCompatible(&poDstRight, m_eDesiredDepth, m_oDesiredParams);

		//---- Trigger Grabbing
		iidc_capture(m_hCamera[0], false);
		iidc_capture(m_hCamera[1], false);

		//??? trigger with iidc_extended(...) ???

		//---- Initialize variables ----
		IIDC_WAIT waitparam;
		waitparam.event = IIDCID_EVENT_FRAMEEND;
		waitparam.timeout = 1000;

		//---- Get image from buffer ----
		bool bRetLeft = iidc_wait( m_hCamera[0], &waitparam, sizeof(IIDC_WAIT) );
		bool bRetRight = iidc_wait( m_hCamera[1], &waitparam, sizeof(IIDC_WAIT) );

      if (bRetLeft)  copyGrabbingBuffer (0, poDstLeft);
      if (bRetRight) copyGrabbingBuffer (1, poDstRight);
   }

	void SonyFwGrabber::GetCamAllString(long camIndex, char *strCamera) {
		char* pBuf1, *pBuf2, *pBuf3;
		long	lRet = iidc_getstring( camIndex, IIDCID_STRING_VENDOR, NULL, 0 );
		pBuf1 = new char[lRet+1];
		memset( pBuf1, NULL, lRet+1);
		lRet = iidc_getstring( camIndex, IIDCID_STRING_VENDOR, pBuf1, lRet );
		if( !lRet )
		{
			delete [] pBuf1;
			return;
		}

		lRet = iidc_getstring( camIndex, IIDCID_STRING_MODEL, NULL, 0 );
		pBuf2 = new char[lRet+1];
		memset( pBuf2, NULL, lRet+1);
		lRet = iidc_getstring( camIndex, IIDCID_STRING_MODEL, pBuf2, lRet );
		if( !lRet )
		{
			delete [] pBuf2;
			return;
		}

		lRet = iidc_getstring( camIndex, IIDCID_STRING_CAMERAID, NULL, 0 );
		pBuf3 = new char[lRet+1];
		memset( pBuf3, NULL, lRet+1);
		lRet = iidc_getstring( camIndex, IIDCID_STRING_CAMERAID, pBuf3, lRet );
		if( !lRet )
		{
			delete [] pBuf3;
			return;
		}

		sprintf(strCamera,"%s %s %s",pBuf1,pBuf2,pBuf3);

		delete [] pBuf1;
		delete [] pBuf2;
		delete [] pBuf3;
	}

}

#endif //SONYIIDC
#endif //WIN32