#include "radio.hh"

#include "anytone_interface.hh"
#include "radioddity_interface.hh"
#include "tyt_interface.hh"
#include "dr1801uv_interface.hh"
#include "gd73_interface.hh"
#include "dm32uv_interface.hh"
#include "openrtx_interface.hh"

#include "rd5r.hh"
#include "gd73.hh"
#include "gd77.hh"
#include "md390.hh"
#include "uv390.hh"
#include "md2017.hh"
#include "dm1701.hh"
#include "dm32uv.hh"
#include "dr1801uv.hh"
#include "opengd77.hh"
#include "openuv380.hh"
#include "openrtx.hh"
#include "d868uv.hh"
#include "d878uv.hh"
#include "d878uv2.hh"
#include "d578uv.hh"
#include "d168uv.hh"
#include "dmr6x2uv.hh"
#include "dmr6x2uv2.hh"

#include "logger.hh"

#include <QSet>


/* ******************************************************************************************** *
 * Implementation of Radio
 * ******************************************************************************************** */
Radio::Radio(QObject *parent)
  : QThread(parent), _task(StatusIdle)
{
  // pass...
}

Radio::~Radio() {
  // pass...
}

const CallsignDB *
Radio::callsignDB() const {
  return nullptr;
}

CallsignDB *
Radio::callsignDB() {
  return nullptr;
}


