#include "mainwindow.h"
#include "communication/messages.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QtDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);

  // Add scale image items
  QStringList scales = {"20%",       "50%",        "100%",     "200%",
                        "Arbitrary", "Fit Window", "Fit Width"};
  ui->imageScaleSelect->addItems(scales);
  ui->imageScaleSelect->setCurrentIndex(2);
  QStringList lineSelectModes = {"App", "MCU"};
  ui->lineSelectModeBox->addItems(lineSelectModes);
  ui->lineSelectModeBox->setCurrentIndex(0);

  //-------------------------------------------------------------------------//
  //------------------------ init all components ----------------------------//
  //-------------------------------------------------------------------------//
  this->pert = new GUIpertinency(ui);
  this->decoder = new Decoder();
  this->port = new PortController(this->decoder);
  this->sensor = new CMOS();
  this->regsWindow = new setRegsWindow(this);
  this->roiWindow = new ROIWindow(this);
  this->about = new About(this);
  this->img = new ImageWorker();
  this->pendingControler = new Pending(ui, this->roiWindow->ui, this->sensor);
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  // load ports
  this->loadPorts();

  //-------------------------------------------------------------------------//
  //--------------------------- GUI signals ---------------------------------//
  //-------------------------------------------------------------------------//
  // connect connection signals
  connect(ui->refreshButton, SIGNAL(clicked()), this, SLOT(loadPorts()));
  connect(ui->connectButton, SIGNAL(clicked()), this, SLOT(openPort()));
  connect(ui->disconnectButton, SIGNAL(clicked()), this, SLOT(closePort()));

  // connect capture signals
  connect(ui->snapshotButton, SIGNAL(clicked()), this, SLOT(snapShot()));
  connect(ui->playButton, SIGNAL(clicked()), this, SLOT(startContinuos()));
  connect(ui->stopButton, SIGNAL(clicked()), this, SLOT(stopContinuos()));

  // connect image view signals
  connect(ui->imageScaleSelect, SIGNAL(currentIndexChanged(int)), this,
          SLOT(scaleChanged(int)));
  connect(ui->imageScaleArb, SIGNAL(valueChanged(int)), this,
          SLOT(arbScaleChanged(int)));
  connect(ui->imageView, SIGNAL(wheelEvent(int)), this,
          SLOT(catchWheelEvent(int)));
  connect(ui->wholeImageView, SIGNAL(wheelEvent(int)), this,
          SLOT(catchWheelEvent(int)));
  connect(ui->imageView, SIGNAL(resized()), this, SLOT(imageViewResize()));
  connect(ui->wholeImageView, SIGNAL(resized()), this,
          SLOT(imageViewLResize()));

  // connect save image signals
  connect(ui->saveImage, SIGNAL(triggered()), this, SLOT(saveImage()));
  connect(ui->actionSave_Line, SIGNAL(triggered()), this,
          SLOT(saveImageLine()));
  connect(ui->actionSave_Line_Data, SIGNAL(triggered()), this,
          SLOT(saveLineData()));

  // connect save CMOS setting signal
  connect(ui->saveCMOSsettings, SIGNAL(triggered()), this, SLOT(saveCMOS()));
  connect(ui->loadCMOSsettings, SIGNAL(triggered()), this, SLOT(loadCMOS()));

  // connect setRegisters signals
  connect(ui->setRegsButton, SIGNAL(clicked()), this, SLOT(setRegs()));

  // connect set data signals
  connect(ui->setAECButton, SIGNAL(clicked()), this, SLOT(sendAEC()));
  connect(ui->setAGCButton, SIGNAL(clicked()), this, SLOT(sendAGC()));
  connect(ui->setHDRButton, SIGNAL(clicked()), this, SLOT(sendHDR()));
  connect(ui->setBrightnessButton, SIGNAL(clicked()), this, SLOT(sendBright()));
  connect(ui->setGainButton, SIGNAL(clicked()), this, SLOT(sendGain()));
  connect(ui->setExposureButton, SIGNAL(clicked()), this, SLOT(sendExposure()));
  connect(ui->setBinningButton, SIGNAL(clicked()), this, SLOT(sendBin()));
  connect(ui->setFlipButton, SIGNAL(clicked()), this, SLOT(sendFlip()));
  connect(ui->setROIButton, SIGNAL(clicked()), this, SLOT(setROIShow()));
  connect(ui->setAllButton, SIGNAL(clicked()), this, SLOT(setAll()));

  connect(ui->defaultSettButton, SIGNAL(clicked()), this,
          SLOT(setDefaultSett()));

  // connect refreshed data signals
  connect(ui->refreshAEC, SIGNAL(clicked()), this->sensor, SLOT(refreshAEC()));
  connect(ui->refreshAGC, SIGNAL(clicked()), this->sensor, SLOT(refreshAGC()));
  connect(ui->refreshHDR, SIGNAL(clicked()), this->sensor, SLOT(refreshHDR()));
  connect(ui->refreshBright, SIGNAL(clicked()), this->sensor,
          SLOT(refreshBright()));
  connect(ui->refreshGain, SIGNAL(clicked()), this->sensor,
          SLOT(refreshGain()));
  connect(ui->refreshExposure, SIGNAL(clicked()), this->sensor,
          SLOT(refreshExposure()));
  connect(ui->refreshBinning, SIGNAL(clicked()), this, SLOT(refreshBinSkip()));
  connect(this, SIGNAL(refreshBinning()), this->sensor, SLOT(refreshBinning()));
  connect(this, SIGNAL(refreshSkip()), this->sensor, SLOT(refreshSkip()));
  connect(ui->refreshFlip, SIGNAL(clicked()), this->sensor,
          SLOT(refreshFlip()));
  connect(ui->refreshROI, SIGNAL(clicked()), this->sensor, SLOT(refreshROI()));

  // connect set test modes signals
  connect(ui->actionTestMode, SIGNAL(triggered()), this, SLOT(sendTest()));
  connect(ui->actionNextTest, SIGNAL(triggered()), this, SLOT(sendTest()));

  // connect speed test signals
  connect(ui->actionSpeedTest, SIGNAL(triggered()), this, SLOT(speedTest()));

  // connect signal for gain spin (special object)
  connect(ui->gainSpin, SIGNAL(valueChanged(bool)), this,
          SLOT(onGainChange(bool)));

  // Binning or Skip change
  connect(ui->binningLabel, SIGNAL(currentIndexChanged(QString)), this,
          SLOT(binSkipSelect(QString)));

  // About window
  connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAbout()));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  //-------------------------------------------------------------------------//
  //------------------------ decoder signals --------------------------------//
  //-------------------------------------------------------------------------//
  // image data decoder -> imageView
  connect(this->decoder, SIGNAL(imageData(Image *)), this->img,
          SLOT(catchImg(Image *)));
  // sensor data decoder -> sensor
  connect(this->decoder, SIGNAL(sensorData(uint8_t, QByteArray &)),
          this->sensor, SLOT(processSensorData(uint8_t, QByteArray &)));
  // register data -> registers window
  connect(this->decoder, SIGNAL(registerData(uint8_t, uint16_t)),
          this->regsWindow, SLOT(registerData(uint8_t, uint16_t)));
  connect(this, SIGNAL(clearAllDecoder(void)), this->decoder, SLOT(clearAll()));
  // fps
  connect(this->decoder, SIGNAL(fps(double)), this, SLOT(catchFPS(double)));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  //-------------------------------------------------------------------------//
  //-------------------------- image signals --------------------------------//
  //-------------------------------------------------------------------------//
  // finnished image imageView -> show image
  connect(this->img, SIGNAL(showImg(const QPixmap &)), this->ui->imageView,
          SLOT(setPixmap(const QPixmap &)));
  connect(this->img, SIGNAL(showImgL(const QPixmap &)),
          this->ui->wholeImageView, SLOT(setPixmap(const QPixmap &)));
  connect(this->img, SIGNAL(showImgLine(const QPixmap &)), this->ui->lineView,
          SLOT(setPixmap(const QPixmap &)));
  // reDraw request MainWindow -> imageWorker
  connect(this, SIGNAL(requestRedrawAreaMode()), this->img,
          SLOT(reDrawAreaMode()));
  connect(this, SIGNAL(requestRedrawLineArea()), this->img,
          SLOT(reDrawLineArea()));
  connect(this, SIGNAL(requestRedrawLine()), this->img, SLOT(reDrawLine()));
  // set viewScale MainWindow -> imageWorker
  connect(this, SIGNAL(requestSetViewScale(double)), this->img,
          SLOT(setViewScale(double)));
  // set viewScale MainWindow -> imageWorker
  connect(this, SIGNAL(requestSetViewScaleL(double)), this->img,
          SLOT(setViewScaleL(double)));
  // update viewScale MainWindow -> imageWorker
  connect(this, SIGNAL(requsetUpdateViewScale(double)), this->img,
          SLOT(updateViewScale(double)));
  // do Bayer mask interpolation change state
  connect(ui->interpolateCheck, SIGNAL(stateChanged(int)), this->img,
          SLOT(setInterpolateState(int)));
  // show graph
  connect(this->img, SIGNAL(showGraph(Image &, uint32_t)), ui->graph,
          SLOT(catchLine(Image &, uint32_t)));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  //-------------------------------------------------------------------------//
  //------------------------ decoder signals ------------------------------- //
  //-------------------------------------------------------------------------//
  // open status port -> mainWindow
  connect(this->port, SIGNAL(openStatus(bool, bool)), this,
          SLOT(portStatus(bool, bool)));
  // open status port -> registersWindow
  connect(this->port, SIGNAL(openStatus(bool, bool)), this->regsWindow,
          SLOT(portStatus(bool, bool)));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  //-------------------------------------------------------------------------//
  //------------------------- sensor signals ------------------------------- //
  //-------------------------------------------------------------------------//
  // sensor data update sensor -> mainWindow
  connect(this->sensor, SIGNAL(sensorDataUpdated(uint8_t)), this,
          SLOT(catchSensorUpdate(uint8_t)));
  // sensor data update sensor -> roiWindow
  connect(this->sensor, SIGNAL(sensorDataUpdated(uint8_t)), this->roiWindow,
          SLOT(catchSensorUpdate(uint8_t)));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  //-------------------------------------------------------------------------//
  //----------------------- roi window signals ------------------------------//
  //-------------------------------------------------------------------------//
  // aligned data roiWindow -> mainWindow (to show in status bar)
  connect(this->roiWindow, SIGNAL(aligned(void)), this,
          SLOT(alignedSize(void)));
  // preview state update roiWindow -> imageWorker
  connect(this->roiWindow, SIGNAL(previewState(bool)), this->img,
          SLOT(setPreviewState(bool)));
  // roi window update roiWindow -> imageWorker
  connect(this->roiWindow, SIGNAL(roiWindow(QRect)), this->img,
          SLOT(setROIPreview(QRect)));
  // reDraw request MainWindow -> imageWorker
  connect(this->roiWindow, SIGNAL(requestRedraw()), this->img,
          SLOT(reDrawAreaMode()));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//

  //-------------------------------------------------------------------------//
  //----------------------- linear mode signals -----------------------------//
  //-------------------------------------------------------------------------//
  // line select mode
  connect(ui->lineSelectModeBox, SIGNAL(currentIndexChanged(int)), this,
          SLOT(lineModeSelectChanged(int)));
  connect(ui->setLineSelectMode, SIGNAL(clicked()), this,
          SLOT(setLineSelectMode()));
  // select line
  connect(ui->selectLine, SIGNAL(clicked()), this, SLOT(setLine()));
  // rescale graph
  connect(ui->homeGraph, SIGNAL(clicked()), ui->graph, SLOT(home()));
  // pending line
  connect(ui->selectedLineSpin, SIGNAL(valueChanged(int)), this,
          SLOT(lineSelectChanged(int)));

  // cursors enable
  connect(ui->cursor1enable, SIGNAL(stateChanged(int)), ui->graph,
          SLOT(cursor1Enabled(int)));
  connect(ui->cursor1enable, SIGNAL(stateChanged(int)), this->img,
          SLOT(cursor1Enabled(int)));
  connect(ui->cursor2enable, SIGNAL(stateChanged(int)), ui->graph,
          SLOT(cursor2Enabled(int)));
  connect(ui->cursor2enable, SIGNAL(stateChanged(int)), this->img,
          SLOT(cursor2Enabled(int)));

  // cursors position
  connect(ui->cursor1x, SIGNAL(valueChanged(int)), ui->graph,
          SLOT(cursor1x(int)));
  connect(ui->cursor1x, SIGNAL(valueChanged(int)), this->img,
          SLOT(cursor1x(int)));
  connect(ui->cursor2x, SIGNAL(valueChanged(int)), ui->graph,
          SLOT(cursor2x(int)));
  connect(ui->cursor2x, SIGNAL(valueChanged(int)), this->img,
          SLOT(cursor2x(int)));

  // cursors values
  connect(this->img, SIGNAL(cursor1y(const QString &)), ui->cursor1y,
          SLOT(setText(const QString &)));
  connect(this->img, SIGNAL(cursor2y(const QString &)), ui->cursor2y,
          SLOT(setText(const QString &)));
  connect(this->img, SIGNAL(y1my2(const QString &)), ui->y1my2,
          SLOT(setText(const QString &)));
  connect(this->img, SIGNAL(x1mx2(const QString &)), ui->x1mx2,
          SLOT(setText(const QString &)));
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
  //-------------------------------------------------------------------------//
}

