/* ex: set tabstop=4 expandtab: */
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <values.h>
#include <string.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/videodev.h>
#include <sys/mman.h>
#include <sys/types.h>


static PyObject * openVideo(PyObject *self, PyObject *args);
static PyObject * grabVideo(PyObject *self, PyObject *args);
static PyObject * closeVideo(PyObject *self, PyObject *args);


static PyMethodDef Methods[] = {
    {"openVideo",  openVideo, METH_VARARGS, "Open a video device for reading."},
    {"grabVideo",  grabVideo, METH_VARARGS, "Grab one frame of video."},
    {"closeVideo",  closeVideo, METH_VARARGS, "Close the video device."},
    {NULL, NULL, 0, NULL}        /* Sentinel */
    };



void initvideoInput(void)
    {
    (void) Py_InitModule("videoInput", Methods);
    }


static int video_fd;
static unsigned char * videoData;
static unsigned char * returnBuffer = NULL;
static int videoWidth, videoHeight;
static struct video_mbuf videoBuffers;
static int videoFrame=0;

#define RETURN_PY_NONE { Py_INCREF(Py_None); return Py_None; }


static PyObject * openVideo(PyObject *self, PyObject *args)
    {
    char *devname;
    struct video_channel channel;

    if (!PyArg_ParseTuple(args, "sii", &devname, &videoWidth, &videoHeight))
        return NULL;

    if (!devname)
        devname = "/dev/video";

    video_fd = open(devname, O_RDWR);
    if (video_fd == -1)
        {
        perror(devname);
        RETURN_PY_NONE
        }
    
    if (returnBuffer)
        free(returnBuffer);
    returnBuffer = (unsigned char *) malloc(videoWidth*videoHeight*3);
    
    channel.channel = 1;
    channel.norm = VIDEO_MODE_NTSC;
    if (ioctl(video_fd,VIDIOCSCHAN,&channel) == -1)
        {
        perror("VIDIOCSCHAN");
        close(video_fd);
        video_fd = -1;
        RETURN_PY_NONE
        } 

    struct video_picture pic;
    pic.depth = 24;
    pic.palette = VIDEO_PALETTE_RGB24;
    pic.brightness = pic.hue = pic.colour = pic.contrast = pic.whiteness = 32767;
    ioctl(video_fd, VIDIOCSPICT, &pic);
    
    ioctl(video_fd, VIDIOCGMBUF, &videoBuffers);

    videoData = mmap(0, videoBuffers.size, PROT_READ, MAP_SHARED, video_fd, 0);

    struct video_mmap vmap;
    vmap.format = VIDEO_PALETTE_RGB24;
    vmap.frame  = videoFrame;
    vmap.width  = videoWidth;
    vmap.height = videoHeight;
    if (ioctl(video_fd, VIDIOCMCAPTURE, &vmap) == -1)
        perror("VIDIOCMCAPTURE");

    RETURN_PY_NONE
    }



static PyObject * grabVideo(PyObject *self, PyObject *args)
    {
    if (video_fd == -1)
        RETURN_PY_NONE

    struct video_mmap vmap;
    vmap.format = VIDEO_PALETTE_RGB24;
    vmap.frame  = (videoFrame + 1) % videoBuffers.frames;
    vmap.width  = videoWidth;
    vmap.height = videoHeight;
    if (ioctl(video_fd,VIDIOCMCAPTURE,&vmap) == -1)
        perror("VIDIOCMCAPTURE");
    
    vmap.format = VIDEO_PALETTE_RGB24;
    vmap.frame  = videoFrame;
    vmap.width  = videoWidth;
    vmap.height = videoHeight;
    if (ioctl(video_fd,VIDIOCSYNC,&vmap) == -1)
        perror("VIDIOCSYNC");

    unsigned char * data = videoData+videoBuffers.offsets[videoFrame];
    int i;
    for (i=0; i < videoWidth*videoHeight*3; i+=3)
        {
        returnBuffer[i+0] = data[i+2];
        returnBuffer[i+1] = data[i+1];
        returnBuffer[i+2] = data[i+0];
        }
        
    videoFrame = (videoFrame + 1) % videoBuffers.frames;
    
    return Py_BuildValue("s#", returnBuffer, videoWidth*videoHeight*3);
    }


static PyObject * closeVideo(PyObject *self, PyObject *args)
    {
    if (video_fd != -1)
        {
        close(video_fd);
        video_fd = -1;
        }
    if (returnBuffer)
        {
        free(returnBuffer);
        returnBuffer = NULL;
        }
    RETURN_PY_NONE
    }

