/* 
 *
 * Copyright (C) 2002 George Staikos <staikos@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <endian.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#ifdef __STRICT_ANSI__
#define FOO__STRICT_ANSI__
#undef __STRICT_ANSI__
#endif
#include <asm/types.h>
#ifdef FOO__STRICT_ANSI__
#define __STRICT_ANSI__
#undef FOO__STRICT_ANSI__
#endif
#include <linux/videodev.h>

#include <kdebug.h>

#include "v4ldevtuner.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>

//////////////////////////////////////////////////////////////////////////////
////////////////////////  Tuner Implementation   /////////////////////////////
//////////////////////////////////////////////////////////////////////////////

int findPalette(void) {

XVisualInfo *vi_in, vi_out;
long mask = VisualScreenMask;
int nvis = 0;
int rc = -1;

	vi_out.screen = QPaintDevice::x11AppScreen();
	vi_in = XGetVisualInfo(qt_xdisplay(), mask, &vi_out, &nvis);

	if (vi_in) {
		for (int i = 0; i < nvis; i++) {
			int bpp = -1;
			int n;
			XPixmapFormatValues *pf = XListPixmapFormats(qt_xdisplay(),&n);
			for (int j = 0; j < n; j++) {
				if (pf[j].depth == vi_in[i].depth) {
					bpp = pf[j].bits_per_pixel;
					break;
				}
			}

			XFree(pf);

			switch (bpp) {
			case 32:
				if (vi_in[i].red_mask == 0xff0000 &&
				    vi_in[i].green_mask == 0x00ff00 &&
				    vi_in[i].blue_mask == 0x0000ff) {
					rc = VIDEO_PALETTE_RGB32;
					kdDebug() << "Found RGB32 display." << endl;
				}
			break;
			case 24:
				if (vi_in[i].red_mask == 0xff0000 &&
				    vi_in[i].green_mask == 0x00ff00 &&
				    vi_in[i].blue_mask == 0x0000ff) {
					rc = VIDEO_PALETTE_RGB24;
					kdDebug() << "Found RGB24 display." << endl;
				}
			break;
			case 16:
				if (vi_in[i].red_mask == 0x00f800 &&
				    vi_in[i].green_mask == 0x0007e0 &&
				    vi_in[i].blue_mask == 0x00001f) {
					rc = VIDEO_PALETTE_RGB565;
					kdDebug() << "Found RGB565 display." << endl;
				} else   // is this correct?
				if (vi_in[i].red_mask == 0x007c00 &&
				    vi_in[i].green_mask == 0x0003e0 &&
				    vi_in[i].blue_mask == 0x00001f) {
					rc = VIDEO_PALETTE_RGB555;
					kdDebug() << "Found RGB555 display." << endl;
				}
			break;
			case 8:
			default:
				continue;
			}
			if (rc != -1)
				break;
		}
	}

return rc;
}



V4LTuner::V4LTuner(int fd, const QString &name, int channels, int type, int minw, int minh, int maxw, int maxh) 
: V4LDev(fd, name, channels, type, minw, minh, maxw, maxh), _disableOverlay(false) {
struct video_picture vp;
struct video_buffer vb;

	_isTuner = true;  // for now
	_tunerNum = -1;

	int rc = ioctl(_fd, VIDIOCGFBUF, &vb);

	int bpp = QPaintDevice::x11AppDepth();
	int n;
	XPixmapFormatValues *pf = XListPixmapFormats(qt_xdisplay(), &n);
	for (int j = 0; j < n; j++) {
		if (pf[j].depth == bpp) {
			bpp = pf[j].bits_per_pixel;
			break;
		}
	}

	XFree(pf);

	if (vb.depth != bpp) {
		kdDebug() << "V4L and QtVision disagree about the depth of the"
			  << " display.  Is kv4lsetup installed suid root?"
			  << endl;
		kdDebug() << "(I think it should be " << bpp << ")" << endl;
		_disableOverlay = true;
		return;
	}

	int palette = findPalette();
	if (palette < 0) {
		kdDebug() << "Couldn't find a suitable palette to use.  What "
			  << " kind of display do you have?  Overlay disabled."
			  << endl;
		_disableOverlay = true;
		return;
	}

	rc = ioctl(_fd, VIDIOCGPICT, &vp);

	if (rc >= 0) {
		vp.palette = palette;
		vp.depth = QPaintDevice::x11AppDepth();
		rc = ioctl(_fd, VIDIOCSPICT, &vp);
		if (rc < 0) {
			_disableOverlay = true;
		}
	}
}


V4LTuner::~V4LTuner() {
}


bool V4LTuner::canOverlay() const {
	if (_disableOverlay)
		return false;
	return V4LDev::canOverlay();
}


int V4LTuner::setFreq(unsigned long freq) {
if (!_isTuner)
	return -1;
int rc = ioctl(_fd, VIDIOCSFREQ, &freq);

    if (rc < 0)
        perror("VIDIOCSFREQ");

return rc;
}

unsigned long V4LTuner::freq() const {
unsigned long freq;
int rc = ioctl(_fd, VIDIOCSFREQ, &freq);
if (rc == 0)
	return freq;
return 0;
}


int V4LTuner::setSource(const QString &source) {
    int rc = V4LDev::setSource(source);

    _isTuner = false;
    _hasAudio = false;

    if (rc < 0) {
	return rc;
    }
    
    kdDebug() << "Set source: " << source << endl;

    _tunerNum = -1;
    for (unsigned int i = 0; i < _sources.count(); i++) {
        if (_sources[i] == source) {
            _tunerNum = i;
            break;
        }
    }

    assert(_tunerNum != -1);

    if (_channels[_tunerNum].flags & VIDEO_VC_AUDIO)
	_hasAudio = true;

    if (_channels[_tunerNum].flags & VIDEO_VC_TUNER)
	_isTuner = true;
	
    kdDebug() << "Is this source a tuner? " << _isTuner << endl;
    kdDebug() << "Does this source have audio? " << _isTuner << endl;

    if (_isTuner) {
        setTunerMode(-1);
    } else {
	_minFreq = _maxFreq = 0;
    }
    
    return 0;
}


int V4LTuner::signal() const {
struct video_tuner vt;
int rc;

	rc = ioctl(_fd, VIDIOCGTUNER, &vt);
	if (rc < 0) {
		return -1;
	}

return vt.signal;
}



int V4LTuner::tunerMode() const {
int x;
	switch(_vt.mode) {
	case VIDEO_MODE_AUTO:
		x = 0;
	break;
	case VIDEO_MODE_NTSC:
		x = 1;
	break;
	case VIDEO_MODE_PAL:
		x = 2;
	break;
	case VIDEO_MODE_SECAM:
		x = 3;
	break;
	default:
		x = -1;
	break;
	}

return x;
}



int V4LTuner::setTunerMode(int x) {
    _vt.tuner = _tunerNum;
    ioctl(_fd, VIDIOCGTUNER, &_vt);
    int oldmode = _vt.mode;

    memset(&_vt, 0, sizeof(_vt));

    switch(x) {
    case 0:
        _vt.mode = VIDEO_MODE_AUTO;
        _aspectRatio = 4/3;
    break;
    case 1:
        _vt.mode = VIDEO_MODE_NTSC;
        _aspectRatio = 4/3;
    break;
    case 2:
        _vt.mode = VIDEO_MODE_PAL;
        _aspectRatio = 4/3;
    break;
    case 3:
        _vt.mode = VIDEO_MODE_SECAM;
        _aspectRatio = 4/3;
    break;
    case -1:
        /* keep current one */
        _vt.mode = oldmode;
    break;
    default:
        return -1;
    break;
    }

    _vt.tuner = _tunerNum;
    int rc = ioctl(_fd, VIDIOCSTUNER, &_vt);
    if (rc < 0) {
        perror("VIDIOCSTUNER");
    }

    _vt.tuner = _tunerNum;
    memset(&_vt, 0, sizeof(_vt));
    rc = ioctl(_fd, VIDIOCGTUNER, &_vt);
    if (rc >= 0) {   // update these incase they change
        _minFreq = _vt.rangelow;
        _maxFreq = _vt.rangehigh;
        if (_maxFreq > 0x7fff) {
            _minFreq = 0x8e;
            _maxFreq = 0x7fff;
        } 
        kdDebug() << "minfreq = " << _minFreq << " maxFreq= " << _maxFreq << endl;
    }

    struct video_capability vcap;
    rc = ioctl(_fd, VIDIOCGCAP, &vcap);
    if (rc == 0) {
        _maxWidth = vcap.maxwidth;
        _minWidth = vcap.minwidth;
        _maxHeight= vcap.maxheight;
        _minHeight= vcap.minheight;
        if (_vt.mode == VIDEO_MODE_NTSC && vcap.maxheight == 480)
            _maxWidth = 640;
        else if (_vt.mode == VIDEO_MODE_PAL && vcap.maxheight == 576)
            _maxWidth = 768;
        else if (_vt.mode == VIDEO_MODE_SECAM && vcap.maxheight == 576)
            _maxWidth = 768;
        setImageSize(_capW, _capH);
        kdDebug() << "Set tuner min/max to: " << _minWidth << "-" << _maxWidth
                  << " " << _minHeight << "-" << _maxHeight << endl;
    }


return _vt.mode;
}

bool V4LTuner::isTuner() const {
    return _isTuner;
}

int V4LTuner::setImageSize(int w, int h) {

return V4LDev::setImageSize(w, h);
}




