/***************************************************************************
                          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 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <qlistview.h>
#include <qtimer.h>
#include <qcheckbox.h>
#include <qspinbox.h>
#include <qlineedit.h>
#include <qlcdnumber.h>

#include <kdebug.h>
#include <klineeditdlg.h>
#include <klistview.h>
#include <klocale.h>
#include <kpushbutton.h>
#include <knuminput.h>

#include "channel.h"
#include "channelstore.h"
#include "channelwidgetimpl.h"
#include "channelwidgetimpl.moc"
#include "channelpropertiesdialog.h"
#include "qtvision.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, QtVision *qtvision)
                  :ChannelWidget(parent), cs(qtvision->channels()), qtv(qtvision)
{
    _listview->setColumnAlignment(0, KListView::AlignLeft);
    _listview->setColumnAlignment(1, KListView::AlignLeft);
    _listview->setItemsRenameable(true);
    _listview->setRenameable(0, false);
    _listview->setRenameable(1, true);

    createChannelList(cs);

    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 dialogue
    connect(_listview, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotEditClicked()));
             
    connect(_autoscan, SIGNAL(clicked()), this, SLOT(slotAutoscanClicked()));
    connect(_renumber, SIGNAL(clicked()), this, SLOT(slotRenumberClicked()));
    connect(_display, SIGNAL(clicked()), this, SLOT(slotChangeChannel()));
    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()));

    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


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


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

    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);
        _display->setEnabled(true);
        _moveUp->setEnabled(true);
        _moveDown->setEnabled(true);
    }
    return;
} // slotListItemClicked


void ChannelWidgetImpl::slotAutoscanClicked()
{
    return;
} // slotWizardClicked


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

    if (!lvi)
        return;

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

    KLineEditDlg *inputBox = new KLineEditDlg(i18n("Please enter a new name for this channel:"),
                                              clvi->text(1),
                                              this);
    inputBox->exec();
    if (!inputBox->result())
        return;

    clvi->c->updateValues(inputBox->text(), clvi->c->number(), 
			  clvi->c->freq(), 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 (qtv->channel() == l->c)
            qtv->setChannel(cs->channelAfter(l->c));
        cs->removeChannel(l->c);
    }

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

void ChannelWidgetImpl::slotRenumberClicked()
{
    cs->renumber();
}

void ChannelWidgetImpl::slotNewClicked()
{
    Channel *c = cs->addChannel((unsigned long)1000);
    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);

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


void ChannelWidgetImpl::slotFreqUpdate( unsigned long freq ) 
{
    kdDebug() << "ChannelWidgetImpl::slotFreqUpdate( " << freq << " )" << endl;
    ChannelListViewItem *l = getItemInEditor();
    if ( l != NULL ) 
    {
      l->c->updateValues( l->c->name(), l->c->number(), freq, l->c->enabled() );
      QTimer::singleShot(0, this, SLOT(slotChangeChannel));
    }
}


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

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

    // switch the channel to the currently selected channel
    QTimer::singleShot(0, this, SLOT(slotChangeChannel()));
        
    ChannelListViewItem *l = static_cast<ChannelListViewItem*>(item);
    ChannelPropertiesDialog *d = new ChannelPropertiesDialog(this);

    d->_enabled->setChecked(l->c->enabled());
    d->_number->display(l->c->number());
    d->_name->setText(l->c->name());
    d->_freq->setValue(double(l->c->freq())/1000);
    d->setFreqStep(0.250);
    
    setItemInEditor( l );
    unsigned long prevFreq = l->c->freq();
    connect( d, SIGNAL( frequencyUpdate( unsigned long ) ), this, SLOT( slotFreqUpdate( unsigned long ) ) );

    int rc = d->exec();
    if (rc == QDialog::Accepted) 
    {
        unsigned long value = static_cast<long>(d->_freq->value()*1000);
        l->c->updateValues(d->_name->text(), d->_number->intValue(), value, d->_enabled->isChecked());
    } 
    else 
    {
        // Edit has been cancelled so we have to reset the frequency
        // because it may have been changed by the editor.
        l = getItemInEditor();
        l->c->updateValues( l->c->name(), l->c->number(), prevFreq, l->c->enabled() );
    }

    // Make sure to display the edited channel with updated frequency
    QTimer::singleShot(0, this, SLOT(slotChangeChannel()));

    delete d;
}


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->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);
        currItem->updateFields();
    } else {
        // do the swap - NB! channel numbers do not change!
        Channel *tempCh = new Channel(currItem->c->parent()); 
	tempCh->setValues( currItem->c );

        currItem->c->updateValues(nextItem->c->name(), currItem->c->number(), nextItem->c->freq(), nextItem->c->enabled());
        nextItem->c->updateValues(tempCh->name(), nextItem->c->number(), tempCh->freq(), tempCh->enabled());
        delete tempCh;

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


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->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);
            currItem->updateFields();
        }
    } else {
        // do the swap - NB: channel numbers do not change!
        Channel *tempCh = new Channel(currItem->c->parent());
        tempCh->setValues( currItem->c );

        currItem->c->updateValues(nextItem->c->name(), currItem->c->number(), nextItem->c->freq(), nextItem->c->enabled());
        nextItem->c->updateValues(tempCh->name(), nextItem->c->number(), tempCh->freq(), tempCh->enabled());
        delete tempCh;

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

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

    return;
} // slotMoveDownClicked


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::setItemInEditor( ChannelListViewItem *i ) {
  itemInEditor = i;
}

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


