#include "roiwindow.h"
#include "communication/messages.h"
#include "mainwindow.h"
#include "ui_roiwindow.h"

ROIWindow::ROIWindow(QWidget *parent) : QDialog(parent), ui(new Ui::ROIWindow) {
  ui->setupUi(this);
  this->parent = parent;
  this->rowBin = 1;
  this->collumBin = 1;
  this->rowSkip = 1;
  this->collumSkip = 1;
  this->rowUsed = 1;
  this->collumUsed = 1;
}

ROIWindow::~ROIWindow() { delete ui; }

void ROIWindow::catchSensorUpdate(uint8_t code) {
  switch (code) {
  case 2:
    if (reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getCapabilities()
            .rgb) {
      this->ui->widthSpin->setSingleStep(2);
      this->ui->startXSpin->setSingleStep(2);
      this->ui->heightSpin->setSingleStep(2);
      this->ui->startYSpin->setSingleStep(2);
    } else {
      this->ui->widthSpin->setSingleStep(1);
      this->ui->startXSpin->setSingleStep(1);
      this->ui->heightSpin->setSingleStep(1);
      this->ui->startYSpin->setSingleStep(1);
    }
    break;
  case 3:
    this->ui->widthSpin->setMaximum(reinterpret_cast<MainWindow *>(this->parent)
                                        ->getSensor()
                                        ->getLimits()
                                        .maxWidth /
                                    this->collumUsed);
    this->ui->widthSpin->setMinimum(reinterpret_cast<MainWindow *>(this->parent)
                                        ->getSensor()
                                        ->getLimits()
                                        .minWidth /
                                    this->collumUsed);

    this->ui->startXSpin->setMaximum(
        (reinterpret_cast<MainWindow *>(this->parent)
             ->getSensor()
             ->getLimits()
             .maxWidth -
         1) /
        this->collumUsed);
    this->ui->startXSpin->setMinimum(0);
    break;
  case 4:
    this->ui->heightSpin->setMaximum(
        reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getLimits()
            .maxHeight /
        this->rowUsed);
    this->ui->heightSpin->setMinimum(
        reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getLimits()
            .minHeight /
        this->rowUsed);

    this->ui->startYSpin->setMaximum(
        (reinterpret_cast<MainWindow *>(this->parent)
             ->getSensor()
             ->getLimits()
             .maxHeight -
         1) /
        this->rowUsed);
    this->ui->startYSpin->setMinimum(0);
    break;
  case 12:
    break;
  case 13:
    this->ui->startXSpin->setValue(reinterpret_cast<MainWindow *>(this->parent)
                                       ->getSensor()
                                       ->getROI()
                                       .beginX /
                                   this->collumUsed);
    this->ui->startYSpin->setValue(reinterpret_cast<MainWindow *>(this->parent)
                                       ->getSensor()
                                       ->getROI()
                                       .beginY /
                                   this->rowUsed);
    this->ui->widthSpin->setValue(reinterpret_cast<MainWindow *>(this->parent)
                                      ->getSensor()
                                      ->getROI()
                                      .width /
                                  this->collumUsed);
    this->ui->heightSpin->setValue(reinterpret_cast<MainWindow *>(this->parent)
                                       ->getSensor()
                                       ->getROI()
                                       .height /
                                   this->rowUsed);
    break;
  case 20:
    if (reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getBinning()
            .row == 0) {
      this->rowBin = 1;
    } else {
      this->rowBin = reinterpret_cast<MainWindow *>(this->parent)
                         ->getSensor()
                         ->getBinning()
                         .row;
    }
    if (this->rowBin >= this->rowSkip) {
      this->rowUsed = rowBin;
    }
    if (reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getBinning()
            .collum == 0) {
      this->collumBin = 1;
    } else {
      this->collumBin = reinterpret_cast<MainWindow *>(this->parent)
                            ->getSensor()
                            ->getBinning()
                            .collum;
    }
    if (this->collumBin >= this->collumSkip) {
      this->collumUsed = collumBin;
    }
    break;
  case 21:
    if (reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getSkip()
            .row == 0) {
      this->rowSkip = 1;
    } else {
      this->rowSkip = reinterpret_cast<MainWindow *>(this->parent)
                          ->getSensor()
                          ->getSkip()
                          .row;
    }
    this->rowUsed = rowSkip;
    if (reinterpret_cast<MainWindow *>(this->parent)
            ->getSensor()
            ->getSkip()
            .collum == 0) {
      this->collumSkip = 1;
    } else {
      this->collumSkip = reinterpret_cast<MainWindow *>(this->parent)
                             ->getSensor()
                             ->getSkip()
                             .collum;
    }
    this->collumUsed = collumSkip;
    break;
  default:
    break;
  }
}