MainWindow::~MainWindow() {
  delete this->decoder;
  delete this->port;
  delete this->sensor;
  delete this->regsWindow;
  delete this->roiWindow;
  delete this->img;
  delete this->pendingControler;
  delete this->pert;
  delete ui;
}

void MainWindow::loadPorts() {
  this->ui->deviceList->clear();
  QList<QString> list = this->port->getAvailablePorts();
  foreach (QString p, list) { this->ui->deviceList->addItem(p); }
  this->ui->statusbar->showMessage("Devices refreshed.", 3000);
}

void MainWindow::openPort() {
  this->port->openPort(ui->deviceList->currentText());
}

void MainWindow::closePort() { this->port->stopPort(); }

void MainWindow::portStatus(bool stat, bool previousStat) {
  if (stat && !previousStat) {
    // Opened
    this->pert->onConnectionChange(true);
    ui->statusbar->showMessage("Connection established.", 3000);
  } else if (!stat && !previousStat) {
    ui->statusbar->showMessage("Oups can not connect to selected device.",
                               3000);
  } else if (!stat && previousStat) {
    // Closed
    ui->statusbar->showMessage("Device disconnected.", 3000);
    this->pert->onConnectionChange(false);
    ui->sensorTypeLabel->clear();
    this->img->setPreviewState(false);
    emit requestRedrawAreaMode();
    emit clearAllDecoder();
  } else {
    ui->statusbar->showMessage(
        "Oups something went wrong during disconnection.", 3000);
  }
}

