#include "digitalchanneldialog.hh"
#include "application.hh"
#include <QCompleter>
#include "rxgrouplistdialog.hh"
#include "repeatercompleter.hh"
#include "repeaterdatabase.hh"
#include "extensionwrapper.hh"
#include "propertydelegate.hh"
#include "settings.hh"
#include "utils.hh"
#include "logger.hh"


/* ********************************************************************************************* *
 * Implementation of DigitalChannelDialog
 * ********************************************************************************************* */
DigitalChannelDialog::DigitalChannelDialog(Config *config, QWidget *parent)
  : QDialog(parent), _config(config), _myChannel(new DMRChannel(this)), _channel(nullptr)
{
  construct();
}

DigitalChannelDialog::DigitalChannelDialog(Config *config, DMRChannel *channel, QWidget *parent)
  : QDialog(parent), _config(config), _myChannel(nullptr), _channel(channel)
{
  if (_channel) {
    _myChannel = _channel->clone()->as<DMRChannel>();
    _myChannel->setParent(parent);
  }
  construct();
}

void
DigitalChannelDialog::construct() {
  setupUi(this);
  Settings settings;

  if (settings.hideChannelNote()) {
    hintLabel->setVisible(false);
    layout()->invalidate();
    adjustSize();
  }

  Application *app = qobject_cast<Application *>(qApp);
  DMRRepeaterFilter *filter = new DMRRepeaterFilter(app->repeater(), app->position(), this);
  filter->setSourceModel(app->repeater());
  QCompleter *completer = new RepeaterCompleter(2, app->repeater(), this);
  completer->setModel(filter);
  channelName->setCompleter(completer);
  connect(completer, SIGNAL(activated(const QModelIndex &)),
          this, SLOT(onRepeaterSelected(const QModelIndex &)));

  powerValue->setItemData(0, unsigned(Channel::Power::Max));
  powerValue->setItemData(1, unsigned(Channel::Power::High));
  powerValue->setItemData(2, unsigned(Channel::Power::Mid));
  powerValue->setItemData(3, unsigned(Channel::Power::Low));
  powerValue->setItemData(4, unsigned(Channel::Power::Min));
  powerDefault->setChecked(true); powerValue->setCurrentIndex(1); powerValue->setEnabled(false);
  totDefault->setChecked(true); totValue->setValue(0); totValue->setEnabled(false);
  scanList->addItem(tr("[None]"), QVariant::fromValue((ScanList *)(nullptr)));
  scanList->setCurrentIndex(0);
  for (int i=0; i<_config->scanlists()->count(); i++) {
    scanList->addItem(_config->scanlists()->scanlist(i)->name(),
                      QVariant::fromValue(_config->scanlists()->scanlist(i)));
    if (_myChannel && (_myChannel->scanList() == _config->scanlists()->scanlist(i)) )
      scanList->setCurrentIndex(i+1);
  }
  txAdmit->setItemData(0, unsigned(DMRChannel::Admit::Always));
  txAdmit->setItemData(1, unsigned(DMRChannel::Admit::Free));
  txAdmit->setItemData(2, unsigned(DMRChannel::Admit::ColorCode));
  timeSlot->setItemData(0, unsigned(DMRChannel::TimeSlot::TS1));
  timeSlot->setItemData(1, unsigned(DMRChannel::TimeSlot::TS2));
  populateRXGroupListBox(rxGroupList, _config->rxGroupLists(),
                         (nullptr != _myChannel ? _myChannel->groupListObj() : nullptr));
  txContact->addItem(tr("[None]"), QVariant::fromValue(nullptr));
  if (_myChannel && (nullptr == _myChannel->txContactObj()))
    txContact->setCurrentIndex(0);
  for (int i=0; i<_config->contacts()->count(); i++) {
    txContact->addItem(_config->contacts()->contact(i)->name(),
                       QVariant::fromValue(_config->contacts()->contact(i)));
    if (_myChannel && (_myChannel->txContactObj() == _config->contacts()->contact(i)) )
      txContact->setCurrentIndex(i+1);
  }
  gpsSystem->addItem(tr("[None]"), QVariant::fromValue((GPSSystem *)nullptr));
  for (int i=0; i<_config->posSystems()->count(); i++) {
    PositioningSystem *sys = _config->posSystems()->system(i);
    gpsSystem->addItem(sys->name(), QVariant::fromValue(sys));
    if (_myChannel && (_myChannel->aprsObj() == sys))
      gpsSystem->setCurrentIndex(i+1);
  }
  roaming->addItem(tr("[None]"), QVariant::fromValue((RoamingZone *)nullptr));
  roaming->addItem(tr("[Default]"), QVariant::fromValue(DefaultRoamingZone::get()));
  if (_myChannel && (_myChannel->roamingZone() == DefaultRoamingZone::get()))
    roaming->setCurrentIndex(1);
  for (int i=0; i<_config->roamingZones()->count(); i++) {
    RoamingZone *zone = _config->roamingZones()->zone(i);
    roaming->addItem(zone->name(), QVariant::fromValue(zone));
    if (_myChannel && (_myChannel->roamingZone() == zone))
      roaming->setCurrentIndex(i+2);
  }
  dmrID->addItem(tr("[Default]"), QVariant::fromValue(DefaultRadioID::get()));
  dmrID->setCurrentIndex(0);
  for (int i=0; i<_config->radioIDs()->count(); i++) {
    dmrID->addItem(_config->radioIDs()->getId(i)->name(),
                   QVariant::fromValue(_config->radioIDs()->getId(i)));
    if (_myChannel && (_config->radioIDs()->getId(i) == _myChannel->radioIdObj())) {
      dmrID->setCurrentIndex(i+1);
    }
  }
  voxDefault->setChecked(true); voxValue->setValue(0); voxValue->setEnabled(false);

  channelName->setText(_myChannel->name());
  rxFrequency->setText(_myChannel->rxFrequency().format(Frequency::Unit::MHz));
  txFrequency->setText(_myChannel->txFrequency().format(Frequency::Unit::MHz));

  offsetComboBox->addItem(QIcon::fromTheme("symbol-none"), "");
  offsetComboBox->setItemData(0, tr("No offset"), Qt::ToolTipRole);
  offsetComboBox->addItem(QIcon::fromTheme("symbol-plus"), "");
  offsetComboBox->setItemData(1, tr("Positive offset"), Qt::ToolTipRole);
  offsetComboBox->addItem(QIcon::fromTheme("symbol-minus"), "");
  offsetComboBox->setItemData(2, tr("Negative offset"), Qt::ToolTipRole);

  updateOffsetFrequency();


  if (! _myChannel->defaultPower()) {
    powerDefault->setChecked(false); powerValue->setEnabled(true);
    switch (_myChannel->power()) {
    case Channel::Power::Max: powerValue->setCurrentIndex(0); break;
    case Channel::Power::High: powerValue->setCurrentIndex(1); break;
    case Channel::Power::Mid: powerValue->setCurrentIndex(2); break;
    case Channel::Power::Low: powerValue->setCurrentIndex(3); break;
    case Channel::Power::Min: powerValue->setCurrentIndex(4); break;
    }
  }
  if (! _myChannel->defaultTimeout()) {
    totDefault->setChecked(false); totValue->setEnabled(true);
    totValue->setValue(_channel->timeout());
  }
  rxOnly->setChecked(_myChannel->rxOnly());
  switch (_myChannel->admit()) {
  case DMRChannel::Admit::Always: txAdmit->setCurrentIndex(0); break;
  case DMRChannel::Admit::Free: txAdmit->setCurrentIndex(1); break;
  case DMRChannel::Admit::ColorCode: txAdmit->setCurrentIndex(2); break;
  }
  colorCode->setValue(_myChannel->colorCode());
  if (DMRChannel::TimeSlot::TS1 == _myChannel->timeSlot())
    timeSlot->setCurrentIndex(0);
  else if (DMRChannel::TimeSlot::TS2 == _myChannel->timeSlot())
    timeSlot->setCurrentIndex(1);
  if (! _myChannel->defaultVOX()) {
    voxDefault->setChecked(false); voxValue->setEnabled(true);
    voxValue->setValue(_channel->vox());
  }

  extensionView->setObjectName("digitalChannelExtension");
  extensionView->setObject(_myChannel, _config);

  if (! settings.showExtensions())
    tabWidget->tabBar()->hide();

  connect(powerDefault, SIGNAL(toggled(bool)), this, SLOT(onPowerDefaultToggled(bool)));
  connect(totDefault, SIGNAL(toggled(bool)), this, SLOT(onTimeoutDefaultToggled(bool)));
  connect(voxDefault, SIGNAL(toggled(bool)), this, SLOT(onVOXDefaultToggled(bool)));
  connect(hintLabel, SIGNAL(linkActivated(QString)), this, SLOT(onHideChannelHint()));

  connect(txFrequency, &QLineEdit::editingFinished, this, &DigitalChannelDialog::onTxFrequencyEdited);
  connect(rxFrequency, &QLineEdit::editingFinished, this, &DigitalChannelDialog::onRxFrequencyEdited);
  connect(offsetLineEdit, &QLineEdit::editingFinished, this, &DigitalChannelDialog::onOffsetFrequencyEdited);
  connect(offsetComboBox, &QComboBox::currentIndexChanged, this, &DigitalChannelDialog::onOffsetCurrentIndexChanged);
}

