/***************************************************************************
 *   Copyright (C) 2005-2007 Nicolas Hadacek <hadacek@kde.org>             *
 *   Copyright (C) 2003-2004 Alain Gibaud <alain.gibaud@free.fr>           *
 *                                                                         *
 *   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 "direct_config_widget.h"

#include <tqtooltip.h>
#include <tqcheckbox.h>
#include <tqtimer.h>
#include <tqpushbutton.h>

#include "progs/direct/base/direct_prog_config.h"
#include "progs/direct/base/direct_prog.h"

//-----------------------------------------------------------------------------
::Programmer::ConfigWidget *Direct::GroupUI::createConfigWidget(TQWidget *parent) const
{
  return new ConfigWidget(static_cast<const ::Programmer::Group &>(group()), parent);
}

//-----------------------------------------------------------------------------
const char * const INV_PIN_LABEL = I18N_NOOP("Check this option if your hardware uses negative logic for this pin.");
const char * const DELAY_LABEL = I18N_NOOP("Some programming cards need low clock rate:\nadding delay to clock pulses might help.");

Direct::HConfigWidget::HConfigWidget(::Programmer::Base &base, TQWidget *parent, bool edit)
  : ::Hardware::HConfigWidget(base, parent, edit)
{
  // pins assignment
  TQGroupBox *groupb = new TQGroupBox(1,Qt::Horizontal, i18n("Pin assignment"), this);
  _mainVBox->addWidget(groupb);
  TQWidget *w = new TQWidget(groupb);
  TQGridLayout *grid = new TQGridLayout(w, 1, 1, 0, 10);
  if (edit) grid->setColStretch(5, 1);
  for (uint i=0; i<Nb_PinTypes; i++) {
    TQLabel *label = new TQLabel(i18n(PIN_DATA[i].label), w);
    TQToolTip::add(label, PIN_DATA[i].comment);
    grid->addWidget(label, i, 0);
    _combos[i] = new TQComboBox(w);
    _combos[i]->setEnabled(edit);
    connect(_combos[i], TQT_SIGNAL(activated(int)), TQT_SLOT(slotPinChanged()));
    TQToolTip::add(_combos[i], PIN_DATA[i].comment);
    grid->addWidget(_combos[i], i, 1);
    _invcbs[i] = new TQCheckBox(i18n("Inverted"), w);
    _invcbs[i]->setEnabled(edit);
    TQToolTip::add(_invcbs[i], i18n(INV_PIN_LABEL));
    grid->addWidget(_invcbs[i], i, 2);
    if (edit) {
      _testcbs[i] = new TQCheckBox(i18n("on"), w);
      TQToolTip::add(_testcbs[i], PIN_DATA[i].testComment);
      connect(_testcbs[i], TQT_SIGNAL(clicked()), TQT_SLOT(slotTestPin()));
      grid->addWidget(_testcbs[i], i, 3);
      _testLabels[i] = new TQLabel(w);
      TQToolTip::add(_testcbs[i], PIN_DATA[i].testComment);
      grid->addWidget(_testLabels[i], i, 4);
      updateTestStatus(PinType(i), false);
    } else {
      _testcbs[i] = 0;
      _testLabels[i] = 0;
    }
  }

  TQHBoxLayout *hbox = new TQHBoxLayout(_mainVBox);
  TQLabel *label = new TQLabel(i18n("Clock delay"), this);
  TQToolTip::add(label, i18n(DELAY_LABEL));
  hbox->addWidget(label);
  _delay = new KIntNumInput(0,Qt::Horizontal, this);
  _delay->setRange(0, 50, 5);
  _delay->setEnabled(edit);
  TQToolTip::add(_delay, i18n(DELAY_LABEL));
  hbox->addWidget(_delay);

  if (edit) {
    _sendBitsButton = new TQPushButton(i18n("Send 0xA55A"), this);
    _sendBitsButton->setToggleButton(true);
    connect(_sendBitsButton, TQT_SIGNAL(clicked()), TQT_SLOT(sendBits()));
    TQToolTip::add(_sendBitsButton, i18n("Continuously send 0xA55A on \"Data out\" pin."));
    _editVBox->addWidget(_sendBitsButton);
    _editVBox->addStretch(1);
  } else _sendBitsButton = 0;

  // timer for sending bits
  _timerSendBits = new TQTimer(this);
  connect(_timerSendBits, TQT_SIGNAL(timeout()), TQT_SLOT(slotSendBits()));

  // timer for automatically polling DataOut pin
  _timerPollDataOut = new TQTimer(this);
  connect(_timerPollDataOut, TQT_SIGNAL(timeout()), TQT_SLOT(updateDataIn()));
}

void Direct::HConfigWidget::sendBits()
{
  if ( _timerSendBits->isActive() ) {
    _sendBitsButton->setText(i18n("Send 0xA55A"));
    _timerSendBits->stop();
    updateTestPin(DataOut);
  } else {
    _sendBitsButton->setText(i18n("Stop"));
    _timerSendBits->start(1);
  }
}

uint Direct::HConfigWidget::pin(PinType ptype) const
{
  return static_cast<const Hardware *>(_hardware)->pinForIndex(PIN_DATA[ptype].dir, _combos[ptype]->currentItem());
}

void Direct::HConfigWidget::slotPinChanged()
{
  for (uint i = 0; i<Nb_PinTypes; i++) {
    if ( sender()!=_combos[i] ) continue;
    updatePin(PinType(i));
  }
}

void Direct::HConfigWidget::updatePin(PinType ptype)
{
  bool ground = hardware()->isGroundPin(pin(ptype));
  _invcbs[ptype]->setEnabled(_edit);
  if (ground) _invcbs[ptype]->hide();
  else _invcbs[ptype]->show();
  if (_edit) {
    _testcbs[ptype]->setEnabled(PIN_DATA[ptype].dir==Port::Out && _connected);
    _testLabels[ptype]->setEnabled(_connected);
    if (ground) {
      _testcbs[ptype]->hide();
      _testLabels[ptype]->hide();
    } else {
      _testcbs[ptype]->show();
      _testLabels[ptype]->show();
    }
  }
}

void Direct::HConfigWidget::slotTestPin()
{
  for (uint i = 0; i<Nb_PinTypes; i++) {
    if ( sender()!=_testcbs[i] ) continue;
    updateTestPin(PinType(i));
    break;
  }
}

void Direct::HConfigWidget::updateTestPin(PinType ptype)
{
  Q_ASSERT( _connected && ptype!=DataIn );
  bool on = _testcbs[ptype]->isChecked();
  hardware()->setPin(ptype, on);
  updateTestStatus(ptype, on);
  if ( ptype==Vpp ) updateDataIn();
}

void Direct::HConfigWidget::updateTestStatus(PinType ptype, bool on)
{
  if (on) _testLabels[ptype]->setText(i18n(PIN_DATA[ptype].onLabel));
  else _testLabels[ptype]->setText(i18n(PIN_DATA[ptype].offLabel));
}

void Direct::HConfigWidget::updateDataIn()
{
  bool on = hardware()->readBit();
  updateTestStatus(DataIn, on);
  _testcbs[DataIn]->setChecked(on);
}

void Direct::HConfigWidget::sendBits(uint d, int nbb)
{
  Q_ASSERT(_connected);
  hardware()->setWrite();
  for (; nbb; --nbb) {
    hardware()->setPin(Clock, High);
    if ( d & 0x01 ) hardware()->setPin(DataOut, High);
    else hardware()->setPin(DataOut, Low);
    hardware()->setPin(Clock, Low);
    d >>= 1;       // Move the data over 1 bit
  }
  hardware()->setPin(DataOut, Low);
  hardware()->setRead();
}

void Direct::HConfigWidget::slotSendBits()
{
    sendBits(0xA55A, 16);
}

bool Direct::HConfigWidget::set(const Port::Description &pd, const ::Hardware::Data &data)
{
  // connect port
  _timerPollDataOut->stop();
  if ( _timerSendBits->isActive() ) sendBits(); // stop
  delete _hardware;
  const HardwareData &hdata = static_cast<const HardwareData &>(data);
  if ( pd.type==PortType::Serial ) _hardware = new SerialHardware(_base, pd.device, hdata);
  else _hardware = new ParallelHardware(_base, pd.device, hdata);
  bool ok = _hardware->connectHardware();
  if ( !_edit) _hardware->disconnectHardware();
  else _connected = ok;

  // update GUI
  if (_edit) {
    for (uint i=0; i<Nb_PinTypes; i++) {
      _testcbs[i]->setEnabled(_connected);
      updateTestStatus(PinType(i), false);
    }
    if ( _connected ) _timerPollDataOut->start(100);
    _sendBitsButton->setEnabled(_connected);
  }

  // update pins
  for (uint i=0; i<Nb_PinTypes; i++) {
    _combos[i]->clear();
    Port::IODir dir = PIN_DATA[i].dir;
    for (uint k=0; k<hardware()->nbPins(dir); k++)
      _combos[i]->insertItem(hardware()->pinLabelForIndex(dir, k));
    if (PIN_DATA[i].canBeGround) _combos[i]->insertItem("GND");
    _combos[i]->setCurrentItem(hardware()->indexForPin(dir, hdata.data.pins[i]));
    _invcbs[i]->setChecked(hdata.data.pins[i]<0);
    updatePin(PinType(i));
  }
  _delay->setValue(hdata.data.clockDelay);

  return ok;
}

Hardware::Data *Direct::HConfigWidget::data() const
{
  HardwareData *hdata = new HardwareData;
  hdata->portType = _hardware->portDescription().type;
  for (uint i=0; i<Nb_PinTypes; i++) {
    hdata->data.pins[i] = pin(PinType(i));
    if ( _invcbs[i]->isChecked() ) hdata->data.pins[i] = -hdata->data.pins[i];
  }
  hdata->data.clockDelay = _delay->value();
  return hdata;
}

//-----------------------------------------------------------------------------
Direct::ConfigWidget::ConfigWidget(const ::Programmer::Group &group, TQWidget *parent)
  : ::Hardware::ConfigWidget(new ::Direct::PicBase(group, 0), new Config, parent)
{}

Hardware::HConfigWidget *Direct::ConfigWidget::createHardwareConfigWidget(TQWidget *parent, bool edit) const
{
  return new HConfigWidget(*_base, parent, edit);
}
