#define USE_MMAP 1

#include <string.h>
#include <assert.h>
#include <string>
#include <iostream>

#include "V4LCamera.h"

//#if defined(HAVE_V4L)
#define LARGE 1

V4LCamera v4lcamera;

#define MY_BLACK 0
#define MY_WHITE 255

#if !LARGE
int MY_COLUMNS =88;
int MY_ROWS    =72;
#else
int MY_COLUMNS = 176;
int MY_ROWS    = 144;
//int MY_COLUMNS = 147;
//int MY_ROWS    = 126;
#endif

#define MY_TYPE     pix_rgb24

#define IN_TV		0
#define IN_COMPOSITE	1
#define IN_COMPOSITE2	2
#define IN_SVIDEO	3
#define IN_DEFAULT	8

#define NORM_PAL	0
#define NORM_NTSC	1
#define NORM_SECAM	2
#define NORM_DEFAULT	0


BYTE *
V4LCamera::set_up_mmap (int dev, int width, int height, int fmt)
{

	BYTE *map;
	int len;
	
	if (ioctl (dev, VIDIOCGMBUF, &vid_mbuf) == -1)
	  {
	    fprintf(stderr, "\nSorry, can't use memory map\n");
	    return(NULL);
	  }

	map = (BYTE*)mmap (0, vid_mbuf.size, PROT_READ|PROT_WRITE,MAP_SHARED,dev,0);
	if ((unsigned char *)-1 == (unsigned char *)map) {
		perror ("mmap()");
		return (NULL);
	}

	vid_mmap.format = fmt;
	vid_mmap.frame  = 0;
	vid_mmap.width  = width;
	vid_mmap.height = height;
	
	if (ioctl (dev, VIDIOCMCAPTURE, &vid_mmap) == -1) {
		perror ("VIDIOCMCAPTURE");
		munmap (map, vid_mbuf.size);
		return (NULL);
	}
	
	return (map);
}

void V4LCamera::mmap_read(Picture *p_frame, int expected_size)
{

  if (ioctl (vid_fd, VIDIOCSYNC, &vid_mmap) == -1) {
		perror ("VIDIOCSYNC");
		munmap (video_map, vid_mbuf.size);
		return;
	}
  
  //memcpy(p_frame->data, video_map, expected_size);

  p_frame->data=video_map;

  if (ioctl (vid_fd, VIDIOCMCAPTURE, &vid_mmap) == -1) {
                perror ("VIDIOCMCAPTURE");
		munmap (video_map, vid_mbuf.size);
		return;
	}

}



void V4LCamera::set_brightness(int value)
{

  vid_pic.brightness = value;
  if (ioctl (vid_fd, VIDIOCSPICT, &vid_pic) == -1)
    perror ("ioctl (VIDIOCSPICT) - unable to set brightness\n");
 
}

