// -*- c++ -*-

/*
 *
 * Copyright (C) 2002 Richard Moore <rich@kde.org>
 * 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 <klocale.h>
#include <qfile.h>
#include <qstringlist.h>
#include <qtextstream.h>

#include <ktempfile.h>
#include <kdebug.h>
#include <ksimpleconfig.h>

#include "channel.h"
#include "channelstore.h"

#include "channeliokwintv2.h"

//
// KWinTV2 Format Handler
//

ChannelIOFormatKWinTV2::ChannelIOFormatKWinTV2(Kdetv *ktv, QObject *parent, const char* name)
    : KdetvChannelPlugin(ktv, "KWinTV Channels", parent, name)
{
    _fmtName  = "ch";
    _menuName = i18n("KWinTV2");
    _flags    = FormatRead|FormatWrite;
}


bool ChannelIOFormatKWinTV2::readKConfigFormat(ChannelStore *store, QIODevice *file)
{
	if (!file->reset())
		return false;

	KTempFile tf;

	tf.setAutoDelete(false);

	// Copy into a temp file
	QString tmpnam = tf.name();
	ts = new QTextStream( file );
	QTextStream *ots = tf.textStream();
	
	while (!ts->atEnd()) {
		QString l = ts->readLine();
		(*ots) << l << endl;
	}
	
	tf.close();

	// Try to read it as a KConfig file
	KSimpleConfig *cfg = new KSimpleConfig(tmpnam, true);
	QStringList groups = cfg->groupList();
	for (QStringList::ConstIterator i = groups.constBegin();
         i != groups.constEnd();
         ++i) {
		cfg->setGroup(*i);
		Channel *chan = new Channel( store );
		chan->setChannelProperty("frequency", (Q_ULLONG)cfg->readNumEntry("Frequency")*1000/16);
		chan->setNumber(cfg->readNumEntry("ChannelId"));
		chan->setName(cfg->readEntry("ChannelName"));
		chan->setEnabled(cfg->readBoolEntry("Enabled", true));

		/*
		 * Dirk Ziegelmeier: this is a hack. To do it correctly,
		 * I would need the _encodings enumeration for the currently
		 * selected source. But it would be ugly to give the channel
		 * load/save function a pointer to kdetv from where we
		 * could get to the current video plugin...
		 * No. That's not worth it. I hacked TV norm to work,
		 * source has to stand back.
		 */
		int norm = cfg->readNumEntry("Enabled", 0);
		if (norm == 0) {
            chan->setChannelProperty("encoding", QString("pal"));
		} else if (norm == 1) {
            chan->setChannelProperty("encoding", QString("ntsc"));
		} else if (norm == 2) {
            chan->setChannelProperty("encoding", QString("secam"));
		} else if (norm == 3) {
            chan->setChannelProperty("encoding", QString("auto"));
		}      

		store->addChannel(chan);
		kdDebug() << "IOFormatKWinTV2: found channel "
                  << chan->name() << " " << chan->number() 
			  << " " << (unsigned int)chan->getChannelProperty("frequency").toULongLong() << endl;
	}

	delete cfg;

	// FIXME - delete the tmp file
	QFile::remove(tmpnam);

    return groups.count() > 0;
}


bool ChannelIOFormatKWinTV2::load( ChannelStore *store, ChannelFileMetaInfo *,
                                   QIODevice *file, const QString& /*fmt*/ )
{
    kdDebug() << "IOFormatKWinTV2::load(...)" << endl;

    chan = 0;
    ts = new QTextStream( file );

    if ( !readHeader() ) {
        kdDebug() << "IOFormatKWinTV2::load() trying KConfig format instead" << endl;
        delete ts;
        return readKConfigFormat(store, file);
    }

    this->store = store;
    QString line = ts->readLine();
    while( !line.isNull() ) {
        if ( line == "*" ) {
            chan = readChannel();
            if ( chan )
                store->addChannel( chan );
            else
                break;
        }
        line = ts->readLine();
    }

    delete ts;
    file->close();

    return chan ? true : false;
}