void MainWindow::snapShot() { this->port->send(getImage); }

void MainWindow::startContinuos() {
  this->img->setOnline(true);
  this->fps.clear();
  this->pert->onContinuosTransChange(true);
  this->port->send(startImages);
}

void MainWindow::stopContinuos() {
  this->img->setOnline(false);
  this->port->send(stopImages);
  this->pert->onContinuosTransChange(false);
}

double MainWindow::computeFitScale(bool area) {
  if (area) {
    int viewWidth = this->ui->scrollArea->width() - 30;
    int imgWidth = this->sensor->getImg().width;
    double wScale = static_cast<double>(viewWidth) / imgWidth;
    int viewHeight = this->ui->scrollArea->height() - 30;
    int imgHeight = this->sensor->getImg().height;
    double hScale = static_cast<double>(viewHeight) / imgHeight;
    return wScale < hScale ? wScale : hScale;
  } else {
    int viewWidth = this->ui->scrollArea_2->width() - 30;
    int imgWidth = this->sensor->getImg().width;
    double wScale = static_cast<double>(viewWidth) / imgWidth;
    int viewHeight = this->ui->scrollArea_2->height() - 30;
    int imgHeight = this->sensor->getImg().height;
    double hScale = static_cast<double>(viewHeight) / imgHeight;
    return wScale < hScale ? wScale : hScale;
  }
}

double MainWindow::computeFitWidthScale(bool area) {
  if (area) {
    int viewWidth = this->ui->scrollArea->width() - 40;
    int imgWidth = this->sensor->getImg().width;
    double wScale = static_cast<double>(viewWidth) / imgWidth;
    return wScale;
  } else {
    int viewWidth = this->ui->scrollArea_2->width() - 40;
    int imgWidth = this->sensor->getImg().width;
    double wScale = static_cast<double>(viewWidth) / imgWidth;
    return wScale;
  }
}