void
V4LCamera::read(Picture *p_frame)
{
  int expected_size;
  int size;
  BYTE *p, temp;
  int i;
  
  /*
  if (greyscale)
    expected_size = width*height;
  else
    expected_size = width*height*3;

  */
  
  expected_size = p_frame->datasize;

  if (p_frame->data == NULL)
   {
     p_frame->data = (BYTE*) malloc (sizeof(BYTE) * expected_size);
     fprintf(stdout,"That thing that should not be!\n");
   }
  
#ifdef USE_MMAP
  
  mmap_read(p_frame, expected_size);

  //swap r & b - arh this sucks
  p = p_frame->data;
  for (i=0; i < width*height; i++)
    {
      temp = p[0];
      p[0] = p[2];
      p[2] = temp;
      p+=3;
    }

#else
  size = ::read(vid_fd, p_frame->data, expected_size);

  if ((unsigned) size != expected_size)
    {
      cerr <<"v4l: read() returned " << size << " not "
	   << expected_size << " bytes" << endl;
      return;
    }

#endif
  
  frame_count++;

}
bool
V4LCamera::open(IPL_CONFIG *ipl_config)
{
  struct video_window vid_win;

  char* device = "/dev/video";

  int temp_fd = vid_fd;

  vid_fd = ::open(device, O_RDWR);
  if (0 > vid_fd)
    {
      
      if (errno != EBADF)
	{
	  cerr << "v4l: open(" << device << ") failed: " << strerror(errno) << endl;
	  return false;
	}
      else 
	vid_fd = temp_fd;
    }
  
  if (0 != ioctl(vid_fd, VIDIOCGCAP, &vid_cap))
    {
      cerr << "v4l: Device " << device << " is not a video4linux capture device. (Failed ioctl)" << endl;
      ::close(vid_fd);
      return false;
    }


  if (!(vid_cap.type & VID_TYPE_CAPTURE))
    {
      cerr << "v4l: Device " << device << " is not a video capture device."
	   << endl;
      ::close(vid_fd);
      return false;
    }
 
  if (0 != ioctl (vid_fd, VIDIOCGPICT, &vid_pic))
    {
      cerr << "v4l: Device " << device << " VIDIOCGPICT failed: " <<
	strerror(errno) << endl;
      ::close(vid_fd);
      return false;
    }

  if (vid_cap.type & VID_TYPE_MONOCHROME)
    {
      greyscale = true;
      palette = VIDEO_PALETTE_GREY;
      vid_pic.depth = 8;
    }
  else
    {
      greyscale = false;
      palette = VIDEO_PALETTE_RGB24;
      vid_pic.depth = 24;
    }
  
  vid_pic.palette = palette;
  
  if(0 != ioctl (vid_fd, VIDIOCSPICT, &vid_pic)) 
    {
      cerr << "v4l: Device " << device << " VIDIOCSPICT sfailed: " <<
	strerror(errno) << endl;
      ::close(vid_fd);

      return false;
    }
  
  if (0 != ioctl (vid_fd, VIDIOCGWIN, &vid_win)) 
    {
      cerr << "v4l: Device " << device << " VIDIOCGWIN failed: " <<
	strerror(errno) << endl;
      ::close(vid_fd);      return false;
    }
  
  vid_win.x=0;
  vid_win.y=0;
  vid_win.width=MY_COLUMNS;
  vid_win.height=MY_ROWS;

  if (0 != ioctl (vid_fd, VIDIOCSWIN, &vid_win)) 
    {
      cerr << "v4l: Device " << device << " VIDIOCSWIN failed: " <<
	strerror(errno) << endl;
      ::close(vid_fd);
      return false;
    }
  
  /* check to see that image dimension are possible */
  if (0 != ioctl (vid_fd, VIDIOCGWIN, &vid_win)) 
    {
      cerr << "v4l: Device " << device << " VIDIOCGWIN failed: " <<
	strerror(errno) << endl;
      ::close(vid_fd);
      return false;
    }
 
  if ( (vid_win.width != MY_COLUMNS) || (vid_win.height != MY_ROWS) )
    fprintf(stderr, "\nWarning: The specified image dimensions are not possible. The driver has decided you want image dimensions of %dX%d.\n"
	    , vid_win.width, vid_win.height);
  
  ipl_config->colorBlack  = MY_BLACK;
  ipl_config->colorWhite  = MY_WHITE;
  ipl_config->imageWidth  = vid_win.width;
  ipl_config->imageHeight = vid_win.height;
  ipl_config->imageType   = MY_TYPE; 

  width   = vid_win.width;
  height  = vid_win.height;
  input   = IN_DEFAULT;
  norm    = NORM_DEFAULT;

#ifdef USE_MMAP
 
  video_map =  set_up_mmap (vid_fd, width, height, palette);
  if (video_map == NULL)
    return(false);

#endif
  
  return true;

}

void V4LCamera::set_contrast(int value)
{

  vid_pic.contrast = value;
  if (ioctl (vid_fd, VIDIOCSPICT, &vid_pic) == -1)
    perror ("ioctl (VIDIOCSPICT) - unable to set contrast\n");

}


void V4LCamera::set_colour(int value)
{

  vid_pic.colour = value;
  if (ioctl (vid_fd, VIDIOCSPICT, &vid_pic) == -1)
    perror ("ioctl (VIDIOCSPICT) - unable to set colour\n");
}



void
V4LCamera::close(void)
{
  
#ifdef USE_MMAP
  munmap (video_map, vid_mbuf.size);
#endif

  if (-1 != vid_fd)
    ::close(vid_fd);
  vid_fd= -1;

}

bool
V4LCamera::iscolor(void)
{
  if (vid_cap.type & VID_TYPE_MONOCHROME)
    return(false);
    
  else
    return(true);

}

void
V4LCamera::get_info(CamInfo *info)
{
  info->width = width; 
  info->height = height;
  if (iscolor() == true)
    info->bpp = 24;
  else
    info->bpp = 8;  /* for the quickcam on xena(Thu Jul 13 14:39:26 WST 2000)*/

}

bool 
V4LCamera::get_IPL(IPL_CONFIG *ipl_config)
{

  ipl_config->imageWidth  = width;
  ipl_config->imageHeight = height;
  ipl_config->imageType   = pix_rgb24;
  ipl_config->image_ratio = (float) ((float)ipl_config->imageHeight / 
				     (float) ipl_config->imageWidth);

}

void V4LCamera::params(double *p)
{
  struct video_picture vid_pic;
  
  vid_pic.brightness = (int) p[3];
  ioctl (vid_fd, VIDIOCSPICT, &vid_pic) ;
  
  vid_pic.contrast = (int) p[4];  
  ioctl (vid_fd, VIDIOCSPICT, &vid_pic);

  vid_pic.colour = (int) p[5];  
  ioctl (vid_fd, VIDIOCSPICT, &vid_pic); 
  
}

//#endif /* HAVE_V4L */