DMRChannel *
DigitalChannelDialog::channel()
{
  _myChannel->setRadioIdObj(dmrID->currentData().value<DMRRadioID*>());
  _myChannel->setName(channelName->text());
  _myChannel->setRXFrequency(Frequency::fromString(rxFrequency->text()));
  _myChannel->setTXFrequency(Frequency::fromString(txFrequency->text()));
  if (powerDefault->isChecked())
    _myChannel->setDefaultPower();
  else
    _myChannel->setPower(Channel::Power(powerValue->currentData().toUInt()));
  if (totDefault->isChecked())
    _myChannel->setDefaultTimeout();
  else
    _myChannel->setTimeout(totValue->value());
  _myChannel->setRXOnly(rxOnly->isChecked());
  _myChannel->setScanList(scanList->currentData().value<ScanList *>());
  _myChannel->setAdmit(DMRChannel::Admit(txAdmit->currentData().toUInt()));
  _myChannel->setColorCode(colorCode->value());
  _myChannel->setTimeSlot(DMRChannel::TimeSlot(timeSlot->currentData().toUInt()));
  _myChannel->setGroupListObj(rxGroupList->currentData().value<RXGroupList *>());
  _myChannel->setTXContactObj(txContact->currentData().value<DMRContact *>());
  _myChannel->setAPRSObj(gpsSystem->currentData().value<PositioningSystem *>());
  _myChannel->setRoamingZone(roaming->currentData().value<RoamingZone *>());
  if (voxDefault->isChecked())
    _myChannel->setVOXDefault();
  else
    _myChannel->setVOX(voxValue->value());

  DMRChannel *channel = _myChannel;
  if (nullptr == _channel) {
    _myChannel->setParent(nullptr);
    _myChannel = nullptr;
  } else {
    _channel->copy(*_myChannel);
    channel = _channel;
  }

  return channel;
}