void MainWindow::scaleChanged(int index) {
  switch (index) {
  case 0:
    emit requestSetViewScale(0.2);
    emit requestSetViewScaleL(0.2);
    break;
  case 1:
    emit requestSetViewScale(0.5);
    emit requestSetViewScaleL(0.5);
    break;
  case 2:
    emit requestSetViewScale(1);
    emit requestSetViewScaleL(1);
    break;
  case 3:
    emit requestSetViewScale(2);
    emit requestSetViewScaleL(2);
    break;
  case 4:
    emit requestSetViewScale(this->ui->imageScaleArb->value() / 100);
    emit requestSetViewScaleL(this->ui->imageScaleArb->value() / 100);
    break;
  case 5: {
    if (!this->ui->imageSize->text().isEmpty()) {
      emit requestSetViewScale(this->computeFitScale(true));
      emit requestSetViewScaleL(this->computeFitScale(false));
    }
    break;
  }
  case 6: {
    if (!this->ui->imageSize->text().isEmpty()) {
      emit requestSetViewScale(this->computeFitWidthScale(true));
      emit requestSetViewScaleL(this->computeFitWidthScale(false));
    }
    break;
  }
  default:
    emit requestSetViewScale(1);
    emit requestSetViewScaleL(1);
    break;
  }
  this->pert->onScaleChange();
  emit requestRedrawAreaMode();
  emit requestRedrawLineArea();
}

void MainWindow::arbScaleChanged(int scale) {
  emit requestSetViewScale(static_cast<double>(scale) / 100);
  emit requestSetViewScaleL(static_cast<double>(scale) / 100);
  emit requestRedrawAreaMode();
  emit requestRedrawLineArea();
}

void MainWindow::catchWheelEvent(int scroll) {
  if (this->ui->imageScaleSelect->currentIndex() == 4 &&
      QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
    if (scroll > 0) {
      emit requsetUpdateViewScale(0.01);
      this->ui->imageScaleArb->setValue(this->ui->imageScaleArb->value() + 1);
    } else if (this->ui->imageScaleArb->value() > 1) {
      emit requsetUpdateViewScale(-0.01);
      this->ui->imageScaleArb->setValue(this->ui->imageScaleArb->value() - 1);
    }
    emit requestRedrawAreaMode();
    emit requestRedrawLineArea();
  }
}

void MainWindow::imageViewResize() {
  if (this->ui->imageScaleSelect->currentIndex() == 5) {
    emit requestSetViewScale(this->computeFitScale(true));
    emit requestRedrawAreaMode();
    emit requestRedrawLineArea();
  } else if (this->ui->imageScaleSelect->currentIndex() == 6) {
    emit requestSetViewScale(this->computeFitWidthScale(true));
    emit requestRedrawAreaMode();
    emit requestRedrawLineArea();
  }
}

void MainWindow::imageViewLResize() {
  if (this->ui->imageScaleSelect->currentIndex() == 5) {
    emit requestSetViewScaleL(this->computeFitScale(false));
    emit requestRedrawAreaMode();
    emit requestRedrawLineArea();
  } else if (this->ui->imageScaleSelect->currentIndex() == 6) {
    emit requestSetViewScaleL(this->computeFitWidthScale(false));
    emit requestRedrawAreaMode();
    emit requestRedrawLineArea();
  }
}

