/***************************************************************************
                          channelwidgetimpl.cpp
    Implementation of the ChannelWidget.  Provieds the functionality for
    editing the channel list through the slot<action>Clicked() functions.
                             -------------------
    begin                : Wed Jun 12 2002
    copyright            : (C) 2002 by Kevin Hessels
    email                : khessels@shaw.ca
 ***************************************************************************/

/*
 * 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 <qlistview.h>
#include <qtimer.h>
#include <qcheckbox.h>
#include <qspinbox.h>
#include <qlineedit.h>
#include <qlcdnumber.h>
#include <qcombobox.h>
#include <qinputdialog.h>

#include <kdebug.h>
#include <kdeversion.h>

#include <kinputdialog.h>

#include <kiconloader.h>
#include <klistview.h>
#include <klocale.h>
#include <kpushbutton.h>
#include <knuminput.h>

#include "cfgdata.h"
#include "channel.h"
#include "channelstore.h"
#include "channelwidgetimpl.h"
#include "channelwidgetimpl.moc"
#include "kdetv.h"
#include "sourcemanager.h"


#define CLVI_RTTI 589426

ChannelListViewItem::ChannelListViewItem(KListView *parent, Channel *ch)
    :QCheckListItem(parent, QString::number(ch->number()), QCheckListItem::CheckBox), c(ch)
{
    setText(1, c->name());
    connect(c, SIGNAL(changed()), this, SLOT(updateFields()));
    return;
} // ChannelListItem constructor


ChannelListViewItem::~ChannelListViewItem()
{
    return;
} // ChannelListViewItem destructor


void ChannelListViewItem::updateFields()
{
    if (c->name() != text(1))
        setText(1, c->name());
    if (QString::number(c->number()) != text(0))
        setText(0, QString::number(c->number()));
    if (c->enabled() != isOn())
        setOn(c->enabled());
} // updateFields

// taken wholesale from channeleditor.cpp....
int ChannelListViewItem::compare(QListViewItem *item, int col, bool ascending) const
{
    int me, him;

    if (item->rtti() != CLVI_RTTI)
        return QCheckListItem::compare(item, col, ascending);

    switch (col) {
    case 0:
    case 1:
        me = c->number();
        him = static_cast<ChannelListViewItem*>(item)->c->number();

        if (me == him)
            return 0;
        return (ascending ? 1 : -1) * (me > him ? 1 : -1);
        break;
    default:
        break;
    } // switch

    return QCheckListItem::compare(item, col, ascending);
} // compare


int ChannelListViewItem::rtti() const
{
    return CLVI_RTTI;
} // rtti



/***************************************************************************/




ChannelWidgetImpl::ChannelWidgetImpl(QWidget *parent, Kdetv *ktv, ConfigData *cfg)
    : ChannelWidget(parent),
      SettingsDialogPage(i18n("Channels"),
                         i18n("Configure Channel Settings"),
                         "queue"),
      _cs(ktv->channels()),
      _ktv(ktv),
      _srcm(ktv->sourceManager()),
      _cfg(cfg)
{
    _listview->setColumnAlignment(0, KListView::AlignLeft);
    _listview->setColumnAlignment(1, KListView::AlignLeft);
    _listview->setItemsRenameable(true);
    _listview->setRenameable(0, false);
    _listview->setRenameable(1, true);

    connect(_listview, SIGNAL(selectionChanged()),
            this, SLOT(slotListItemClicked()));

    // call slotRenameItem when the user renames the seleted item
    connect(_listview, SIGNAL(itemRenamed(QListViewItem *)),
            this, SLOT(slotRenameItem(QListViewItem *)));
    // a double click will raise the edit dialog
    connect(_listview, SIGNAL(doubleClicked(QListViewItem*)),
            this, SLOT(slotEditClicked()));

    connect(_new, SIGNAL(clicked()), this, SLOT(slotNewClicked()));
    connect(_edit, SIGNAL(clicked()), this, SLOT(slotEditClicked()));

    // set the cursor so that text can be entered
    connect(_rename, SIGNAL(clicked()), this, SLOT(slotRenameClicked()));
    connect(_remove, SIGNAL(clicked()), this, SLOT(slotRemoveClicked()));
    connect(_moveUp, SIGNAL(clicked()), this, SLOT(slotMoveUpClicked()));
    connect(_moveDown, SIGNAL(clicked()), this, SLOT(slotMoveDownClicked()));
    connect(_killdoubles, SIGNAL(clicked()), this, SLOT(slotKillDoublesClicked()));

    _moveUp->setPixmap  (KGlobal::iconLoader()->loadIcon( "1uparrow",
                                                          KIcon::NoGroup,
                                                          KIcon::SizeSmall ));
    _moveDown->setPixmap(KGlobal::iconLoader()->loadIcon( "1downarrow",
                                                          KIcon::NoGroup,
                                                          KIcon::SizeSmall ));

    return;
} // ChannelWidgetImpl constructor