void ROIWindow::on_startXSpin_valueChanged(int arg1) {
  if ((arg1 * this->collumUsed) +
          (this->ui->widthSpin->value() * this->collumUsed) >
      reinterpret_cast<MainWindow *>(this->parent)
          ->getSensor()
          ->getLimits()
          .maxWidth) {
    this->ui->widthSpin->setValue((reinterpret_cast<MainWindow *>(this->parent)
                                       ->getSensor()
                                       ->getLimits()
                                       .maxWidth /
                                   this->collumUsed) -
                                  arg1);
  }
  this->checkAlign(false);
  if (this->ui->previewCheck->isChecked()) {
    emit roiWindow(
        QRect(this->ui->startXSpin->value(), this->ui->startYSpin->value(),
              this->ui->widthSpin->value(), this->ui->heightSpin->value()));
    emit requestRedraw();
  }
}

void ROIWindow::on_widthSpin_valueChanged(int arg1) {
  if ((arg1 * this->collumUsed) +
          (this->ui->startXSpin->value() * this->collumUsed) >
      reinterpret_cast<MainWindow *>(this->parent)
          ->getSensor()
          ->getLimits()
          .maxWidth) {
    this->ui->startXSpin->setValue((reinterpret_cast<MainWindow *>(this->parent)
                                        ->getSensor()
                                        ->getLimits()
                                        .maxWidth /
                                    this->collumUsed) -
                                   arg1);
  }
  this->checkAlign(false);
  if (this->ui->previewCheck->isChecked()) {
    emit roiWindow(
        QRect(this->ui->startXSpin->value(), this->ui->startYSpin->value(),
              this->ui->widthSpin->value(), this->ui->heightSpin->value()));
    emit requestRedraw();
  }
}

void ROIWindow::on_startYSpin_valueChanged(int arg1) {
  if ((arg1 * this->rowUsed) + (this->ui->heightSpin->value() * this->rowUsed) >
      reinterpret_cast<MainWindow *>(this->parent)
          ->getSensor()
          ->getLimits()
          .maxHeight) {
    this->ui->heightSpin->setValue((reinterpret_cast<MainWindow *>(this->parent)
                                        ->getSensor()
                                        ->getLimits()
                                        .maxHeight /
                                    this->rowUsed) -
                                   arg1);
  }
  this->checkAlign(true);
  if (this->ui->previewCheck->isChecked()) {
    emit roiWindow(
        QRect(this->ui->startXSpin->value(), this->ui->startYSpin->value(),
              this->ui->widthSpin->value(), this->ui->heightSpin->value()));
    emit requestRedraw();
  }
}

void ROIWindow::on_heightSpin_valueChanged(int arg1) {
  if ((arg1 * this->rowUsed) + (this->ui->startYSpin->value() * this->rowUsed) >
      reinterpret_cast<MainWindow *>(this->parent)
          ->getSensor()
          ->getLimits()
          .maxHeight) {
    this->ui->startYSpin->setValue((reinterpret_cast<MainWindow *>(this->parent)
                                        ->getSensor()
                                        ->getLimits()
                                        .maxHeight /
                                    this->rowUsed) -
                                   arg1);
  }
  this->checkAlign(true);
  if (this->ui->previewCheck->isChecked()) {
    emit roiWindow(
        QRect(this->ui->startXSpin->value(), this->ui->startYSpin->value(),
              this->ui->widthSpin->value(), this->ui->heightSpin->value()));
    emit requestRedraw();
  }
}

void ROIWindow::on_sendROIButton_clicked() {
  uint16_t startX =
      static_cast<uint16_t>(this->ui->startXSpin->value()) * this->collumUsed;
  uint16_t startY =
      static_cast<uint16_t>(this->ui->startYSpin->value()) * this->rowUsed;
  uint16_t width =
      static_cast<uint16_t>(this->ui->widthSpin->value()) * this->collumUsed;
  uint16_t height =
      static_cast<uint16_t>(this->ui->heightSpin->value()) * this->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);
  reinterpret_cast<MainWindow *>(this->parent)->getPort()->send(setROI);
}

void ROIWindow::checkAlign(bool editWidth) {
  int overFlow =
      (this->ui->widthSpin->value() * this->ui->heightSpin->value()) % 4;
  if (overFlow != 0) {
    int newValue;
    if (editWidth) {
      newValue = this->ui->heightSpin->value() % 2 == 0
                     ? 2 * (this->ui->widthSpin->value() / 2)
                     : 4 * (this->ui->widthSpin->value() / 4);
      this->ui->widthSpin->setValue(newValue);
    } else {
      newValue = this->ui->widthSpin->value() % 2 == 0
                     ? 2 * (this->ui->heightSpin->value() / 2)
                     : 4 * (this->ui->heightSpin->value() / 4);
      this->ui->heightSpin->setValue(newValue);
    }
    emit aligned();
  }
}

void ROIWindow::on_previewCheck_stateChanged(int arg1) {
  emit previewState(arg1 > 0 ? true : false);
  emit roiWindow(
      QRect(this->ui->startXSpin->value(), this->ui->startYSpin->value(),
            this->ui->widthSpin->value(), this->ui->heightSpin->value()));
  emit requestRedraw();
}