void
DigitalChannelDialog::onRepeaterSelected(const QModelIndex &index) {
  Application *app = qobject_cast<Application *>(qApp);

  QModelIndex src = qobject_cast<QAbstractProxyModel*>(
        channelName->completer()->completionModel())->mapToSource(index);
  src = qobject_cast<QAbstractProxyModel*>(
        channelName->completer()->model())->mapToSource(src);
  Frequency rx = app->repeater()->get(src.row()).rxFrequency();
  Frequency tx = app->repeater()->get(src.row()).txFrequency();
  colorCode->setValue(app->repeater()->get(src.row()).colorCode());
  rxFrequency->setText(rx.format());
  txFrequency->setText(tx.format());

  _myChannel->setRXFrequency(Frequency::fromString(rxFrequency->text()));
  _myChannel->setTXFrequency(Frequency::fromString(txFrequency->text()));
  updateOffsetFrequency();
}

void
DigitalChannelDialog::onPowerDefaultToggled(bool checked) {
  powerValue->setEnabled(!checked);
}

void
DigitalChannelDialog::onTimeoutDefaultToggled(bool checked) {
  totValue->setEnabled(!checked);
}

void
DigitalChannelDialog::onVOXDefaultToggled(bool checked) {
  voxValue->setEnabled(! checked);
}

