/** Copyright (c) 2020-2022 The Creators of Simphone

    class SeedGen (LoginDialog): display audio entropy collection progress

    See the file COPYING.LESSER.txt for copying permission.
**/

#include "seedgen.h"
#include "ui_seedgen.h"

#include "contacts.h"
#include "chatframe.h"
#include "qtfix.h"

SeedGen::SeedGen(QWidget * parent, QEvent * event, int bits, int type)
  : Parent(parent), ui(new Ui::SeedGen), m_err(SIM_OK), m_type(type), m_bits(bits), m_started(false), m_event(event)
{
  ui->setupUi(this);
  initLanguage(ui->actionEnglish, ui->actionFrench, ui->actionGerman);
  ui->okButton->setText(qApp->translate("QDialogButtonBox", "OK"));
  ui->menubar->adjustSize();
  ui->menubar->resize(width(), ui->menubar->height());
  qtfix::fixPointSize(ui->listenLabel, font().pointSize());
  qtfix::fixPointSize(ui->talkLabel, font().pointSize());
  qtfix::fixPointSize(ui->okButton, font().pointSize(), 15);

  connect(SimCore::get(), SIGNAL(signalSpeech(unsigned, int, int, int)),
          this, SLOT(onSignalSpeech(unsigned, int, int, int)));
  connect(SimCore::get(), SIGNAL(signalContactAudioChanged(unsigned, SimAudioState)),
          this, SLOT(onSignalContactAudioChanged(unsigned, SimAudioState)));
  m_collector = new MouseEntropyCollector(this);
  setWindowFlags(Qt::MSWindowsFixedSizeDialogHint);

  ui->label->setPixmap(QPixmap(":/welcome") /*.scaledToWidth(width() - 80, Qt::SmoothTransformation)*/);
  ui->okButton->setVisible(false);
  adjustToBiggerSize();
  ui->talkLabel->setVisible(false); // after adjustSize call so extra space for the OTHER text will be left
  ui->listenLabel->setVisible(true);
}

SeedGen::~SeedGen()
{
  delete m_collector;
  delete m_event;
  delete ui;
  //log_note_("ui", "%s\n", __FUNCTION__);
}

void SeedGen::generate()
{
  QByteArray bytes = m_collector->stop();
  simtype seed = sim_nil();
  int simres = SIM_API_BAD_TYPE;

  m_collector->clearEntropy();
  if (m_bits) {
    simtype entropy = sim_pointer_new_length(bytes.data(), bytes.size());
    simres = sim_key_generate_(&seed, entropy, m_bits < 0 ? 0 : m_bits, m_type);
    if (m_bits < 0 && simres == SIM_OK && m_event) {
      QCoreApplication::postEvent(SimCore::get(), m_event);
      m_event = 0;
    }
  }
  bytes.fill(0);

  if (simres != SIM_OK) {
    QString simerr = SimCore::getError(simres);
    if (simres == SIM_KEY_NO_ENTROPY) {
      if (m_err != SIM_OK) simerr = SimCore::getErrorBuffer(m_err);
      simerr.append(tr("<br/><br/>Simphone did not hear you talking; something is wrong with your audio recording"
                       " settings. <span style=\" color:#ff0000;\"><b>Check that your microphone is working,"
                       " connected to your audio device, and not muted.</b></span> If you have multiple audio devices,"
                       " you can choose the one to use from the \"Login\" menu."));
      QString qs = "<p><b>";
      qs.append(tr("Key generation not successful (%1)").arg(m_err).toHtmlEscaped().replace(" ", "&nbsp;"));
      QMessageBox::critical(this, tr("Simphone error"), qs.append("</b></p><p>").append(simerr).append("</p>"));
    } else {
      SimCore::execMessageBox(true, tr("Key generation not successful (%1)").arg(simres), simerr);
    }
    m_seed[1] = m_seed[0] = QString();
  } else {
    const char * s = sim_get_pointer(seed);
    if (s) {
      m_seed[0] = QString::fromUtf8(s);
      while (*s++) {}
      m_seed[1] = QString::fromUtf8(s);
    } else {
      m_seed[1] = m_seed[0] = "";
    }
  }
  sim_string_free(seed);
}

void SeedGen::closeEvent(QCloseEvent * event)
{
  //log_note_("ui", "%s\n", __FUNCTION__);
  if (ui->okButton->isVisible()) generate();
  Parent::closeEvent(event);
}

void SeedGen::changeEvent(QEvent * event)
{
  if (event->type() == QEvent::LanguageChange) {
    ui->retranslateUi(this);
    ui->label->setPixmap(QPixmap(":/welcome"));
    ui->okButton->setText(qApp->translate("QDialogButtonBox", "OK"));
  }
  Parent::changeEvent(event);
}

void SeedGen::showEvent(QShowEvent * event)
{
  Parent::showEvent(event);
  if (!m_started) {
    SimParam::set("crypto.entropy", m_bits, true);

    int simres = sim_audio_call_(CONTACT_ID_KEYGEN);
    if (simres == SIM_OK) {
      setWindowTitle(tr("Generating %1-bit secret key").arg(abs(m_bits)));
      m_started = true;
    } else {
      QString simerr = SimCore::getError(simres);
      simerr.append(tr("\n\nMake sure that your audio device is connected to the computer and not disabled."
                       " If you have multiple audio devices, you can choose the one to use from the \"Login\" menu."));
      SimCore::execMessageBox(true, tr("Opening audio device not successful (%1)").arg(simres), simerr);
      // cannot close directly because this is within the dialog constructor
      QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
    }
  }
}

void SeedGen::on_okButton_clicked()
{
  generate();
  Parent::accept();
}

void SeedGen::on_actionConsole_triggered()
{
  Contact * c = SimCore::getContact(SimCore::get()->getTestContactId());
  if (!c || !c->m_chatFrame || !c->m_chatFrame->isVisible()) {
    ConsoleDialog console(0, false);
    if (console.exec() < 0) done(-1);
  }
}

void SeedGen::on_actionEnglish_triggered()
{
  Parent::on_actionEnglish_triggered(ui->actionEnglish, ui->actionFrench, ui->actionGerman);
}

void SeedGen::on_actionFrench_triggered()
{
  Parent::on_actionFrench_triggered(ui->actionEnglish, ui->actionFrench, ui->actionGerman);
}

void SeedGen::on_actionGerman_triggered()
{
  Parent::on_actionGerman_triggered(ui->actionEnglish, ui->actionFrench, ui->actionGerman);
}

void SeedGen::onSignalSpeech(unsigned, int error, int probability, int percent)
{
  ui->progressBar->setValue(percent);
  //log_debug_("ui", "probability %d, precent %d\n", probability, percent);
  if (probability == SIM_EVENT_SPEECH_END) {
    Contact * c = SimCore::getContact(SimCore::get()->getTestContactId());

    m_err = error;
    //if (c && c->m_chatFrame) emit c->m_chatFrame->signalContactQuit(); // do not auto close console
    if ((mc_modal || (c && c->m_chatFrame && c->m_chatFrame->isVisible())) && m_bits < 0) {
      ui->okButton->setVisible(true);
    } else {
      on_okButton_clicked();
    }
  } else {
    ui->listenLabel->setVisible(false);
    ui->talkLabel->setVisible(true);
  }
}

void SeedGen::onSignalContactAudioChanged(unsigned, SimAudioState newState)
{
  if (newState == audio_talk) m_collector->start();
}