void MainWindow::catchSensorUpdate(uint8_t code) {
  switch (code) {
  case 0:
    this->ui->sensorTypeLabel->setText(this->sensor->getSensorName());
    break;
  case 1:
    switch (this->sensor->getShutterType()) {
    case global:
      this->ui->shutterType->setText("Global shutter");
      break;
    case rolling:
      this->ui->shutterType->setText("Rolling shutter");
      break;
    }
    break;
  case 2:
    this->pert->enableAEC(this->sensor->getCapabilities().aec);
    this->pert->enableAGC(this->sensor->getCapabilities().agc);
    this->pert->enableHDR(this->sensor->getCapabilities().hdr);
    this->ui->binningLabel->clear();
    if (this->sensor->getCapabilities().bin) {
      this->ui->binningLabel->addItem("Binning");
    }
    if (this->sensor->getCapabilities().skip) {
      this->ui->binningLabel->addItem("Skip");
    }
    this->pert->enableBinning(this->sensor->getCapabilities().bin ||
                              this->sensor->getCapabilities().skip);
    this->pert->enableFlip(this->sensor->getCapabilities().flip);
    this->pert->enableROI(this->sensor->getCapabilities().roi);
    this->pert->enableBrightness(this->sensor->getCapabilities().aec ||
                                 this->sensor->getCapabilities().agc);
    this->pert->enableTest(this->sensor->getCapabilities().test);
    this->ui->interpolateCheck->setEnabled(this->sensor->getCapabilities().rgb);
    if (this->sensor->getCapabilities().rgb) {
      this->ui->interpolateCheck->setChecked(true);
      this->img->setInterpolate(true);
    }
    break;
  case 5:
    this->ui->binColBox->clear();
    this->ui->binRowBox->clear();
    for (uint32_t i = 0; i < this->sensor->getLimits().binSteps.size(); i++) {
      this->ui->binColBox->addItem(
          QString::number(this->sensor->getLimits().binSteps.at(i)));
      this->ui->binRowBox->addItem(
          QString::number(this->sensor->getLimits().binSteps.at(i)));
    }
    break;
  case 6:
    this->ui->binColBox->clear();
    this->ui->binRowBox->clear();
    for (uint32_t i = 0; i < this->sensor->getLimits().skipSteps.size(); i++) {
      this->ui->binColBox->addItem(
          QString::number(this->sensor->getLimits().skipSteps.at(i)));
      this->ui->binRowBox->addItem(
          QString::number(this->sensor->getLimits().skipSteps.at(i)));
    }
    break;
  case 7: {
    this->ui->gainSpin->setRange(static_cast<double>(this->sensor->gainToReal(
                                     this->sensor->getLimits().minGain)),
                                 static_cast<double>(this->sensor->gainToReal(
                                     this->sensor->getLimits().maxGain)));
    break;
  }
  case 8:
    this->ui->brightnessSpin->setRange(this->sensor->getLimits().minBright,
                                       this->sensor->getLimits().maxBright);
    break;
  case 9: {
    QString size = QString::number(this->sensor->getImg().width);
    size.append("x");
    size.append(QString::number(this->sensor->getImg().height));
    this->ui->imageSize->setText(size);
    if (this->ui->imageScaleSelect->currentIndex() == 5) {
      emit requestSetViewScale(this->computeFitScale(true));
      emit requestSetViewScaleL(this->computeFitScale(false));
    } else if (this->ui->imageScaleSelect->currentIndex() == 6) {
      emit requestSetViewScale(this->computeFitWidthScale(true));
      emit requestSetViewScaleL(this->computeFitWidthScale(false));
    }
    this->ui->cursor1x->setMaximum(this->sensor->getImg().width - 1);
    this->ui->cursor2x->setMaximum(this->sensor->getImg().width - 1);
    break;
  }
  case 10: {
    QString size = QString::number(this->sensor->getImg().width);
    size.append("x");
    size.append(QString::number(this->sensor->getImg().height));
    this->ui->imageSize->setText(size);
    if (this->ui->imageScaleSelect->currentIndex() == 5) {
      emit requestSetViewScale(this->computeFitScale(true));
      emit requestSetViewScaleL(this->computeFitScale(false));
    } else if (this->ui->imageScaleSelect->currentIndex() == 6) {
      emit requestSetViewScale(this->computeFitWidthScale(true));
      emit requestSetViewScale(this->computeFitWidthScale(false));
    }
    if (this->ui->lineSelectModeBox->currentIndex() == 0) {
      this->ui->selectedLineSpin->setMaximum(this->sensor->getImg().height - 1);
      this->img->setLine(0);
      this->img->setPendingLine(0);
      this->ui->selectedLineSpin->setValue(0);
    }
    break;
  }
  case 11:
    this->ui->imageColor->setText(this->sensor->getImg().rgb ? "RGB" : "Gray");
    if (this->sensor->getImg().rgb) {
      this->ui->setLineSelectMode->setEnabled(false);
    }
    break;
  case 13:
    this->pendingControler->setPending(this->roiWindow->ui->widthSpin, false);
    this->pendingControler->setPending(this->roiWindow->ui->heightSpin, false);
    this->pendingControler->setPending(this->roiWindow->ui->startXSpin, false);
    this->pendingControler->setPending(this->roiWindow->ui->startYSpin, false);
    this->pendingControler->setPending(this->ui->setROIButton, false);
    this->pendingControler->setROIpendingCount(0);
    break;
  case 14:
    this->ui->AECCheck->setChecked(this->sensor->getAEC());
    this->pert->enableBrightness(this->sensor->getAEC() ||
                                 this->sensor->getAGC());
    this->pert->enableExposure(!this->sensor->getAEC());
    this->pendingControler->setPending(this->ui->AECCheck, false);
    break;
  case 15:
    this->ui->AGCCheck->setChecked(this->sensor->getAGC());
    this->pert->enableBrightness(this->sensor->getAEC() ||
                                 this->sensor->getAGC());
    this->pert->enableGain(!this->sensor->getAGC());
    this->pendingControler->setPending(this->ui->AGCCheck, false);
    break;
  case 16:
    this->ui->HDRCheck->setChecked(this->sensor->getHDR());
    this->pendingControler->setPending(this->ui->HDRCheck, false);
    break;
  case 17:
    this->ui->brightnessSpin->setValue(this->sensor->getBrightness());
    this->pendingControler->setPending(this->ui->brightnessSpin, false);
    break;
  case 18: {
    this->ui->gainSpin->setValue(static_cast<double>(
        this->sensor->gainToReal(this->sensor->getManualGain())));
    uint32_t index = 0;
    while (this->sensor->getManualGain() >
           this->sensor->getLimits().gainLims.at(index)) {
      index++;
    }
    this->ui->gainSpin->setSingleStep(static_cast<double>(
        this->sensor->getLimits().gainStepSizeReal.at(index)));
    this->pendingControler->setPending(this->ui->gainSpin, false);
    break;
  }
  case 19:
    this->ui->exposureSpin->setValue(
        static_cast<int>(this->sensor->getExposureTime() & (~0x80000000)));
    this->pendingControler->setPending(this->ui->exposureSpin, false);
    break;
  case 20:
    this->ui->binColBox->setCurrentText(
        QString::number(this->sensor->getBinning().collum));
    this->ui->binRowBox->setCurrentText(
        QString::number(this->sensor->getBinning().row));
    this->pendingControler->setPending(this->ui->binColBox, false);
    this->pendingControler->setPending(this->ui->binRowBox, false);
    break;
  case 21:
    this->ui->binColBox->setCurrentText(
        QString::number(this->sensor->getSkip().collum));
    this->ui->binRowBox->setCurrentText(
        QString::number(this->sensor->getSkip().row));
    this->pendingControler->setPending(this->ui->binColBox, false);
    this->pendingControler->setPending(this->ui->binRowBox, false);
    break;
  case 22:
    this->ui->flipColBox->setChecked(this->sensor->getFlip().collum);
    this->ui->flipRowBox->setChecked(this->sensor->getFlip().row);
    this->pendingControler->setPending(this->ui->flipColBox, false);
    this->pendingControler->setPending(this->ui->flipRowBox, false);
    break;
  case 23:
    this->ui->actionTestMode->setChecked(this->sensor->getTest());
    this->ui->actionNextTest->setEnabled(this->sensor->getTest());
    if (!this->pendingControler->enabled) {
      this->pendingControler->enablePending(true);
    }
    break;
  default:
    break;
  }
}