Radio *
Radio::detect(const USBDeviceDescriptor &descr, const RadioInfo &force, const ErrorStack &err) {
  if (! descr.isValid()) {
    errMsg(err) << "Cannot detect radio: Invalid interface descriptor.";
    return nullptr;
  }
  logDebug() << "Try to detect radio at " << descr.description() << ".";

  if (AnytoneGD32Interface::interfaceInfo() == descr) {
    auto anytone = new AnytoneGD32Interface(descr, err);
    if (anytone->isOpen()) {
      RadioInfo id = anytone->identifier(err);
      if ((id.isValid() && (RadioInfo::D868UVE == id.id())) || (force.isValid() && (RadioInfo::D868UVE == force.id()))) {
        return new D868UV(anytone);
      } else if ((id.isValid() && (RadioInfo::D878UV == id.id())) || (force.isValid() && (RadioInfo::D878UV == force.id()))) {
        return new D878UV(anytone);
      } else if ((id.isValid() && (RadioInfo::D878UVII == id.id())) || (force.isValid() && (RadioInfo::D878UVII == force.id()))) {
        return new D878UV2(anytone);
      } else if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) {
        return new D578UV(anytone);
      } else if ((id.isValid() && (RadioInfo::DMR6X2UV == id.id())) || (force.isValid() && (RadioInfo::DMR6X2UV == force.id()))) {
        return new DMR6X2UV(anytone);
      } else if ((id.isValid() && (RadioInfo::DMR6X2UV2 == id.id())) || (force.isValid() && (RadioInfo::DMR6X2UV2 == force.id()))) {
        return new DMR6X2UV2(anytone);
      } else if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) {
        return new D578UV(anytone);
      } else if (id.isValid()) {
        errMsg(err) << tr("Unhandled device %1 '%2'. Device known but not implemented yet.")
                       .arg(id.manufacturer())
                       .arg(id.name());
      } else {
        errMsg(err) << tr("Unknown AnyTone (or similar) device.");
      }
      anytone->close();
      anytone->deleteLater();
      return nullptr;
    }
    anytone->deleteLater();
  } else if (AnytoneSTM32Interface::interfaceInfo() == descr) {
    auto anytone = new AnytoneSTM32Interface(descr, err);
    if (anytone->isOpen()) {
      RadioInfo id = anytone->identifier(err);
      if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) {
        return new D578UV(anytone);
      } else if ((id.isValid() && (RadioInfo::D168UV == id.id())) || (force.isValid() && (RadioInfo::D168UV == force.id()))) {
        return new D168UV(anytone);
      } else if (id.isValid()) {
        errMsg(err) << tr("Unhandled device %1 '%2'. Device known but not implemented yet.")
                       .arg(id.manufacturer())
                       .arg(id.name());
      } else {
        errMsg(err) << tr("Unknown AnyTone (or similar) device.");
      }
      anytone->close();
      anytone->deleteLater();
      return nullptr;
    }
    anytone->deleteLater();
  } else if (OpenGD77Interface::interfaceInfo() == descr) {
    OpenGD77Interface *ogd77 = new OpenGD77Interface(descr, err);
    if (ogd77->isOpen()) {
      RadioInfo id = ogd77->identifier();
      if ((id.isValid() && (RadioInfo::OpenGD77 == id.id())) || (force.isValid() && (RadioInfo::OpenGD77 == force.id()))) {
        return new OpenGD77(ogd77);
      } else if ((id.isValid() && (RadioInfo::OpenUV380 == id.id())) || (force.isValid() && (RadioInfo::OpenUV380 == force.id()))) {
        return new OpenUV380(ogd77);
      } else {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
                    << ". Device known but not implemented yet.";
      }
      ogd77->close();
      ogd77->deleteLater();
      return nullptr;
    }
    ogd77->deleteLater();
  } else if (OpenRTXInterface::interfaceInfo() == descr) {
    auto rtxIf = new OpenRTXInterface(descr, err);
    if (rtxIf->isOpen()) {
      RadioInfo id = rtxIf->identifier();
      if ((id.isValid() && (RadioInfo::OpenRTX == id.id())) || (force.isValid() && (RadioInfo::OpenRTX == force.id()))) {
        logInfo() << "Yah!";
        return nullptr; //new OpenRTX(rtxIf);
      } else {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
        << ". Device known but not implemented yet.";
      }
      rtxIf->close();
      rtxIf->deleteLater();
      return nullptr;
    }
    rtxIf->deleteLater();
  } else if (TyTInterface::interfaceInfo() == descr) {
    TyTInterface *dfu = new TyTInterface(descr, err);
    if (dfu->isOpen()) {
      RadioInfo id = dfu->identifier();
      if ((id.isValid() && (RadioInfo::MD390 == id.id())) || (force.isValid() && (RadioInfo::MD390 == force.id()))) {
        return new MD390(dfu);
      } else if ((id.isValid() && (RadioInfo::UV390 == id.id())) || (force.isValid() && (RadioInfo::UV390 == force.id()))) {
        return new UV390(dfu);
      } else if ((id.isValid() && (RadioInfo::MD2017 == id.id())) || (force.isValid() && (RadioInfo::MD2017 == force.id()))) {
        return new MD2017(dfu);
      } else if ((id.isValid() && (RadioInfo::DM1701 == id.id())) || (force.isValid() && (RadioInfo::DM1701 == force.id()))) {
        logDebug() << "Create DM-1701 radio object.";
        return new DM1701(dfu);
      } else {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
                    << ". Device known but not implemented yet.";
      }
      dfu->close();
      dfu->deleteLater();
      return nullptr;
    }
    dfu->deleteLater();
  } else if (RadioddityInterface::interfaceInfo() == descr) {
    RadioddityInterface *hid = new RadioddityInterface(descr, err);
    if (hid->isOpen()) {
      RadioInfo id = hid->identifier();
      if ((id.isValid() && (RadioInfo::RD5R == id.id())) || (force.isValid() && (RadioInfo::RD5R == force.id()))) {
        return new RD5R(hid);
      } else if ((id.isValid() && (RadioInfo::GD77 == id.id())) || (force.isValid() && (RadioInfo::GD77 == force.id()))) {
        return new GD77(hid);
      } else if (id.isValid()) {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
                    << ". Device known but not implemented yet.";
      } else {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
                    << ". Device not known.";
      }
      hid->close();
      hid->deleteLater();
      return nullptr;
    }
    hid->deleteLater();
  } else if ((DR1801UVInterface::interfaceInfo() == descr) && force.isValid() && (RadioInfo::DR1801UV==force.id())){
    DR1801UVInterface *dif = new DR1801UVInterface(descr, err);
    if (dif->isOpen()) {
      RadioInfo id = dif->identifier(err);
      if (id.isValid() && (RadioInfo::DR1801UV == id.id())) {
        return new DR1801UV(dif);
      } else if (id.isValid()) {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
                    << ". Device known but not implemented yet.";
      } else {
        errMsg(err) << "Unknown device or failed connection to the device.";
      }
      dif->close();
      dif->deleteLater();
      return nullptr;
    }
    dif->deleteLater();
  } else if (C7000Device::interfaceInfo() == descr) {
    GD73Interface *gdif = new GD73Interface(descr, err);
    if (gdif->isOpen()) {
      RadioInfo id = gdif->identifier();
      if ((id.isValid() && (RadioInfo::GD73 == id.id())) ||
          (force.isValid() && (RadioInfo::GD73 == force.id()))) {
        return new GD73(gdif);
      } else {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
                    << ". Device known but not implemented yet.";
      }
      gdif->close();
      gdif->deleteLater();
      return nullptr;
    }
    gdif->deleteLater();
  } else if ((DM32UVInterface::interfaceInfo() == descr) && force.isValid() && (RadioInfo::DM32UV==force.id())) {
    DM32UVInterface *dm32if = new DM32UVInterface(descr, err);
    if (dm32if->isOpen()) {
      RadioInfo id = dm32if->identifier(err);
      if (! id.isValid()) {
        errMsg(err) << "Cannot identify device.";
      } else if (RadioInfo::DM32UV == id.id()) {
        return new DM32UV(dm32if);
      } else {
        errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name()
        << ". Device known but not implemented yet.";
      }
      dm32if->close();
      dm32if->deleteLater();
      return nullptr;
    }
    dm32if->deleteLater();
    return nullptr;
  }
  return nullptr;
}

Radio::Status
Radio::status() const {
  return _task;
}

const ErrorStack &
Radio::errorStack() const {
  return _errorStack;
}