void
DigitalChannelDialog::onHideChannelHint() {
  Settings settings;
  settings.setHideChannelNote(true);
  hintLabel->setVisible(false);
  layout()->invalidate();
  adjustSize();
}

void
DigitalChannelDialog::onTxFrequencyEdited() {
  _myChannel->setTXFrequency(Frequency::fromString(txFrequency->text()));
  txFrequency->setText(_myChannel->txFrequency().format());
  updateOffsetFrequency();
}

void
DigitalChannelDialog::onRxFrequencyEdited() {
  _myChannel->setRXFrequency(Frequency::fromString(rxFrequency->text()));
  rxFrequency->setText(_myChannel->rxFrequency().format());

  if (_myChannel->txFrequency().isZero()) {
    // If no previous txFrequency set, match rx frequency.
    _myChannel->setTXFrequency(Frequency::fromString(rxFrequency->text()));
    txFrequency->setText(_myChannel->txFrequency().format());
  }

  updateOffsetFrequency();
}

void
DigitalChannelDialog::onOffsetFrequencyEdited() {
  Frequency txFreq = _myChannel->rxFrequency();
  FrequencyOffset offsetFrequency = FrequencyOffset::fromString(offsetLineEdit->text()).abs();

  switch (offsetComboBox->currentIndex()) {
  case 0: txFreq = _myChannel->rxFrequency(); break;
  case 1: txFreq = _myChannel->rxFrequency() + offsetFrequency; break;
  case 2: txFreq = _myChannel->rxFrequency() + offsetFrequency.invert(); break;
  }

  _myChannel->setTXFrequency(txFreq);
  txFrequency->setText(txFreq.format());
  offsetLineEdit->setText(offsetFrequency.format());
}

void
DigitalChannelDialog::onOffsetCurrentIndexChanged(int index) {
  Frequency txFreq = _myChannel->rxFrequency();
  FrequencyOffset offsetFrequency = FrequencyOffset::fromString(offsetLineEdit->text()).abs();

  switch (index) {
  case 0:
    offsetLineEdit->setEnabled(false);
    txFreq = _myChannel->rxFrequency();
  break;
  case 1:
    offsetLineEdit->setEnabled(true);
    txFreq = _myChannel->rxFrequency() + offsetFrequency;
  break;
  case 2:
    offsetLineEdit->setEnabled(true);
    txFreq = _myChannel->rxFrequency() + offsetFrequency.invert();
  break;
  }

  _myChannel->setTXFrequency(txFreq);
  txFrequency->setText(txFreq.format());
}


void
DigitalChannelDialog::updateOffsetFrequency() {
  FrequencyOffset offsetFrequency = _myChannel->offsetFrequency();
  // Show absolute value
  offsetLineEdit->setText(offsetFrequency.abs().format());
  // Use combo box to indicate direction
  updateComboBox();
}

void
DigitalChannelDialog::updateComboBox() {
  switch (_myChannel->offsetShift()) {
  case Channel::OffsetShift::None:
    offsetComboBox->setCurrentIndex(0);
    offsetLineEdit->setEnabled(false);
  break;
  case Channel::OffsetShift::Positive:
    offsetComboBox->setCurrentIndex(1);
    offsetLineEdit->setEnabled(true);
  break;
  case Channel::OffsetShift::Negative:
    offsetComboBox->setCurrentIndex(2);
    offsetLineEdit->setEnabled(true);
  break;
  }
}