void MainWindow::onGainChange(bool up) {
  uint32_t i;
  for (i = 0; i < this->sensor->getLimits().gainLims.size(); i++) {
    if (this->sensor->gainToSensor(
            static_cast<float>(this->ui->gainSpin->value())) ==
        this->sensor->getLimits().gainLims.at(i)) {
      if (up) {
        this->ui->gainSpin->setSingleStep(static_cast<double>(
            this->sensor->getLimits().gainStepSizeReal.at(i + 1)));
      } else {
        this->ui->gainSpin->setSingleStep(static_cast<double>(
            this->sensor->getLimits().gainStepSizeReal.at(i)));
      }
      break;
    }
  }
  if (up) {
    this->ui->gainSpin->setValue(this->ui->gainSpin->value() +
                                 this->ui->gainSpin->singleStep());
  } else {
    this->ui->gainSpin->setValue(this->ui->gainSpin->value() -
                                 this->ui->gainSpin->singleStep());
  }
}

void MainWindow::saveImage() {
  QString fileName = "";
  QString selectedFilter;
  fileName = QFileDialog::getSaveFileName(
      this, tr("Save Image"), "", tr("*.png;;*.jpg;;*.bmp"), &selectedFilter);
  if (fileName.compare("") == 0) {
    return;
  }
  selectedFilter = selectedFilter.remove(0, 1);
  fileName = fileName.split('.').at(0);
  fileName = fileName.append(selectedFilter);
  if (this->img->save(fileName)) {
    this->ui->statusbar->showMessage("Image saved.", 3000);
  } else {
    this->ui->statusbar->showMessage("No image to save!", 3000);
  }
}

void MainWindow::saveImageLine() {
  QString fileName = "";
  QString selectedFilter;
  fileName =
      QFileDialog::getSaveFileName(this, tr("Save Line Image"), "",
                                   tr("*.png;;*.jpg;;*.bmp"), &selectedFilter);
  if (fileName.compare("") == 0) {
    return;
  }
  selectedFilter = selectedFilter.remove(0, 1);
  fileName = fileName.split('.').at(0);
  fileName = fileName.append(selectedFilter);
  if (this->img->saveLine(fileName)) {
    this->ui->statusbar->showMessage("Image saved.", 3000);
  } else {
    this->ui->statusbar->showMessage("No image to save!", 3000);
  }
}

void MainWindow::saveLineData() {
  QString fileName = "";
  QString selectedFilter;
  fileName = QFileDialog::getSaveFileName(this, tr("Save Line Data"), "",
                                          tr("*.csv"), &selectedFilter);
  if (fileName.compare("") == 0) {
    return;
  }
  selectedFilter = selectedFilter.remove(0, 1);
  fileName = fileName.split('.').at(0);
  fileName = fileName.append(selectedFilter);
  if (this->img->saveLineData(fileName)) {
    this->ui->statusbar->showMessage("Line Data saved.", 3000);
  } else {
    this->ui->statusbar->showMessage("No image to save!", 3000);
  }
}

void MainWindow::saveCMOS() {
  QString fileName = "";
  QString selectedFilter;
  fileName = QFileDialog::getSaveFileName(this, tr("Save CMOS settings"), "",
                                          "", &selectedFilter);
  if (fileName.compare("") == 0) {
    return;
  }
  fileName = fileName.split(".").at(0);
  if (this->sensor->saveCMOS(fileName)) {
    this->ui->statusbar->showMessage("CMOS settings saved.", 3000);
  } else {
    this->ui->statusbar->showMessage("CMOS settings save error!", 3000);
  }
}

void MainWindow::loadCMOS() {
  QString fileName = "";
  QString selectedFilter;
  QString filter = this->sensor->getSensorName();
  filter.prepend("*.");
  fileName = QFileDialog::getOpenFileName(this, tr("Save CMOS settings"), "",
                                          filter, &selectedFilter);
  if (fileName.compare("") == 0) {
    return;
  }
  if (this->sensor->loadCMOS(fileName)) {
    this->setAll();
    this->ui->statusbar->showMessage("CMOS settings loaded.", 3000);
  } else {
    this->ui->statusbar->showMessage("CMOS settings load error!", 3000);
  }
}

void MainWindow::setRegs() { this->regsWindow->show(); }

void MainWindow::sendAEC() {
  setAEC[3] = this->ui->AECCheck->isChecked();
  this->port->send(setAEC);
}

void MainWindow::sendAGC() {
  setAGC[3] = this->ui->AGCCheck->isChecked();
  this->port->send(setAGC);
}

void MainWindow::sendHDR() {
  setHDR[3] = this->ui->HDRCheck->isChecked();
  this->port->send(setHDR);
}

void MainWindow::sendBright() {
  setBright[3] = static_cast<char>(this->ui->brightnessSpin->value());
  this->port->send(setBright);
}

void MainWindow::sendGain() {
  uint8_t gain = this->sensor->gainToSensor(
      static_cast<float>(this->ui->gainSpin->value()));
  setGain[3] = static_cast<char>(gain);
  this->port->send(setGain);
}

void MainWindow::sendExposure() {
  int exp = this->ui->exposureSpin->value();
  setExposure[3] = static_cast<char>(exp);
  setExposure[4] = static_cast<char>(exp >> 8);
  setExposure[5] = static_cast<char>(exp >> 16);
  setExposure[6] = static_cast<char>(exp >> 24);
  this->port->send(setExposure);
}

void MainWindow::sendBin() {
  if (this->ui->binningLabel->currentText().compare("Binning") == 0) {
    setBin[3] = static_cast<char>(this->ui->binRowBox->currentText().toInt());
    setBin[4] = static_cast<char>(this->ui->binColBox->currentText().toInt());
    this->port->send(setBin);
  } else if (this->ui->binningLabel->currentText().compare("Skip") == 0) {
    setSkip[3] = static_cast<char>(this->ui->binRowBox->currentText().toInt());
    setSkip[4] = static_cast<char>(this->ui->binColBox->currentText().toInt());
    this->port->send(setSkip);
  }
}