ChannelWidgetImpl::~ChannelWidgetImpl()
{
    return;
} // ChannelWidgetImpl destructor


void ChannelWidgetImpl::updateChannels()
{
    if (!_cs)
        return;

    if (_listview->childCount() != (int)_cs->count())
        return;

    for (int i = 0; i < _listview->childCount(); ++i) {
        ChannelListViewItem *item = static_cast<ChannelListViewItem *>(_listview->itemAtIndex(i));
        item->c->setEnabled(item->isOn());
    } // for
    return;
} // updateChannels


void ChannelWidgetImpl::setup()
{
    createChannelList(_cs);
}

void ChannelWidgetImpl::apply()
{
    updateChannels();
    _ktv->saveChannels();
}

void ChannelWidgetImpl::defaults()
{
}

void ChannelWidgetImpl::cancel()
{
    _ktv->reloadChannels();
}


/***************************************************************************
 *
 * Private
 *
 ***************************************************************************/


void ChannelWidgetImpl::createChannelList(ChannelStore *)
{
    if (!_cs)
        return;

    _listview->clear();

    for (uint i = 0; i < _cs->count(); ++i) {
        Channel *ch = _cs->channelAt(i);
        if (ch) {
            ChannelListViewItem *cli = new ChannelListViewItem(_listview, ch);
            cli->setOn(ch->enabled());
        } // if
    } // for
    _listview->sort();
    return;
} // createChannelList


/***************************************************************************
 *
 * Private Slots
 *
 ***************************************************************************/


void ChannelWidgetImpl::slotListItemClicked()
{
    if (! _listview->selectedItems().isEmpty()) {
        _edit->setEnabled(true);
        _rename->setEnabled(true);
        _remove->setEnabled(true);
        _moveUp->setEnabled(true);
        _moveDown->setEnabled(true);
    }

    slotChangeChannel();
    return;
} // slotListItemClicked


void ChannelWidgetImpl::slotRenameClicked()
{
    QListViewItem *lvi = _listview->currentItem();

    if (!lvi)
        return;

    ChannelListViewItem *clvi = static_cast<ChannelListViewItem*>(lvi);

    bool ok;
    QString text = KInputDialog::getText(i18n("Rename Channel"),
                                         i18n("Please enter a new name for this channel:"),
                                         clvi->text(1),
                                         &ok,
                                         this);
    if (!ok)
        return;

    clvi->c->updateValues(text, clvi->c->number(), clvi->c->enabled());

    return;
} // slotRenameClicked


void ChannelWidgetImpl::slotRemoveClicked()
{
    QPtrList<QListViewItem> sels = _listview->selectedItems();

    if (sels.isEmpty())
        return;

    for (QPtrListIterator<QListViewItem> it(sels); it.current() != NULL; ++it) {
        QListViewItem* lvi = it.current();
        if (!lvi || lvi->rtti() != CLVI_RTTI)
            continue;
        ChannelListViewItem *l = static_cast<ChannelListViewItem*>(lvi);

        _listview->takeItem(l);
        if (_ktv->channel() == l->c)
            _ktv->setChannel(_cs->channelAfter(l->c));
        _cs->removeChannel(l->c);
    }

    _cs->renumber();

    QListViewItem * currentItem = _listview->currentItem();
    if (currentItem) {
        _listview->selectAll(false);
        _listview->setSelected (currentItem, true);
    } else {
        _edit->setEnabled(false);
        _rename->setEnabled(false);
        _remove->setEnabled(false);
        _moveUp->setEnabled(false);
        _moveDown->setEnabled(false);
    }
}


