/* 
 *
 * 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., 51 Franklin Steet, Fifth Floor,
 * Boston, MA 02110-1301, 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
#define _LINUX_TIME_H
#define _DEVICE_H_
#include "kdetvv4lsetup/videodev.h"

#include <kdebug.h>
#include <klocale.h>

#include "v4ldevtuner.h"

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

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)
{
	_isTuner = true;  // for now
	_tunerNum = -1;
    _vt = new struct video_tuner;

	_encodings.append("pal");
	_encodings.append("ntsc");
	_encodings.append("secam");

    /* Hack to find out if we have a bttv card */
    unsigned int i;
    int rc;
#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
    if (-1 != (rc = ioctl(fd, BTTV_VERSION, &i))) {
#undef BTTV_VERSION
        kDebug() << "V4LDevTuner: Found bttv based tuner card. Adding four more TV norms. (BTTV version: "
                  << ((rc >> 16) & 0xff) << "."
                  << ((rc >>  8) & 0xff) << "."
                  << ( rc        & 0xff) << ")" << endl;
        
        _encodings.append("pal-nc");
        _encodings.append("pal-m");
        _encodings.append("pal-n");
        _encodings.append("ntsc-jp");
    }

    _encodings.append("auto");
    _encoding = "auto";

    _audioMap[i18n("Mono")]       = VIDEO_SOUND_MONO;
    _audioMap[i18n("Stereo")]     = VIDEO_SOUND_STEREO;
    _audioMap[i18n("Language 1")] = VIDEO_SOUND_LANG1;
    _audioMap[i18n("Language 2")] = VIDEO_SOUND_LANG2;

    _audioModes += _audioMap.keys();
}


V4LTuner::~V4LTuner()
{
    delete[] _vt;
}

int V4LTuner::setFreq(unsigned long freq)
{
    syncCurrentFrame();

    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);
    
    if (rc < 0) {
        return rc;
    }
    
    _isTuner = false;
    
    kDebug() << "Set source: " << source;
    
    _tunerNum = -1;
    for (int i = 0; i < _sources.count(); i++) {
        if (_sources[i] == source) {
            _tunerNum = i;
            break;
        }
    }

    if(_tunerNum == -1) {
        return -1; // unknown source
    }
    
    if (_channels[_tunerNum].flags & VIDEO_VC_TUNER)
        _isTuner = true;
	
    kDebug() << "Is this source a tuner? " << _isTuner;
    kDebug() << "Does this source have audio (may be incorrect for v4l2 devices)? "
              << (bool)(_channels[_tunerNum].flags & VIDEO_VC_AUDIO) << endl;

    if (_isTuner) {
        setEncoding(QString::null);
    } else {
        _minFreq = _maxFreq = 0;
    }
    
    return 0;
}


int V4LTuner::signal() const
{
    struct video_tuner vt;
    int rc;
    
    memset(&vt, 0, sizeof(vt));
	rc = ioctl(_fd, VIDIOCGTUNER, &vt);
	if (rc < 0) {
		return -1;
	}

    return vt.signal;
}


int V4LTuner::setEncoding(const QString &encoding)
{
    kDebug() << "v4l: setEncoding: " << encoding;

    syncCurrentFrame();
    
    memset(_vt, 0, sizeof(_vt));
    if (encoding == "auto") {
        _vt->mode = VIDEO_MODE_AUTO;
        _aspectRatio = 4/3;
    } else if (encoding == "ntsc") {
        _vt->mode = VIDEO_MODE_NTSC;
        _aspectRatio = 4/3;
    } else if (encoding == "pal") {
        _vt->mode = VIDEO_MODE_PAL;
        _aspectRatio = 4/3;
    } else if (encoding == "secam") {
        _vt->mode = VIDEO_MODE_SECAM;
        _aspectRatio = 4/3;
    } else if (encoding == "pal-nc") {
        _vt->mode = 3;
        _aspectRatio = 4/3;
    } else if (encoding == "pal-m") {
        _vt->mode = 4;
        _aspectRatio = 4/3;
    } else if (encoding == "pal-n") {
        _vt->mode = 5;
        _aspectRatio = 4/3;
    } else if (encoding == "ntsc-jp") {
        _vt->mode = 6;
        _aspectRatio = 4/3;
    } else if (encoding.isEmpty()) {
        // "" means don't touch
        return 0;
    } else {
        return -1;
    }

    _encoding = encoding;

    _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 in case they change
        _minFreq = _vt->rangelow;
        _maxFreq = _vt->rangehigh;
        if (_maxFreq > 0x7fff) {
            _minFreq = 0x8e;
            _maxFreq = 0x7fff;
        } 
        kDebug() << "minfreq = " << _minFreq << " maxFreq= " << _maxFreq;
    }
    
    struct video_capability vcap;
    memset(&vcap, 0, sizeof(vcap));
    rc = ioctl(_fd, VIDIOCGCAP, &vcap);
    if (rc == 0) {
        _maxWidth = vcap.maxwidth;
        _minWidth = vcap.minwidth;
        _maxHeight= vcap.maxheight;
        _minHeight= vcap.minheight;

        // This shouldn't be necessary (I guess it was there to work around buggy drivers)
        // Can lead to problems with saa7134 (max width 720)
        /*
        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(_grabW, _grabH);
        kDebug() << "Set tuner min/max to: " << _minWidth << "-" << _maxWidth
                  << " " << _minHeight << "-" << _maxHeight << endl;
    }
    
    return _vt->mode;
}