void MainWindow::sendFlip() {
  setFlip[3] = this->ui->flipRowBox->isChecked();
  setFlip[4] = this->ui->flipColBox->isChecked();
  this->port->send(setFlip);
}

void MainWindow::sendTest() {
  setTest[3] = this->ui->actionTestMode->isChecked();
  this->port->send(setTest);
  this->ui->actionNextTest->setEnabled(this->ui->actionTestMode->isChecked());
}

void MainWindow::setAll() {
  QByteArray toSend;
  if (this->sensor->getCapabilities().aec) {
    setAEC[3] = this->ui->AECCheck->isChecked();
    toSend.append(setAEC);
  }
  if (this->sensor->getCapabilities().agc) {
    setAGC[3] = this->ui->AGCCheck->isChecked();
    toSend.append(setAGC);
  }
  if (this->sensor->getCapabilities().hdr) {
    setHDR[3] = this->ui->HDRCheck->isChecked();
    toSend.append(setHDR);
  }
  if (this->sensor->getCapabilities().aec ||
      this->sensor->getCapabilities().agc) {
    setBright[3] = static_cast<char>(this->ui->brightnessSpin->value());
    toSend.append(setBright);
  }
  if (this->sensor->getCapabilities().flip) {
    setFlip[3] = this->ui->flipRowBox->isChecked();
    setFlip[4] = this->ui->flipColBox->isChecked();
    toSend.append(setFlip);
  }
  if (this->sensor->getCapabilities().test) {
    setTest[3] = this->ui->actionTestMode->isChecked();
    toSend.append(setTest);
    this->ui->actionNextTest->setEnabled(this->ui->actionTestMode->isChecked());
  }
  if (this->sensor->getCapabilities().roi) {
    uint16_t startX =
        static_cast<uint16_t>(this->roiWindow->ui->startXSpin->value()) *
        this->roiWindow->collumUsed;
    uint16_t startY =
        static_cast<uint16_t>(this->roiWindow->ui->startYSpin->value()) *
        this->roiWindow->rowUsed;
    uint16_t width =
        static_cast<uint16_t>(this->roiWindow->ui->widthSpin->value()) *
        this->roiWindow->collumUsed;
    uint16_t height =
        static_cast<uint16_t>(this->roiWindow->ui->heightSpin->value()) *
        this->roiWindow->rowUsed;
    setROI[3] = static_cast<char>(startX);
    setROI[4] = static_cast<char>(startX >> 8);
    setROI[5] = static_cast<char>(startY);
    setROI[6] = static_cast<char>(startY >> 8);
    setROI[7] = static_cast<char>(width);
    setROI[8] = static_cast<char>(width >> 8);
    setROI[9] = static_cast<char>(height);
    setROI[10] = static_cast<char>(height >> 8);
    toSend.append(setROI);
  }
  if (this->sensor->getCapabilities().bin) {
    setBin[3] = static_cast<char>(this->ui->binRowBox->currentText().toInt());
    setBin[4] = static_cast<char>(this->ui->binColBox->currentText().toInt());
    toSend.append(setBin);
  }
  if (this->sensor->getCapabilities().skip) {
    setSkip[3] = static_cast<char>(this->ui->binRowBox->currentText().toInt());
    setSkip[4] = static_cast<char>(this->ui->binColBox->currentText().toInt());
    toSend.append(setSkip);
  }
  uint8_t gain = this->sensor->gainToSensor(
      static_cast<float>(this->ui->gainSpin->value()));
  setGain[3] = static_cast<char>(gain);
  toSend.append(setGain);

  int exp = this->ui->exposureSpin->value();
  setExposure[3] = static_cast<char>(exp);
  setExposure[4] = static_cast<char>(exp >> 8);
  setExposure[5] = static_cast<char>(exp >> 16);
  setExposure[6] = static_cast<char>(exp >> 24);
  toSend.append(setExposure);

  this->port->send(toSend);
}

void MainWindow::setROIShow() { this->roiWindow->show(); }

void MainWindow::setDefaultSett() {
  this->port->send(setDefault);
  this->ui->lineSelectModeBox->setCurrentIndex(0);
  setLineSelectMode();
}

void MainWindow::alignedSize() {
  this->ui->statusbar->showMessage("Image size aligned to 4 bytes", 2000);
}

void MainWindow::speedTest() { this->port->send(speedMeasurement); }

void MainWindow::refreshBinSkip(void) {
  if (this->ui->binningLabel->currentText().compare("Binning") == 0) {
    emit refreshBinning();
  } else if (this->ui->binningLabel->currentText().compare("Skip") == 0) {
    emit refreshSkip();
  }
}

void MainWindow::binSkipSelect(QString val) {
  this->ui->binColBox->clear();
  this->ui->binRowBox->clear();
  if (val.compare("Binning") == 0) {
    for (uint32_t i = 0; i < this->sensor->getLimits().binSteps.size(); i++) {
      this->ui->binColBox->addItem(
          QString::number(this->sensor->getLimits().binSteps.at(i)));
      this->ui->binRowBox->addItem(
          QString::number(this->sensor->getLimits().binSteps.at(i)));
    }
    this->ui->binColBox->setCurrentText(
        QString::number(this->sensor->getBinning().collum));
    this->ui->binRowBox->setCurrentText(
        QString::number(this->sensor->getBinning().row));
  } else if (val.compare("Skip") == 0) {
    for (uint32_t i = 0; i < this->sensor->getLimits().skipSteps.size(); i++) {
      this->ui->binColBox->addItem(
          QString::number(this->sensor->getLimits().skipSteps.at(i)));
      this->ui->binRowBox->addItem(
          QString::number(this->sensor->getLimits().skipSteps.at(i)));
    }
    this->ui->binColBox->setCurrentText(
        QString::number(this->sensor->getSkip().collum));
    this->ui->binRowBox->setCurrentText(
        QString::number(this->sensor->getSkip().row));
  }
}