void ChannelWidgetImpl::slotNewClicked()
{
    if (  _cs && _srcm->hasDevice() ) {
        Channel *c = _srcm->createChannel(_cs);
        _cs->addChannel(c);

        ChannelListViewItem *cli = new ChannelListViewItem(_listview, c);
        cli->setOn(c->enabled());
        _listview->ensureItemVisible(cli);
        _listview->setCurrentItem(cli);
        QTimer::singleShot(0, this, SLOT(slotEditClicked()));
    }
}


void ChannelWidgetImpl::slotChangeChannel()
{
    QListViewItem *lvi = _listview->currentItem();

    if (!lvi || lvi->rtti() != CLVI_RTTI)
        return;

    ChannelListViewItem *l = static_cast<ChannelListViewItem*>(lvi);

    _ktv->setChannel(l->c);
}


void ChannelWidgetImpl::slotEditClicked()
{
    QListViewItem *lvi = _listview->currentItem();

    if (!lvi || lvi->rtti() != CLVI_RTTI)
        return;

    ChannelListViewItem *l = static_cast<ChannelListViewItem*>(lvi);

    // switch the channel to the currently selected channel
    QTimer::singleShot(0, this, SLOT(slotChangeChannel()));
    setItemInEditor( l );

    QDialog* d = _srcm->channelPropertiesDialog(l->c, this);
    connect(d, SIGNAL(accepted()),
            this, SLOT(slotPropertiesFinished()));
    connect(d, SIGNAL(rejected()),
            this, SLOT(slotPropertiesFinished()));

    d->show();
}

void ChannelWidgetImpl::slotPropertiesFinished()
{
    QTimer::singleShot(0, this, SLOT(slotChangeChannel()));
}

void ChannelWidgetImpl::slotMoveDownClicked()
{
    ChannelListViewItem *currItem = static_cast<ChannelListViewItem *>(_listview->currentItem());

    if (!currItem || currItem->rtti() != CLVI_RTTI)
        return;

    ChannelListViewItem *nextItem;    // the one to swap with...
    if (currItem == static_cast<ChannelListViewItem*>(_listview->lastItem())) {
        nextItem = 0;
    } else {
        nextItem = static_cast<ChannelListViewItem *>(_listview->itemAtIndex( _listview->itemIndex(currItem) + 1 ));
    }

    if (!nextItem || nextItem->c->number() != currItem->c->number() + 1) {
        currItem->c->setNumber(currItem->c->number()+1);
        _cs->renumber();
        currItem->updateFields();
    } else {
        // do the swap - NB! channel numbers do not change!
        Channel *tempCh = new Channel(currItem->c->parent());
        *tempCh = currItem->c;

        int num;

        num = currItem->c->number();
        *(currItem->c) = nextItem->c;
        currItem->c->setNumber(num);

        num = nextItem->c->number();
        *(nextItem->c) = tempCh;
        nextItem->c->setNumber(num);

        delete tempCh;

        // update the display
        currItem->updateFields();
        nextItem->updateFields();
        _listview->selectAll(false);
        _listview->setSelected(nextItem, true);
        _listview->setCurrentItem(nextItem);
        _listview->ensureItemVisible(nextItem);
    }
} // slotMoveDownClicked


