/* This file is part of the KMPlayer application
   Copyright (C) 2004 Koos Vriezen <koos.vriezen@xs4all.nl>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This program 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
    General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include <math.h>
#include <unistd.h>

#include <tqlayout.h>
#include <tqlabel.h>
#include <tqmap.h>
#include <tqtimer.h>
#include <tqpushbutton.h>
#include <tqbuttongroup.h>
#include <tqcheckbox.h>
#include <tqtable.h>
#include <tqstringlist.h>
#include <tqcombobox.h>
#include <tqlineedit.h>
#include <tqgroupbox.h>
#include <tqwhatsthis.h>
#include <tqtabwidget.h>
#include <tqradiobutton.h>
#include <tqmessagebox.h>
#include <tqpopupmenu.h>
#include <tqsocket.h>
#include <tqeventloop.h>

#include <tdelocale.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <klineedit.h>
#include <kurlrequester.h>
#include <kcombobox.h>
#include <kstatusbar.h>
#include <kprocess.h>
#include <tdeconfig.h>
#include <tdeaction.h>
#include <kiconloader.h>
#include <tdelistview.h>
#include <tdeversion.h>
#include <kinputdialog.h>

#include "kmplayer_backend_stub.h"
#include "kmplayer_callback.h"
#include "kmplayerpartbase.h"
#include "kmplayercontrolpanel.h"
#include "kmplayerconfig.h"
#include "playlistview.h"
#include "viewarea.h"
#include "kmplayervdr.h"
#include "kmplayer.h"

using namespace KMPlayer;

static const char * strVDR = "VDR";
static const char * strVDRPort = "Port";
static const char * strXVPort = "XV Port";
static const char * strXVEncoding = "XV Encoding";
static const char * strXVScale = "XV Scale";

KDE_NO_CDTOR_EXPORT KMPlayerPrefSourcePageVDR::KMPlayerPrefSourcePageVDR (TQWidget * parent, KMPlayer::PartBase * player)
 : TQFrame (parent), m_player (player) {
    //KURLRequester * v4ldevice;
    TQVBoxLayout *layout = new TQVBoxLayout (this, 5, 2);
    TQGridLayout *gridlayout = new TQGridLayout (1, 2);
    xv_port = new TDEListView (this);
    xv_port->addColumn (TQString());
    xv_port->header()->hide ();
    xv_port->setTreeStepSize (15);
    //xv_port->setRootIsDecorated (true);
    //xv_port->setSorting (-1);
    TQListViewItem * vitem = new TQListViewItem (xv_port, i18n ("XVideo port"));
    vitem->setOpen (true);
    TQWhatsThis::add (xv_port, i18n ("Port base of the X Video extension.\nIf left to default (0), the first available port will be used. However if you have multiple XVideo instances, you might have to provide the port to use here.\nSee the output from 'xvinfo' for more information"));
    TQLabel * label = new TQLabel (i18n ("Communication port:"), this);
    gridlayout->addWidget (label, 0, 0);
    tcp_port = new TQLineEdit ("", this);
    TQWhatsThis::add (tcp_port, i18n ("Communication port with VDR. Default is port 2001.\nIf you use another port, with the '-p' option of 'vdr', you must set it here too."));
    gridlayout->addWidget (tcp_port, 0, 1);
    layout->addWidget (xv_port);
    layout->addLayout (gridlayout);
    scale = new TQButtonGroup (2, TQt::Vertical, i18n ("Scale"), this);
    new TQRadioButton (i18n ("4:3"), scale);
    new TQRadioButton (i18n ("16:9"), scale);
    TQWhatsThis::add (scale, i18n ("Aspects to use when viewing VDR"));
    scale->setButton (0);
    layout->addWidget (scale);
    layout->addItem (new TQSpacerItem (5, 0, TQSizePolicy::Minimum, TQSizePolicy::Expanding));
}

KDE_NO_CDTOR_EXPORT KMPlayerPrefSourcePageVDR::~KMPlayerPrefSourcePageVDR () {}

TDE_NO_EXPORT void KMPlayerPrefSourcePageVDR::showEvent (TQShowEvent *) {
    XVideo * xvideo = static_cast<XVideo *>(m_player->players()["xvideo"]);
    if (!xvideo->configDocument ())
        xvideo->getConfigData ();
}
//-----------------------------------------------------------------------------

static const char * cmd_chan_query = "CHAN\n";
static const char * cmd_list_channels = "LSTC\n";
static const char * cmd_volume_query = "VOLU\n";

class VDRCommand {
public:
    KDE_NO_CDTOR_EXPORT VDRCommand (const char * c, VDRCommand * n=0L)
        : command (strdup (c)), next (n) {}
    KDE_NO_CDTOR_EXPORT ~VDRCommand () { free (command); }
    char * command;
    VDRCommand * next;
};

//-----------------------------------------------------------------------------

KDE_NO_CDTOR_EXPORT KMPlayerVDRSource::KMPlayerVDRSource (KMPlayerApp * app)
 : KMPlayer::Source (TQString ("VDR"), app->player (), "vdrsource"),
   m_app (app),
   m_configpage (0),
   m_socket (new TQSocket (this)), 
   commands (0L),
   channel_timer (0),
   timeout_timer (0),
   finish_timer (0),
   tcp_port (0),
   m_stored_volume (0) {
    memset (m_actions, 0, sizeof (TDEAction *) * int (act_last));
    m_player->settings ()->addPage (this);
    connect (m_socket, TQ_SIGNAL (connectionClosed()), this, TQ_SLOT(disconnected()));
    connect (m_socket, TQ_SIGNAL (connected ()), this, TQ_SLOT (connected ()));
    connect (m_socket, TQ_SIGNAL (readyRead ()), this, TQ_SLOT (readyRead ()));
    connect (m_socket, TQ_SIGNAL (error (int)), this, TQ_SLOT (socketError (int)));
}

KDE_NO_CDTOR_EXPORT KMPlayerVDRSource::~KMPlayerVDRSource () {}

KDE_NO_CDTOR_EXPORT void KMPlayerVDRSource::waitForConnectionClose () {
    if (timeout_timer) {
        finish_timer = startTimer (500);
        kdDebug () << "VDR connection not yet closed" << endl;
        TQApplication::eventLoop ()->enterLoop ();
        kdDebug () << "VDR connection:" << (m_socket->state () == TQSocket::Connected) << endl;
        timeout_timer = 0;
    }
}

TDE_NO_EXPORT bool KMPlayerVDRSource::hasLength () {
    return false;
}

TDE_NO_EXPORT bool KMPlayerVDRSource::isSeekable () {
    return true;
}

TDE_NO_EXPORT TQString KMPlayerVDRSource::prettyName () {
    return i18n ("VDR");
}

TDE_NO_EXPORT void KMPlayerVDRSource::activate () {
    last_channel = 0;
    connect (this, TQ_SIGNAL (startPlaying ()), this, TQ_SLOT (processStarted()));
    connect (this, TQ_SIGNAL (stopPlaying ()), this, TQ_SLOT (processStopped ()));
    KMPlayer::ControlPanel * panel = m_app->view()->controlPanel ();
    panel->button (KMPlayer::ControlPanel::button_red)->show ();
    panel->button (KMPlayer::ControlPanel::button_green)->show ();
    panel->button (KMPlayer::ControlPanel::button_yellow)->show ();
    panel->button (KMPlayer::ControlPanel::button_blue)->show ();
    panel->button (KMPlayer::ControlPanel::button_pause)->hide ();
    panel->button (KMPlayer::ControlPanel::button_record)->hide ();
    connect (panel->volumeBar (), TQ_SIGNAL (volumeChanged (int)), this, TQ_SLOT (volumeChanged (int)));
    connect (panel->button (KMPlayer::ControlPanel::button_red), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyRed ()));
    connect (panel->button (KMPlayer::ControlPanel::button_green), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyGreen ()));
    connect (panel->button (KMPlayer::ControlPanel::button_yellow), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyYellow ()));
    connect (panel->button (KMPlayer::ControlPanel::button_blue), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyBlue ()));
    setAspect (m_document, scale ? 16.0/9 : 1.33);
    if (!m_url.protocol ().compare ("kmplayer"))
        m_request_jump = KURL::decode_string (m_url.path ()).mid (1);
    setURL (KURL (TQString ("vdr://localhost:%1").arg (tcp_port)));
    TQTimer::singleShot (0, m_player, TQ_SLOT (play ()));
}

TDE_NO_EXPORT void KMPlayerVDRSource::deactivate () {
    disconnect (m_socket, TQ_SIGNAL (error (int)), this, TQ_SLOT (socketError (int)));
    if (m_player->view ()) {
        disconnect (this, TQ_SIGNAL(startPlaying()), this, TQ_SLOT(processStarted()));
        disconnect (this, TQ_SIGNAL (stopPlaying()), this, TQ_SLOT(processStopped()));
        KMPlayer::ControlPanel * panel = m_app->view()->controlPanel ();
        disconnect (panel->volumeBar (), TQ_SIGNAL (volumeChanged (int)), this, TQ_SLOT (volumeChanged (int)));
        disconnect (panel->button (KMPlayer::ControlPanel::button_red), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyRed ()));
        disconnect (panel->button (KMPlayer::ControlPanel::button_green), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyGreen ()));
        disconnect (panel->button (KMPlayer::ControlPanel::button_yellow), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyYellow ()));
        disconnect (panel->button (KMPlayer::ControlPanel::button_blue), TQ_SIGNAL (clicked ()), this, TQ_SLOT (keyBlue ()));
    }
    processStopped ();
    m_request_jump.truncate (0);
}

TDE_NO_EXPORT void KMPlayerVDRSource::playCurrent () {
    if (m_player->process ())
        m_player->process ()->play (this, current ()); // FIXME HACK
}

TDE_NO_EXPORT void KMPlayerVDRSource::processStopped () {
    if (m_socket->state () == TQSocket::Connected) {
        queueCommand (TQString ("VOLU %1\n").arg (m_stored_volume).ascii ());
        queueCommand ("QUIT\n");
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::processStarted () {
    m_socket->connectToHost ("127.0.0.1", tcp_port);
    commands = new VDRCommand ("connect", commands);
}

#define DEF_ACT(i,text,pix,scut,slot,name) \
    m_actions [i] = new TDEAction (text, TQString (pix), TDEShortcut (scut), this, slot, m_app->actionCollection (), name); \
    m_fullscreen_actions [i] = new TDEAction (text, TDEShortcut (scut), this, slot, m_app->view ()->viewArea ()->actionCollection (), name)

TDE_NO_EXPORT void KMPlayerVDRSource::connected () {
    queueCommand (cmd_list_channels);
    queueCommand (cmd_volume_query);
    killTimer (channel_timer);
    channel_timer = startTimer (3000);
    TDEAction * action = m_app->actionCollection ()->action ("vdr_connect");
    action->setIcon (TQString ("connect_no"));
    action->setText (i18n ("Dis&connect"));
    DEF_ACT (act_up, i18n ("VDR Key Up"), "go-up", , TQ_SLOT (keyUp ()), "vdr_key_up");
    DEF_ACT (act_down, i18n ("VDR Key Down"), "go-down", , TQ_SLOT (keyDown ()), "vdr_key_down");
    DEF_ACT (act_back, i18n ("VDR Key Back"), "back", , TQ_SLOT (keyBack ()), "vdr_key_back");
    DEF_ACT (act_ok, i18n ("VDR Key Ok"), "ok", , TQ_SLOT (keyOk ()), "vdr_key_ok");
    DEF_ACT (act_setup, i18n ("VDR Key Setup"), "configure", , TQ_SLOT (keySetup ()), "vdr_key_setup");
    DEF_ACT (act_channels, i18n ("VDR Key Channels"), "player_playlist", , TQ_SLOT (keyChannels ()), "vdr_key_channels");
    DEF_ACT (act_menu, i18n ("VDR Key Menu"), "showmenu", , TQ_SLOT (keyMenu ()), "vdr_key_menu");
    DEF_ACT (act_red, i18n ("VDR Key Red"), "red", , TQ_SLOT (keyRed ()), "vdr_key_red");
    DEF_ACT (act_green, i18n ("VDR Key Green"), "green", , TQ_SLOT (keyGreen ()), "vdr_key_green");
    DEF_ACT (act_yellow, i18n ("VDR Key Yellow"), "yellow", , TQ_SLOT (keyYellow ()), "vdr_key_yellow");
    DEF_ACT (act_blue, i18n ("VDR Key Blue"), "blue", , TQ_SLOT (keyBlue ()), "vdr_key_blue");
    DEF_ACT (act_custom, "VDR Custom Command", "application-x-executable", , TQ_SLOT (customCmd ()), "vdr_key_custom");
    m_app->initMenu (); // update menu and toolbar
    DEF_ACT (act_0, i18n ("VDR Key 0"), "0", TQt::Key_0, TQ_SLOT (key0 ()), "vdr_key_0");
    DEF_ACT (act_1, i18n ("VDR Key 1"), "1", TQt::Key_1, TQ_SLOT (key1 ()), "vdr_key_1");
    DEF_ACT (act_2, i18n ("VDR Key 2"), "2", TQt::Key_2, TQ_SLOT (key2 ()), "vdr_key_2");
    DEF_ACT (act_3, i18n ("VDR Key 3"), "3", TQt::Key_3, TQ_SLOT (key3 ()), "vdr_key_3");
    DEF_ACT (act_4, i18n ("VDR Key 4"), "4", TQt::Key_4, TQ_SLOT (key4 ()), "vdr_key_4");
    DEF_ACT (act_5, i18n ("VDR Key 5"), "5", TQt::Key_5, TQ_SLOT (key5 ()), "vdr_key_5");
    DEF_ACT (act_6, i18n ("VDR Key 6"), "6", TQt::Key_6, TQ_SLOT (key6 ()), "vdr_key_6");
    DEF_ACT (act_7, i18n ("VDR Key 7"), "7", TQt::Key_7, TQ_SLOT (key7 ()), "vdr_key_7");
    DEF_ACT (act_8, i18n ("VDR Key 8"), "8", TQt::Key_8, TQ_SLOT (key8 ()), "vdr_key_8");
    DEF_ACT (act_9, i18n ("VDR Key 9"), "9", TQt::Key_9, TQ_SLOT (key9 ()), "vdr_key_9");
    //KMPlayer::ViewLayer * layer = m_app->view ()->viewArea ();
    for (int i = 0; i < int (act_last); ++i)
        // somehow, the configured shortcuts only show up after createGUI() call
        m_fullscreen_actions [i]->setShortcut (m_actions [i]->shortcut ());
    //    m_fullscreen_actions[i]->plug (layer);
}

#undef DEF_ACT

TDE_NO_EXPORT void KMPlayerVDRSource::disconnected () {
    kdDebug() << "disconnected " << commands << endl;
    if (finish_timer) {
        deleteCommands ();
        return;
    }
    setURL (KURL (TQString ("vdr://localhost:%1").arg (tcp_port)));
    if (channel_timer && m_player->source () == this)
        m_player->process ()->quit ();
    deleteCommands ();
    TDEAction * action = m_app->actionCollection ()->action ("vdr_connect");
    action->setIcon (TQString ("connect_established"));
    action->setText (i18n ("&Connect"));
    m_app->guiFactory ()->removeClient (m_app);// crash w/ m_actions[i]->unplugAll (); in for loop below
    for (int i = 0; i < int (act_last); ++i)
        if (m_player->view () && m_actions[i]) {
            m_fullscreen_actions[i]->unplug (m_app->view()->viewArea());
            delete m_actions[i];
            delete m_fullscreen_actions[i];
        }
    m_app->initMenu ();
}

TDE_NO_EXPORT void KMPlayerVDRSource::toggleConnected () {
    if (m_socket->state () == TQSocket::Connected) {
        queueCommand ("QUIT\n");
        killTimer (channel_timer);
        channel_timer = 0;
    } else {
        m_socket->connectToHost ("127.0.0.1", tcp_port);
        commands = new VDRCommand ("connect", commands);
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::volumeChanged (int val) {
    queueCommand (TQString ("VOLU %1\n").arg (int (sqrt (255 * 255 * val / 100))).ascii ());
}

static struct ReadBuf {
    char * buf;
    int length;
    KDE_NO_CDTOR_EXPORT ReadBuf () : buf (0L), length (0) {}
    KDE_NO_CDTOR_EXPORT ~ReadBuf () {
        clear ();
    }
    TDE_NO_EXPORT void operator += (const char * s) {
        int l = strlen (s);
        char * b = new char [length + l + 1];
        if (length)
            strcpy (b, buf);
        strcpy (b + length, s);
        length += l;
        delete buf;
        buf = b;
    }
    TDE_NO_EXPORT TQCString mid (int p) {
        return TQCString (buf + p);
    }
    TDE_NO_EXPORT TQCString left (int p) {
        return TQCString (buf, p);
    }
    TDE_NO_EXPORT TQCString getReadLine ();
    TDE_NO_EXPORT void clear () {
        delete [] buf;
        buf = 0;
        length = 0;
    }
} readbuf;

TDE_NO_EXPORT TQCString ReadBuf::getReadLine () {
    TQCString out;
    if (!length)
        return out;
    int p = strcspn (buf, "\r\n");
    if (p < length) {
        int skip = strspn (buf + p, "\r\n");
        out = left (p+1);
        int nl = length - p - skip;
        memmove (buf, buf + p + skip, nl + 1);
        length = nl;
    }
    return out;
}

TDE_NO_EXPORT void KMPlayerVDRSource::readyRead () {
    KMPlayer::View * v = finish_timer ? 0L : static_cast <KMPlayer::View *> (m_player->view ());
    long nr = m_socket->bytesAvailable();
    char * data = new char [nr + 1];
    m_socket->readBlock (data, nr);
    data [nr] = 0;
    readbuf += data;
    TQCString line = readbuf.getReadLine ();
    if (commands) {
        bool cmd_done = false;
        while (!line.isEmpty ()) {
            bool toconsole = true;
            cmd_done = (line.length () > 3 && line[3] == ' '); // from svdrpsend.pl
            // kdDebug () << "readyRead " << cmd_done << " " << commands->command << endl;
            if (!strcmp (commands->command, cmd_list_channels) && m_document) {
                int p = line.find (';');
                int q = line.find (':');
                if (q > 0 && (p < 0 || q < p))
                    p = q;
                if (p > 0)
                    line.truncate (p);
                TQString channel_name = line.mid (4);
                m_document->appendChild (new KMPlayer::GenericMrl (m_document, TQString ("kmplayer://vdrsource/%1").arg(channel_name), channel_name));
                if (cmd_done) {
                    m_player->updateTree ();
                    if (!m_request_jump.isEmpty ()) {
                        jump (m_request_jump);
                        m_request_jump.truncate (0);
                    }
                }
                toconsole = false;
            } else if (!strcmp (commands->command, cmd_chan_query)) {
                if (v && line.length () > 4) {
                    TQString ch = line.mid (4);
                    setTitle (ch);
                    KMPlayer::PlayListItem * lvi = static_cast <KMPlayer::PlayListItem *> (v->playList ()->findItem (ch, 0));
                    if (lvi && lvi->node != m_last_channel) {
                        KMPlayer::PlayListItem * si = static_cast <KMPlayer::PlayListItem *> (v->playList ()->selectedItem ());
                        bool jump_selection = (si && (si->node == m_document || si->node == m_last_channel));
                        if (m_last_channel)
                            m_last_channel->setState (KMPlayer::Node::state_finished);
                        m_last_channel = lvi->node;
                        if (m_last_channel)
                            m_last_channel->setState (KMPlayer::Node::state_began);
                        if (jump_selection) {
                            v->playList ()->setSelected (lvi, true);
                            v->playList ()->ensureItemVisible (lvi);
                        }
                        v->playList ()->triggerUpdate ();
                    }
                    //v->playList ()->selectItem (ch);
                    int c = strtol(ch.ascii(), 0L, 10);
                    if (c != last_channel) {
                        last_channel = c;
                        m_app->statusBar ()->changeItem (TQString::number (c),
                                                         id_status_timer);
                    }
                }
            } else if (cmd_done && !strcmp(commands->command,cmd_volume_query)){
                int pos = line.findRev (TQChar (' '));
                if (pos > 0) {
                    TQString vol = line.mid (pos + 1);
                    if (!vol.compare ("mute"))
                        m_stored_volume = 0;
                    else
                        m_stored_volume = vol.toInt ();
                    if (m_stored_volume == 0)
                        volumeChanged (m_app->view ()->controlPanel ()->volumeBar ()->value ());
                }
            }
            if (v && toconsole)
                v->addText (TQString (line), true);
            line = readbuf.getReadLine ();
        }
        if (cmd_done) {
            VDRCommand * c = commands->next;
            delete commands;
            commands = c;
            if (commands)
                sendCommand ();
            else {
                killTimer (timeout_timer);
                timeout_timer = 0;
            }
        }
    }
    delete [] data;
}

TDE_NO_EXPORT void KMPlayerVDRSource::socketError (int code) {
    if (code == TQSocket::ErrHostNotFound) {
        KMessageBox::error (m_configpage, i18n ("Host not found"), i18n ("Error"));
    } else if (code == TQSocket::ErrConnectionRefused) {
        KMessageBox::error (m_configpage, i18n ("Connection refused"), i18n ("Error"));
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::queueCommand (const char * cmd) {
    if (m_player->source () != this)
        return;
    if (!commands) {
        readbuf.clear ();
        commands = new VDRCommand (cmd);
        if (m_socket->state () == TQSocket::Connected) {
            sendCommand ();
        } else {
            m_socket->connectToHost ("127.0.0.1", tcp_port);
            commands = new VDRCommand ("connect", commands);
        }
    } else {
        VDRCommand * c = commands;
        for (int i = 0; i < 10; ++i, c = c->next)
            if (!c->next) {
                c->next = new VDRCommand (cmd);
                break;
            }
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::queueCommand (const char * cmd, int t) {
    queueCommand (cmd);
    killTimer (channel_timer);
    channel_timer = startTimer (t);
}

TDE_NO_EXPORT void KMPlayerVDRSource::sendCommand () {
    //kdDebug () << "sendCommand " << commands->command << endl;
    m_socket->writeBlock (commands->command, strlen(commands->command));
    m_socket->flush ();
    killTimer (timeout_timer);
    timeout_timer = startTimer (30000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::customCmd () {
    TQString cmd = KInputDialog::getText (i18n ("Custom VDR command"), i18n ("You can pass commands to VDR.\nEnter 'HELP' to see a list of available commands.\nYou can see VDR response in the console window.\n\nVDR Command:"), TQString(), 0, m_player->view ());
    if (!cmd.isEmpty ())
        queueCommand (TQString (cmd + TQChar ('\n')).local8Bit ());
}

TDE_NO_EXPORT void KMPlayerVDRSource::timerEvent (TQTimerEvent * e) {
    if (e->timerId () == timeout_timer || e->timerId () == finish_timer) {
        deleteCommands ();
    } else if (e->timerId () == channel_timer) {
        queueCommand (cmd_chan_query);
        killTimer (channel_timer);
        channel_timer = startTimer (30000);
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::deleteCommands () {
    killTimer (timeout_timer);
    timeout_timer = 0;
    killTimer (channel_timer);
    channel_timer = 0;
    for (VDRCommand * c = commands; c; c = commands) {
        commands = commands->next;
        delete c;
    }
    readbuf.clear ();
    if (finish_timer) {
        killTimer (finish_timer);
        TQApplication::eventLoop ()->exitLoop ();
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::jump (KMPlayer::NodePtr e) {
    if (!e->isPlayable ()) return;
    m_current = e;
    jump (e->mrl ()->pretty_name);
}

TDE_NO_EXPORT void KMPlayerVDRSource::jump (const TQString & channel) {
    TQCString c ("CHAN ");
    TQCString ch = channel.local8Bit ();
    int p = ch.find (' ');
    if (p > 0)
        c += ch.left (p);
    else
        c += ch; // hope for the best ..
    c += '\n';
    queueCommand (c);
}

TDE_NO_EXPORT void KMPlayerVDRSource::forward () {
    queueCommand ("CHAN +\n", 1000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::backward () {
    queueCommand ("CHAN -\n", 1000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyUp () {
    queueCommand ("HITK UP\n", 1000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyDown () {
    queueCommand ("HITK DOWN\n", 1000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyBack () {
    queueCommand ("HITK BACK\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyOk () {
    queueCommand ("HITK OK\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keySetup () {
    queueCommand ("HITK SETUP\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyChannels () {
    queueCommand ("HITK CHANNELS\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyMenu () {
    queueCommand ("HITK MENU\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::key0 () {
    queueCommand ("HITK 0\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key1 () {
    queueCommand ("HITK 1\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key2 () {
    queueCommand ("HITK 2\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key3 () {
    queueCommand ("HITK 3\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key4 () {
    queueCommand ("HITK 4\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key5 () {
    queueCommand ("HITK 5\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key6 () {
    queueCommand ("HITK 6\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key7 () {
    queueCommand ("HITK 7\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key8 () {
    queueCommand ("HITK 8\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::key9 () {
    queueCommand ("HITK 9\n", 2000);
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyRed () {
    queueCommand ("HITK RED\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyGreen () {
    queueCommand ("HITK GREEN\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyYellow () {
    queueCommand ("HITK YELLOW\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::keyBlue () {
    queueCommand ("HITK BLUE\n");
}

TDE_NO_EXPORT void KMPlayerVDRSource::write (TDEConfig * m_config) {
    m_config->setGroup (strVDR);
    m_config->writeEntry (strVDRPort, tcp_port);
    m_config->writeEntry (strXVPort, m_xvport);
    m_config->writeEntry (strXVEncoding, m_xvencoding);
    m_config->writeEntry (strXVScale, scale);
}

TDE_NO_EXPORT void KMPlayerVDRSource::read (TDEConfig * m_config) {
    m_config->setGroup (strVDR);
    tcp_port = m_config->readNumEntry (strVDRPort, 2001);
    m_xvport = m_config->readNumEntry (strXVPort, 0);
    m_xvencoding = m_config->readNumEntry (strXVEncoding, 0);
    scale = m_config->readNumEntry (strXVScale, 0);
}

struct XVTreeItem : public TQListViewItem {
    XVTreeItem (TQListViewItem *parent, const TQString & t, int p, int e)
        : TQListViewItem (parent, t), port (p), encoding (e) {}
    int port;
    int encoding;
};

TDE_NO_EXPORT void KMPlayerVDRSource::sync (bool fromUI) {
    XVideo * xvideo = static_cast<XVideo *>(m_player->players()["xvideo"]);
    if (fromUI) {
        tcp_port = m_configpage->tcp_port->text ().toInt ();
        scale = m_configpage->scale->id (m_configpage->scale->selected ());
        setAspect (m_document, scale ? 16.0/9 : 1.25);
        XVTreeItem * vitem = dynamic_cast <XVTreeItem *> (m_configpage->xv_port->selectedItem ());
        if (vitem) {
            m_xvport = vitem->port;
            m_xvencoding = vitem->encoding;
        }
    } else {
        m_configpage->tcp_port->setText (TQString::number (tcp_port));
        m_configpage->scale->setButton (scale);
        TQListViewItem * vitem = m_configpage->xv_port->firstChild ();
        NodePtr configdoc = xvideo->configDocument ();
        if (configdoc && configdoc->firstChild ()) {
            for (TQListViewItem *i=vitem->firstChild(); i; i=vitem->firstChild())
                delete i;
            NodePtr node = configdoc->firstChild ();
            for (node = node->firstChild (); node; node = node->nextSibling()) {
                if (!node->isElementNode ())
                    continue; // some text sneaked in ?
                Element * elm = convertNode <Element> (node);
                if (elm->getAttribute (KMPlayer::StringPool::attr_type) !=
                        TQString ("tree"))
                    continue;
                for (NodePtr n = elm->firstChild (); n; n = n->nextSibling ()) {
                    if (!n->isElementNode () || strcmp (n->nodeName (), "Port"))
                        continue;
                    Element * e = convertNode <Element> (n);
                    TQString portatt = e->getAttribute (
                            KMPlayer::StringPool::attr_value);
                    int port;
                    TQListViewItem *pi = new TQListViewItem (vitem, i18n ("Port ") + portatt);
                    port = portatt.toInt ();
                    for (NodePtr in=e->firstChild(); in; in=in->nextSibling()) {
                        if (!in->isElementNode () ||
                                strcmp (in->nodeName (), "Input"))
                            continue;
                        Element * i = convertNode <Element> (in);
                        TQString inp = i->getAttribute (
                                KMPlayer::StringPool::attr_name);
                        int enc = i->getAttribute (
                                KMPlayer::StringPool::attr_value).toInt ();
                        TQListViewItem * ii = new XVTreeItem(pi, inp, port, enc);
                        if (m_xvport == port && enc == m_xvencoding) {
                            ii->setSelected (true);
                            m_configpage->xv_port->ensureItemVisible (ii);
                        }
                    }
                }
            }
        } else // wait for showEvent
            connect (xvideo, TQ_SIGNAL (configReceived()), this, TQ_SLOT (configReceived()));
    }
}

TDE_NO_EXPORT void KMPlayerVDRSource::configReceived () {
    XVideo * xvideo = static_cast<XVideo *>(m_player->players()["xvideo"]);
    disconnect (xvideo, TQ_SIGNAL (configReceived()), this, TQ_SLOT (configReceived()));
    sync (false);
}

TDE_NO_EXPORT void KMPlayerVDRSource::prefLocation (TQString & item, TQString & icon, TQString & tab) {
    item = i18n ("Source");
    icon = TQString ("text-x-src");
    tab = i18n ("VDR");
}

TDE_NO_EXPORT TQFrame * KMPlayerVDRSource::prefPage (TQWidget * parent) {
    if (!m_configpage)
        m_configpage = new KMPlayerPrefSourcePageVDR (parent, m_player);
    return m_configpage;
}

TDE_NO_EXPORT bool KMPlayerVDRSource::requestPlayURL (KMPlayer::NodePtr) {
    return true;
}

TDE_NO_EXPORT void KMPlayerVDRSource::stateElementChanged (KMPlayer::Node *, KMPlayer::Node::State, KMPlayer::Node::State) {
}

//-----------------------------------------------------------------------------

static const char * xv_supported [] = {
    "tvsource", "vdrsource", 0L
};

KDE_NO_CDTOR_EXPORT XVideo::XVideo (TQObject * parent, Settings * settings)
 : KMPlayer::CallbackProcess (parent, settings, "xvideo", i18n ("X&Video")) {
    m_supported_sources = xv_supported;
    //m_player->settings ()->addPage (m_configpage);
}

KDE_NO_CDTOR_EXPORT XVideo::~XVideo () {}

TDE_NO_EXPORT bool XVideo::ready (KMPlayer::Viewer * v) {
    if (playing ()) {
        return true;
    }
    initProcess (v);
    TQString cmd  = TQString ("kxvplayer -wid %3 -cb %4").arg (viewer ()->embeddedWinId ()).arg (dcopName ());
    if (m_have_config == config_unknown || m_have_config == config_probe)
        cmd += TQString (" -c");
    if (m_source) {
        int xv_port = m_source->xvPort ();
        int xv_encoding = m_source->xvEncoding ();
        int freq = m_source->frequency ();
        cmd += TQString (" -port %1 -enc %2 -norm \"%3\"").arg (xv_port).arg (xv_encoding).arg (m_source->videoNorm ());
        if (freq > 0)
            cmd += TQString (" -freq %1").arg (freq);
    }
    fprintf (stderr, "%s\n", cmd.latin1 ());
    *m_process << cmd;
    m_process->start (TDEProcess::NotifyOnExit, TDEProcess::All);
    return m_process->isRunning ();
}

#include "kmplayervdr.moc"
