#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>

namespace mimas {

template< typename T >
image_v4linput< T >::image_v4linput
( const std::string &_device, int channel, int width, int height,
  int channel_norm )
  throw (mimasexception):
  device(_device), fd( -1 ), map( MAP_FAILED )
{
  struct stat st;
  MMERROR( stat( device.c_str(), &st ) == 0, mimasexception, ,
           "Couldn't read file-attributes of \"" << device << "\": "
           << strerror( errno ) );
  MMERROR( S_ISCHR(st.st_mode), mimasexception, ,
           '"' << device << "\" is not a device" );
  fd = open( device.c_str(), O_RDWR, 0 );
  MMERROR( fd != -1, mimasexception, ,
           "Could not open device \"" << device << "\": "
           << strerror( errno ) );
  try {
    struct video_capability cap;
    MMERROR( xioctl( VIDIOCGCAP, &cap ) == 0, mimasexception, ,
             "Error " << errno << " requesting video capabilities of device \""
             << device << "\": " << strerror( errno ) );
#ifndef NDEBUG
    std::cerr << cap.name << ":" << std::endl
              << "type = " << cap.type << std::endl
              << "channels = " << cap.channels << std::endl
              << "audios = " << cap.audios << std::endl
              << "minwidth = " << cap.minwidth << std::endl
              << "minheight = " << cap.minheight << std::endl
              << "maxwidth = " << cap.maxwidth << std::endl
              << "maxheight = " << cap.maxheight << std::endl;
#endif
    selectPalette();
    
    chan.channel = channel;
    chan.type = VIDEO_TYPE_CAMERA;
    chan.norm = channel_norm;
    MMERROR( xioctl( VIDIOCSCHAN, &chan ) == 0, mimasexception, ,
             "Error setting channel information for channel "
             << chan.channel << " of device \"" << device << "\": "
             << strerror( errno ) );

    /* chan.type = VIDEO_TYPE_CAMERA;
       chan.norm = VIDEO_MODE_PAL;
       MMERROR( xioctl( VIDIOCSCHAN, &chan ) == 0, mimasexception, ,
       "Error setting channel info." ); */

    win.x = win.y = 0;
    win.chromakey = 0 ;
    win.width = width == -1 ? cap.maxwidth : width;
    win.height = height == -1 ? cap.maxheight : height;
    win.flags = 0;
    // Try to set values.
#ifndef NDEBUG
    int r = xioctl( VIDIOCSWIN, &win );
    if ( r != 0 )
      std::cerr << "Setting capture window failed." << std::endl;
#else
    xioctl( VIDIOCSWIN, &win );
#endif
    // Get values choosen by driver.
    MMERROR( xioctl( VIDIOCGWIN, &win ) == 0, mimasexception, ,
             "Error querying capture window of device \"" << device << "\": "
             << strerror( errno ) );
    
    if ( xioctl( VIDIOCGMBUF, &buf ) == 0 ) {
#ifndef NDEBUG
      std::cerr << "Memory-map interface:" << std::endl
                << "buffer-size = " << buf.size << std::endl
                << "frames = " << buf.frames << std::endl
                << "offset of each frame = " << buf.offsets << std::endl;
#endif
      map = mmap( 0, buf.size, PROT_READ, MAP_SHARED, fd, 0 );
#ifndef NDEBUG
      if ( map == MAP_FAILED )
        std::cerr << "Failed to map memory." << std::endl;
#endif
    } else {
#ifndef NDEBUG
      std::cerr << "Memory-map interface is not supported." << std::endl;
#endif
    };
  } catch ( mimasexception &e ) {
    if ( map != MAP_FAILED ) munmap( map, buf.size );
    if ( fd != -1 ) close( fd );
    throw e;
  }
}

template< typename T >
image_v4linput< T >::~image_v4linput(void)
{
  if( map != MAP_FAILED )
    munmap( map, buf.size );
  assert( fd != -1 );
  close( fd );
}

template< typename T >
int image_v4linput< T >::xioctl( int request, void *arg )
{
  int r;
  do {
    r = ioctl( fd, request, arg );
#ifndef NDEBUG
    if ( r == -1 && errno == EINTR )
      std::cerr << "ioctl returned " << r << std::endl
                << "errno is " << errno << std::endl;
#endif
  } while ( r == -1 && errno == EINTR );
  return r;
}

template< typename T >
void image_v4linput< T >::setSensivity( __u16 brightness,
                                           __u16        hue,
                                           __u16        colour,
                                           __u16        contrast )
  throw (mimasexception)
{
  pic.brightness = brightness;
  pic.hue = hue;
  pic.colour = colour;
  pic.contrast = contrast;
  MMERROR( xioctl( VIDIOCSPICT, &pic ) == 0,
           mimasexception, , "Error changing sensivity of video-device." );
}

};