bool ChannelIOFormatKWinTV2::readHeader()
{
    QString line = ts->readLine();
    if ( line != "### 4" ) {
        kdWarning() << "IOFormatKWinTV2::readHeader() Expected '### 4' but found '" 
                    << line << "'" << endl;
        return false;
    }

    for ( int i = 0; i < 2; i++ ) {
        line = ts->readLine();
        if ( !line.startsWith("#") ) {
            kdWarning() << "IOFormatKWinTV2::readHeader() Parse error, line was '" 
                        << line << "'" << endl;
            return false;
        }
    }

    return true;
}

Channel *ChannelIOFormatKWinTV2::readChannel()
{
    QStringList fields;

    for ( int i = 0; i < 10; i++ ) {
        QString line = ts->readLine();
        if ( line.isNull() )
            return 0;

        fields.append( line );
    }

    QString s = readField( *fields.at(1), "Frq" );
    if ( s.isNull() ) {
        kdWarning() << "IOFormatKWinTV2::readChannel() Could not find 'Frq' field" << endl;
        return 0;
    }

    bool ok;
    unsigned long freq = s.toULong( &ok );
    if ( !ok )
        return 0;

    QString n = readField( *fields.at(7), "Norm" );
    if ( s.isNull() ) {
        kdWarning() << "IOFormatKWinTV2::readChannel() Could not find 'Norm' field" << endl;
        return 0;
    }

    unsigned long norm = n.toULong( &ok );

    Channel *ch = new Channel( store );
    ch->setChannelProperty("frequency", (Q_ULLONG)(freq*1000/16));

    QString c = readField( *fields.at(0), "Chnl" );
    if ( !c.isNull() ) {
        ch->setName(c);
    }

    /* read my remarks at readKConfigFormat() */
    if (norm == 0) {
        ch->setChannelProperty("encoding", QString("pal"));
    } else if (norm == 1) {
        ch->setChannelProperty("encoding", QString("ntsc"));
    } else if (norm == 2) {
        ch->setChannelProperty("encoding", QString("secam"));
    } else if (norm == 3) {
        ch->setChannelProperty("encoding", QString("auto"));
    }      

    return ch;
}

QString ChannelIOFormatKWinTV2::readField( const QString &line, const QString &field )
{
    QStringList sl = QStringList::split( ": ", line );
    if ( sl[0] != field )
        return QString::null;
    else
        return sl[1];
}

bool ChannelIOFormatKWinTV2::save( ChannelStore *store, ChannelFileMetaInfo *,
                                   QIODevice *file, const QString&)
{
    kdDebug() << "IOFormatKWinTV2::save() called" << endl;
	KTempFile tf;

	QString tn = tf.name();
	tf.close();
	this->store = store;

	KSimpleConfig *cfg = new KSimpleConfig(tn, false);

	for (uint i = 0; i < store->count(); i++) {
		Channel *c = store->channelAt(i);
		cfg->setGroup(QString("channel %1").arg(c->number()));
		cfg->writeEntry("ChannelId", c->number());
		cfg->writeEntry("ChannelName", c->name());
		cfg->writeEntry("Frequency", c->getChannelProperty("frequency").toULongLong()*16/1000);
		cfg->writeEntry("Enabled", c->enabled());

		/* read my remarks at readKConfigFormat() */
		if (c->getChannelProperty("encoding").toString() == "pal") {
            cfg->writeEntry("Norm", 0);
		} else if (c->getChannelProperty("encoding").toString() == "ntsc") {
            cfg->writeEntry("Norm", 1);
		} else if (c->getChannelProperty("encoding").toString() == "secam") {
            cfg->writeEntry("Norm", 2);
		} else if (c->getChannelProperty("encoding").toString() == "auto") {
            cfg->writeEntry("Norm", 3);
		}
	}

	delete cfg;

	ts = new QTextStream( file );

	QFile qf(tn);
	if (!qf.open(IO_ReadOnly)) {
		QFile::remove(tn);
		return false;
	}

	QTextStream *itf = new QTextStream(&qf);
	while (!itf->atEnd()) {
		QString l = itf->readLine();
		(*ts) << l << endl;
	}

	delete itf;
	qf.close();

	QFile::remove(tn);

    return true;
}


extern "C" {
	KDETV_EXPORT ChannelIOFormatKWinTV2* create_kwintvchannels(Kdetv *ktv)
    {
		return new ChannelIOFormatKWinTV2(ktv, 0L, "KWinTV Channel Plugin");
	}
}

