#include "amcframegrabber.h"
#include <qdatetime.h>
#include <qtimer.h>
#include <qthread.h>

AMCFrameGrabber::AMCFrameGrabber()
{
	m_pMutex = new QMutex();
  m_zActive = false;

	m_iOffset = 0;
	
	// assign /dev/video0 to device if none is assigned.
	m_pDevice = "/dev/video0";
  
	//opens the fgrabber for reading only.
	m_iVid = open(m_pDevice, O_RDONLY);
	if (m_iVid < 0)
	{
		fprintf(stderr, "Can't open %s\n", m_pDevice);
	}

	getPixelFormats();
	getVideoStandards();
	getStandard();
	setStandardPAL();
	getStandard();
	getBufferFormats();
	getControls();

	// finds out what the fgrabber is capable of
	m_iErr = ioctl(m_iVid, VIDIOC_QUERYCAP, &m_stCap);
	if (m_iErr)
	{
		fprintf(stderr, "QUERYCAP returned error %d\n", errno);
		close(m_iVid);
	}

	qDebug( "Device:    %s", m_stCap.name);
	qDebug( "Type:      %d", m_stCap.type);
	qDebug( "Inputs:    %d", m_stCap.inputs);
	qDebug( "Outputs:   %d", m_stCap.outputs);
	qDebug( "MaxWidth:  %d", m_stCap.maxwidth);
	qDebug( "MaxHeight: %d", m_stCap.maxheight);
	qDebug( "MinWidth:  %d", m_stCap.minwidth);
	qDebug( "MinHeight: %d", m_stCap.minheight);
	qDebug( "F-rate:    %d", m_stCap.maxframerate);

	// check if it is an image capturing device
	if (m_stCap.type != V4L2_TYPE_CAPTURE)
  {
		fprintf( stderr, "Device %s is not a video capture device.\n", m_pDevice);
		close(m_iVid);
	}
  
	// check if the device can be read like a file.
	if (!(m_stCap.flags & V4L2_FLAG_READ))
	{
		fprintf(stderr, "Device %s doesn't support read().\n", m_pDevice);
		close(m_iVid);
	}

	// getting the available formats.
	m_stFmt.type = V4L2_BUF_TYPE_CAPTURE;
	m_iErr = ioctl(m_iVid, VIDIOC_G_FMT, &m_stFmt);
	if (m_iErr)
	{
		fprintf(stderr, "G_FMT returned error %d\n", errno);
		close(m_iVid);
	}
  
	//assigning desired format
	m_stFmt.fmt.pix.flags = FMT_FLAG;
	m_stFmt.fmt.pix.width = MY_WIDTH;
	m_stFmt.fmt.pix.height = MY_HEIGHT;
	m_stFmt.fmt.pix.pixelformat = MY_PIXEL_FORMAT;
	m_iErr = ioctl(m_iVid, VIDIOC_S_FMT, &m_stFmt);
	if (m_iErr)
	{
		fprintf(stderr, "S_FMT returned error %d\n", errno);
		close(m_iVid);
	}
	qDebug( "Width: %d", m_stFmt.fmt.pix.width);
	qDebug( "Height: %d", m_stFmt.fmt.pix.height);
	qDebug( "Size: %d", m_stFmt.fmt.pix.sizeimage);
	m_iBuffsize = m_stFmt.fmt.pix.sizeimage;

};

AMCFrameGrabber::~AMCFrameGrabber()
{
	delete m_pMutex;
	//closing the device and freeing allocated memory.
	close(m_iVid);
};

u_int AMCFrameGrabber::getBuffsize() const
{
	return m_iBuffsize;
};

int AMCFrameGrabber::grabFrame(u_char *data)
{
	m_pMutex->lock();
	m_zActive = true;
	m_pMutex->unlock();

  fd_set rfds;
  FD_ZERO(&rfds);
  FD_SET( m_iVid, &rfds);
  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 1000;
  int retval = select(m_iVid+1, &rfds, NULL, NULL, &tv);
//  if( ! retval )
//    qDebug("Timed out waiting for frame");

  int n;
  n = read(m_iVid, data, m_stFmt.fmt.pix.sizeimage);

  //one can do a tricky thing with pointers.
    
	m_pMutex->lock();
	m_zActive = false;
	m_pMutex->unlock();

	if (n < 0)
	{
		fprintf(stderr, "read()3 returned error %d\n", errno);
		perror("Descripton");
//		close(vid);
		return -1;
	}
  
	return n;
};

/**  */
void AMCFrameGrabber::getPixelFormats( )
{
  // Enum the supported pixel formats
  qDebug("Pixel formats:");
  for(int i=0;;i++)
  {
    m_stFmtDesc.index = i;
    int err = ioctl(m_iVid, VIDIOC_ENUM_PIXFMT, &m_stFmtDesc);
    if (err)
      break;
    qDebug("Index:          %d", m_stFmtDesc.index);
    qDebug("Description:    %s", m_stFmtDesc.description);
    qDebug("Flags:          %d", m_stFmtDesc.flags);
    qDebug("Depth:          %d\n", m_stFmtDesc.depth);

  }
}