void ChannelWidgetImpl::slotMoveUpClicked()
{
    ChannelListViewItem *currItem = static_cast<ChannelListViewItem *>(_listview->currentItem());

    if (!currItem || currItem->rtti() != CLVI_RTTI)
        return;

    ChannelListViewItem *nextItem;  // the one to swap with....
    if (currItem == static_cast<ChannelListViewItem*>(_listview->itemAtIndex(0)))
        nextItem = 0;
    else
        nextItem = static_cast<ChannelListViewItem*>(_listview->itemAtIndex( _listview->itemIndex(currItem) - 1 ));

    if (!nextItem || nextItem->c->number() != currItem->c->number() - 1) {
        if (currItem->c->number() > 1) {
            currItem->c->setNumber(currItem->c->number()-1);
            _cs->renumber();
            currItem->updateFields();
        }
    } else {
        // do the swap - NB: channel numbers do not change!
        Channel *tempCh = new Channel(currItem->c->parent());
        *tempCh = currItem->c;

        int num;

        num = currItem->c->number();
        *(currItem->c) = nextItem->c;
        currItem->c->setNumber(num);

        num = nextItem->c->number();
        *(nextItem->c) = tempCh;
        nextItem->c->setNumber(num);

        delete tempCh;

        // update the display
        currItem->updateFields();
        nextItem->updateFields();

        _listview->selectAll(false);
        _listview->setSelected(nextItem, true);
        _listview->setCurrentItem(nextItem);
        _listview->ensureItemVisible(nextItem);
    }

    return;
} // slotMoveUpClicked


void ChannelWidgetImpl::slotRenameItem(QListViewItem *item)
{
    if (!item || item->rtti() != CLVI_RTTI)
        return;

    ChannelListViewItem *l = static_cast<ChannelListViewItem*>(item);

    // the channel name field of the list item
    const QString str = l->text(1);

    l->c->setName(str);
    return;
}

void ChannelWidgetImpl::slotKillDoublesClicked()
{
    QListViewItem *lvi = _listview->lastItem();
    ChannelListViewItem *dli;

    // check the list from back to front for duplicates
    while (lvi) {
        dli = 0;

        if (lvi->rtti() == CLVI_RTTI) {
            ChannelListViewItem *l = static_cast<ChannelListViewItem*>(lvi);

            /*
              kdDebug() << "Check for Duplicate: (" << l->c->number() << "):" << l->c->name() << ", " \
              << l->c->freq() << ", " << l->c->source() << endl;
            */
            QListViewItem *pli = lvi->itemAbove();

            while (pli) {
                if (pli->rtti() == CLVI_RTTI) {
                    ChannelListViewItem *pl = static_cast<ChannelListViewItem*>(pli);
                    /*
                      kdDebug() << "Check Current: (" << pl->c->number() << "):" << pl->c->name() << ", " \
                      << pl->c->freq() << ", " << pl->c->source() << endl;
                    */
                    if (l->c->compareChannelProperties(*pl->c)) {
                        kdDebug() << "Duplicate: (" << pl->c->number() << "):" << pl->c->name() << ", (" \
                                  << l->c->number() << "):" << l->c->name() << endl;
                        dli = l;
                        break;
                    }
                }
                pli = pli->itemAbove();
            }
        }

        lvi = lvi->itemAbove();

        if (dli) {
            kdDebug() << "Remove duplicate: (" << dli->c->number() << "):" << dli->c->name() << endl;
            _listview->takeItem(dli);
            if (_ktv->channel() == dli->c)
                _ktv->setChannel(_cs->channelAfter(dli->c));
            _cs->removeChannel(dli->c);
            delete dli;
        }
    }

    _cs->renumber();

    QListViewItem * currentItem = _listview->currentItem();
    if (currentItem) {
        _listview->selectAll(false);
        _listview->setSelected (currentItem, true);
    } else {
        _edit->setEnabled(false);
        _rename->setEnabled(false);
        _remove->setEnabled(false);
        _moveUp->setEnabled(false);
        _moveDown->setEnabled(false);
    }
}

void ChannelWidgetImpl::setItemInEditor( ChannelListViewItem *i )
{
    _itemInEditor = i;
}

ChannelListViewItem *ChannelWidgetImpl::getItemInEditor()
{
    return _itemInEditor;
}