void MainWindow::lineModeSelectChanged(int index) {
  if (index != this->img->getLinearModeApp()) {
    this->pendingControler->setPending(this->ui->lineSelectModeBox, false);
  } else {
    this->pendingControler->setPending(this->ui->lineSelectModeBox, true);
  }
  if (index == 1) {
    // hide image area in linear mode
    this->ui->scrollArea_2->setMaximumHeight(0);
    this->ui->scrollArea_2->setMaximumWidth(0);
  } else {
    // show image area in linear mode
    this->ui->scrollArea_2->setMaximumHeight(16777215);
    this->ui->scrollArea_2->setMaximumWidth(16777215);
  }
}

void MainWindow::setLineSelectMode() {
  this->pendingControler->setPending(this->ui->lineSelectModeBox, false);
  if (this->ui->lineSelectModeBox->currentIndex() == 1) {
    this->img->setLine(0);
    this->img->setLinearModeApp(false);
    uint16_t startX = this->sensor->getROI().beginX;
    uint16_t startY = static_cast<uint16_t>(this->img->getLine());
    uint16_t width = this->sensor->getROI().width;
    uint16_t height = 1;
    if (this->sensor->getBinning().row > height) {
      height = this->sensor->getBinning().row;
      startY *= this->sensor->getBinning().row;
    }
    if (this->sensor->getSkip().row > height) {
      height = this->sensor->getSkip().row;
      startY *= this->sensor->getSkip().row;
    }
    if (this->sensor->getImg().width % 4 != 0) {
      uint8_t align = 4 - this->sensor->getImg().width % 4;
      width = width + (align * height);
    }
    setROI[3] = static_cast<char>(startX);
    setROI[4] = static_cast<char>(startX >> 8);
    setROI[5] = static_cast<char>(startY);
    setROI[6] = static_cast<char>(startY >> 8);
    setROI[7] = static_cast<char>(width);
    setROI[8] = static_cast<char>(width >> 8);
    setROI[9] = static_cast<char>(height);
    setROI[10] = static_cast<char>(height >> 8);
    this->getPort()->send(setROI);
  } else {
    this->img->setLinearModeApp(true);
    uint16_t startX = this->sensor->getROI().beginX;
    uint16_t startY = 0;
    uint16_t width = this->sensor->getROI().width;
    uint16_t height = this->sensor->getLimits().maxHeight;
    setROI[3] = static_cast<char>(startX);
    setROI[4] = static_cast<char>(startX >> 8);
    setROI[5] = static_cast<char>(startY);
    setROI[6] = static_cast<char>(startY >> 8);
    setROI[7] = static_cast<char>(width);
    setROI[8] = static_cast<char>(width >> 8);
    setROI[9] = static_cast<char>(height);
    setROI[10] = static_cast<char>(height >> 8);
    this->getPort()->send(setROI);
  }
}

void MainWindow::setLine() {
  if (this->ui->lineSelectModeBox->currentIndex() == 0) {
    this->img->setLine(this->ui->selectedLineSpin->value());
  } else {
    this->img->setLine(0);
  }
  this->pendingControler->setPending(this->ui->selectedLineSpin, false);
  if (this->img->getLinearModeApp()) {
    emit requestRedrawLine();
    emit requestRedrawLineArea();
  } else {
    uint16_t startX = this->sensor->getROI().beginX;
    uint16_t startY = static_cast<uint16_t>(this->img->getLine());
    uint16_t width = this->sensor->getROI().width;
    uint16_t height = 1;
    if (this->sensor->getBinning().row > height) {
      height = this->sensor->getBinning().row;
      startY *= this->sensor->getBinning().row;
    }
    if (this->sensor->getSkip().row > height) {
      height = this->sensor->getSkip().row;
      startY *= this->sensor->getSkip().row;
    }
    if (this->sensor->getImg().width % 4 != 0) {
      uint8_t align = 4 - this->sensor->getImg().width % 4;
      width = width + (align * height);
    }
    setROI[3] = static_cast<char>(startX);
    setROI[4] = static_cast<char>(startX >> 8);
    setROI[5] = static_cast<char>(startY);
    setROI[6] = static_cast<char>(startY >> 8);
    setROI[7] = static_cast<char>(width);
    setROI[8] = static_cast<char>(width >> 8);
    setROI[9] = static_cast<char>(height);
    setROI[10] = static_cast<char>(height >> 8);
    this->getPort()->send(setROI);
  }
}

void MainWindow::lineSelectChanged(int val) {
  if (val != this->img->getLine()) {
    this->pendingControler->setPending(this->ui->selectedLineSpin, true);
  } else {
    this->pendingControler->setPending(this->ui->selectedLineSpin, false);
  }
  this->img->setPendingLine(val);
  if (this->ui->lineSelectModeBox->currentIndex() == 0) {
    emit requestRedrawLineArea();
  }
}

void MainWindow::showAbout() { this->about->show(); }

void MainWindow::catchFPS(double fps) {
  if (!this->ui->playButton->isEnabled()) {
    if (this->fps.size() > 9) {
      this->fps.erase(this->fps.begin());
    }
    this->fps.push_back(fps);
    double f = 0;
    for (double fp : this->fps) {
      f += fp;
    }
    this->ui->fps->setText(QString::number(f / this->fps.size(), 'g', 2));
  }
}