/**  */
void AMCFrameGrabber::getBufferFormats( )
{
  // Enum the supported frame buffer formats
  qDebug("Buffer formats:");
  for(int i=0;;i++)
  {
    m_stFmtDesc.index = i;
    int err = ioctl(m_iVid, VIDIOC_ENUM_FBUFFMT, &m_stFmtDesc);
    if (err)
      break;
    qDebug("Index:          %d", m_stFmtDesc.index);
    qDebug("Description:    %s", m_stFmtDesc.description);
    qDebug("Flags:          %d", m_stFmtDesc.flags);
    qDebug("Depth:          %d\n", m_stFmtDesc.depth);

  }
}

/**  */
void AMCFrameGrabber::getVideoStandards( )
{
  // Enum the supported frame buffer formats
  qDebug("Video formats:");
  for(int i=0;;i++)
  {
    m_stEnumStd.index = i;
    int err = ioctl(m_iVid, VIDIOC_ENUMSTD, &m_stEnumStd);
    if (err)
      break;
    qDebug("Name:                  %s", m_stEnumStd.std.name);
    qDebug("Length:                %d", strlen(m_stEnumStd.std.name));
    qDebug("Framerate Numerator:   %d", m_stEnumStd.std.framerate.numerator);
    qDebug("Framerate Denominator: %d", m_stEnumStd.std.framerate.denominator);
    qDebug("Framelines  :          %d", m_stEnumStd.std.framelines);
    qDebug("Inputs  :              %X\n", m_stEnumStd.inputs);
    if(strcmp( m_stEnumStd.std.name, "PAL" ) == 0)
			m_stStdPAL = m_stEnumStd.std;
    if(strcmp( m_stEnumStd.std.name, "NTSC" ) == 0)
			m_stStdNTSC = m_stEnumStd.std;
  }
}

/**  */
void AMCFrameGrabber::getStandard()
{
  // Enum the supported frame buffer formats
  qDebug("Actual video standard:");
  int err = ioctl(m_iVid, VIDIOC_G_STD, &m_stStandard);
  qDebug("Name:                  %s\n", m_stStandard.name);
}

/**  */
void AMCFrameGrabber::setStandardPAL()
{
  // Enum the supported frame buffer formats
  int err = ioctl(m_iVid, VIDIOC_S_STD, &m_stStdPAL);
}

/**  */
void AMCFrameGrabber::setStandardNTSC()
{
  // Enum the supported frame buffer formats
  int err = ioctl(m_iVid, VIDIOC_S_STD, &m_stStdNTSC);
}

/**  */
void AMCFrameGrabber::getControls( )
{
  // Enum the supported frame buffer formats
//  qDebug("Controls:");
  for(int i=V4L2_CID_BASE;;i++)
  {
    m_stCtrlDesc.id = i;
    int err = ioctl(m_iVid, VIDIOC_QUERYCTRL, &m_stCtrlDesc);
    if (err)
		{
      break;
		}

		// Skip disabled controls
		if( m_stCtrlDesc.flags == V4L2_CTRL_FLAG_DISABLED )
			continue;
/*    qDebug( "  Name:       %s (%d)  %3d-%3d  Step: %2d  Def: %3d Group: %s",
					  m_stCtrlDesc.name,
					  m_stCtrlDesc.id,
					  m_stCtrlDesc.minimum,
					  m_stCtrlDesc.maximum,
					  m_stCtrlDesc.step,
					  m_stCtrlDesc.default_value,
					  m_stCtrlDesc.group );
*/
  }
//  qDebug("Controls2:");
  for(int i=V4L2_CID_PRIVATE_BASE;;i++)
  {
    m_stCtrlDesc.id = i;
    int err = ioctl(m_iVid, VIDIOC_QUERYCTRL, &m_stCtrlDesc);
    if (err)
		{
      break;
		}
/*    qDebug( "  Name:       %s (%d)  %3d-%3d  Step: %2d  Def: %3d Group: %s",
					  m_stCtrlDesc.name,
					  m_stCtrlDesc.id,
					  m_stCtrlDesc.minimum,
					  m_stCtrlDesc.maximum,
					  m_stCtrlDesc.step,
					  m_stCtrlDesc.default_value,
					  m_stCtrlDesc.group );
*/
  }

}

/** Set the control. */
void AMCFrameGrabber::setControl( unsigned int p_iCtrl, int p_iValue )
{
	v4l2_control stCtrl;
  stCtrl.id = p_iCtrl;
	stCtrl.value = p_iValue;
  int err = ioctl(m_iVid, VIDIOC_S_CTRL, &stCtrl);
}

/** Get the control. */
int AMCFrameGrabber::getControl( unsigned int p_iCtrl )
{
	v4l2_control stCtrl;
  stCtrl.id = p_iCtrl;
  int err = ioctl(m_iVid, VIDIOC_G_CTRL, &stCtrl);
	return stCtrl.value;
}

/**
 * If we are still grabbing a video frame, wait until we are done.
 * In case we are active in grabbing a frame we sleep for 20ms (should be
 * enough time to grab an NTSC frame) to unload the system.
 */
void AMCFrameGrabber::waitTillInactive()
{
	for(;;)
	{
		m_pMutex->lock();
		if( ! m_zActive )
		{
			m_pMutex->unlock();
			return;
		}
		m_pMutex->unlock();
		usleep(20000);
	}
  return;
}
