/***

  Olive - Non-Linear Video Editor
  Copyright (C) 2022 Olive Team

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

***/

#include "export.h"

#include <QCloseEvent>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QScrollArea>
#include <QSplitter>
#include <QStandardPaths>

#include "common/digit.h"
#include "common/qtutils.h"
#include "dialog/task/task.h"
#include "exportsavepresetdialog.h"
#include "node/project/project.h"
#include "node/project/sequence/sequence.h"
#include "task/taskmanager.h"
#include "ui/icons/icons.h"
#include "widget/timeruler/timeruler.h"

namespace olive {

#define super QDialog

ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode, QWidget *parent) :
  super(parent),
  viewer_node_(viewer_node),
  stills_only_mode_(stills_only_mode),
  loading_presets_(false)
{
  QHBoxLayout* layout = new QHBoxLayout(this);

  QSplitter* splitter = new QSplitter(Qt::Horizontal);
  splitter->setChildrenCollapsible(false);
  layout->addWidget(splitter);

  preferences_area_ = new QWidget();
  QGridLayout* preferences_layout = new QGridLayout(preferences_area_);
  preferences_layout->setMargin(0);

  int row = 0;

  QLabel* fn_lbl = new QLabel(tr("Filename:"));
  fn_lbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
  preferences_layout->addWidget(fn_lbl, row, 0);

  filename_edit_ = new QLineEdit();
  preferences_layout->addWidget(filename_edit_, row, 1, 1, 2);

  QPushButton* file_browse_btn = new QPushButton();
  file_browse_btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
  file_browse_btn->setIcon(icon::Folder);
  file_browse_btn->setToolTip(tr("Browse for exported file filename"));
  connect(file_browse_btn,
          &QPushButton::clicked,
          this,
          &ExportDialog::BrowseFilename);
  preferences_layout->addWidget(file_browse_btn, row, 3);

  row++;

  QLabel* preset_lbl = new QLabel(tr("Preset:"));
  preset_lbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
  preferences_layout->addWidget(preset_lbl, row, 0);
  preset_combobox_ = new QComboBox();
  LoadPresets();
  connect(preset_combobox_, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ExportDialog::PresetComboBoxChanged);
  preferences_layout->addWidget(preset_combobox_, row, 1, 1, 2);

  /*QPushButton* preset_load_btn = new QPushButton();
  preset_load_btn->setIcon(icon::Open);
  preset_load_btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
  preferences_layout->addWidget(preset_load_btn, row, 2);*/

  QPushButton* preset_save_btn = new QPushButton();
  preset_save_btn->setIcon(icon::Save);
  preset_save_btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
  preferences_layout->addWidget(preset_save_btn, row, 3);
  connect(preset_save_btn, &QPushButton::clicked, this, &ExportDialog::SavePreset);

  row++;

  preferences_layout->addWidget(QtUtils::CreateHorizontalLine(), row, 0, 1, 4);

  row++;

  preferences_layout->addWidget(new QLabel(tr("Range:")), row, 0);

  range_combobox_ = new QComboBox();
  range_combobox_->addItem(tr("Entire Sequence"));
  range_combobox_->addItem(tr("In to Out"));
  range_combobox_->setEnabled(viewer_node_->GetWorkArea()->enabled());

  preferences_layout->addWidget(range_combobox_, row, 1, 1, 3);

  row++;

  preferences_layout->addWidget(QtUtils::CreateHorizontalLine(), row, 0, 1, 4);

  row++;

  preferences_layout->addWidget(new QLabel(tr("Format:")), row, 0);
  format_combobox_ = new ExportFormatComboBox();
  preferences_layout->addWidget(format_combobox_, row, 1, 1, 3);

  row++;

  QHBoxLayout* av_enabled_layout = new QHBoxLayout();

  video_enabled_ = new QCheckBox(tr("Export Video"));
  av_enabled_layout->addWidget(video_enabled_);

  audio_enabled_ = new QCheckBox(tr("Export Audio"));
  av_enabled_layout->addWidget(audio_enabled_);

  subtitles_enabled_ = new QCheckBox(tr("Export Subtitles"));
  av_enabled_layout->addWidget(subtitles_enabled_);

  preferences_layout->addLayout(av_enabled_layout, row, 0, 1, 4);

  row++;

  preferences_tabs_ = new QTabWidget();

  color_manager_ = viewer_node_->project()->color_manager();
  video_tab_ = new ExportVideoTab(color_manager_);
  AddPreferencesTab(video_tab_, tr("Video"));

  audio_tab_ = new ExportAudioTab();
  AddPreferencesTab(audio_tab_, tr("Audio"));

  subtitle_tab_ = new ExportSubtitlesTab();
  AddPreferencesTab(subtitle_tab_, tr("Subtitles"));

  preferences_layout->addWidget(preferences_tabs_, row, 0, 1, 4);

  row++;

  {
    QGroupBox *options_group = new QGroupBox();
    preferences_layout->addWidget(options_group, row, 0, 1, 4);

    QGridLayout *options_layout = new QGridLayout(options_group);

    int opt_row = 0;

    export_bkg_box_ = new QCheckBox(tr("Run In Background"));
    export_bkg_box_->setToolTip(tr("Exporting in the background allows you to continue using Olive while "
                                   "exporting, but may result in slower export speeds, and may"
                                   "severely impact editing and playback performance."));
    options_layout->addWidget(export_bkg_box_, opt_row, 0);

    import_file_after_export_ = new QCheckBox(tr("Import Result After Export"));
    options_layout->addWidget(import_file_after_export_, opt_row, 1);

    connect(export_bkg_box_, &QCheckBox::toggled, import_file_after_export_, [this](bool e){
      import_file_after_export_->setEnabled(!e);
    });
  }

  row++;

  QHBoxLayout *btn_layout = new QHBoxLayout();
  btn_layout->setMargin(0);
  preferences_layout->addLayout(btn_layout, row, 0, 1, 4);

  btn_layout->addStretch();

  QPushButton *export_btn = new QPushButton(tr("Export"));
  btn_layout->addWidget(export_btn);
  connect(export_btn, &QPushButton::clicked, this, &ExportDialog::StartExport);

  QPushButton *cancel_btn = new QPushButton(tr("Cancel"));
  btn_layout->addWidget(cancel_btn);
  connect(cancel_btn, &QPushButton::clicked, this, &ExportDialog::reject);

  btn_layout->addStretch();

  splitter->addWidget(preferences_area_);

  QWidget* preview_area = new QWidget();
  QVBoxLayout* preview_layout = new QVBoxLayout(preview_area);
  preview_layout->addWidget(new QLabel(tr("Preview")));
  preview_viewer_ = new ViewerWidget();
  preview_viewer_->ruler()->SetMarkerEditingEnabled(false);
  preview_viewer_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  connect(preview_viewer_, &ViewerWidget::TimeChanged, video_tab_, &ExportVideoTab::SetTime);
  preview_layout->addWidget(preview_viewer_);
  splitter->addWidget(preview_area);

  // Prioritize preview area
  splitter->setSizes({1, 99999});

  // Set default filename
  SetDefaultFilename();

  // Set defaults
  previously_selected_format_ = ExportFormat::kFormatMPEG4Video;
  connect(format_combobox_, &ExportFormatComboBox::FormatChanged, this, &ExportDialog::FormatChanged);

  VideoParams vp = viewer_node_->GetVideoParams();
  video_aspect_ratio_ = static_cast<double>(vp.width()) / static_cast<double>(vp.height());

  connect(video_tab_->width_slider(),
          &IntegerSlider::ValueChanged,
          this,
          &ExportDialog::ResolutionChanged);

  connect(video_tab_->height_slider(),
          &IntegerSlider::ValueChanged,
          this,
          &ExportDialog::ResolutionChanged);

  connect(video_tab_->scaling_method_combobox(),
          static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
          this,
          &ExportDialog::UpdateViewerDimensions);

  connect(video_tab_->maintain_aspect_checkbox(),
          &QCheckBox::toggled,
          this,
          &ExportDialog::ResolutionChanged);

  connect(video_tab_,
          &ExportVideoTab::ColorSpaceChanged,
          preview_viewer_,
          static_cast<void(ViewerWidget::*)(const ColorTransform&)>(&ViewerWidget::SetColorTransform));
  connect(video_tab_,
          &ExportVideoTab::ImageSequenceCheckBoxChanged,
          this,
          &ExportDialog::ImageSequenceCheckBoxChanged);

  // We don't check if the codec supports subtitles because we can always export to a sidecar file
  bool has_subtitle_tracks = SequenceHasSubtitles();
  connect(subtitles_enabled_, &QCheckBox::toggled, subtitle_tab_, &QWidget::setEnabled);
  subtitles_enabled_->setEnabled(has_subtitle_tracks);

  // If the viewer already has cached params, use them
  if (!stills_only_mode_ && viewer_node_->GetLastUsedEncodingParams().IsValid()) {
    // This will automatically set the param data
    QtUtils::SetComboBoxData(preset_combobox_, kPresetLastUsed);
  } else {
    SetDefaults();
  }

  // Set viewer to view the node and set its colorspace
  preview_viewer_->ConnectViewerNode(viewer_node_);
  preview_viewer_->SetColorMenuEnabled(false);
  preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());

  qApp->installEventFilter(this);

  connect(video_enabled_, &QCheckBox::toggled, video_tab_, &QWidget::setEnabled);
  video_tab_->setEnabled(video_enabled_->isChecked());
  connect(audio_enabled_, &QCheckBox::toggled, audio_tab_, &QWidget::setEnabled);
  audio_tab_->setEnabled(audio_enabled_->isChecked());
  connect(subtitles_enabled_, &QCheckBox::toggled, subtitle_tab_, &QWidget::setEnabled);
  subtitle_tab_->setEnabled(subtitles_enabled_->isChecked());
}

rational ExportDialog::GetSelectedTimebase() const
{
  return video_tab_->GetSelectedFrameRate().flipped();
}

void ExportDialog::SetSelectedTimebase(const rational &r)
{
  video_tab_->SetSelectedFrameRate(r.flipped());
}

void ExportDialog::StartExport()
{
  if (!video_enabled_->isChecked() && !audio_enabled_->isChecked() && !subtitles_enabled_->isChecked()) {
    QtUtils::MsgBox(this, QMessageBox::Critical, tr("Invalid parameters"),
                        tr("Video, audio, and subtitles are disabled. There's nothing to export."));
    return;
  }

  // Validate if the entered filename contains the correct extension (the extension is necessary
  // for both FFmpeg and OIIO to determine the output format)
  QString necessary_ext = QStringLiteral(".%1").arg(ExportFormat::GetExtension(format_combobox_->GetFormat()));
  QString proposed_filename = filename_edit_->text().trimmed();

  // If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export.
  if (!proposed_filename.endsWith(necessary_ext, Qt::CaseInsensitive)) {
    if (QtUtils::MsgBox(this, QMessageBox::Warning, tr("Invalid filename"),
                            tr("The filename must contain the extension \"%1\". Would you like to append it "
                                             "automatically?").arg(necessary_ext),
                            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
      filename_edit_->setText(proposed_filename.append(necessary_ext));
    } else {
      return;
    }
  }

  // Validate the intended path
  QFileInfo file_info(proposed_filename);
  QFileInfo dir_info(file_info.path());

  // If the directory does not exist, try to create it
  QDir dest_dir(file_info.path());
  if (!FileFunctions::DirectoryIsValid(dest_dir)) {
    QtUtils::MsgBox(this, QMessageBox::Critical, tr("Failed to create output directory"),
                        tr("The intended output directory doesn't exist and Olive couldn't create it. "
                                         "Please choose a different filename."));
    return;
  }

  // Validate if this is an image sequence and if the filename contains enough digits
  if (video_tab_->IsImageSequenceSet()) {
    // Ensure filename contains digits
    if (!Encoder::FilenameContainsDigitPlaceholder(proposed_filename)) {
      QtUtils::MsgBox(this, QMessageBox::Critical, tr("Invalid filename"),
                          tr("Export is set to an image sequence, but the filename does not have a section for digits "
                                             "(formatted as [#####] where the amount of # is the amount of digits)."));
      return;
    }

    int64_t frame_count = GetExportLengthInTimebaseUnits();
    int64_t needed_digit_count = GetDigitCount(frame_count);
    int current_digit_count = Encoder::GetImageSequencePlaceholderDigitCount(proposed_filename);
    if (current_digit_count < needed_digit_count) {
      QtUtils::MsgBox(this, QMessageBox::Critical, tr("Invalid filename"),
                          tr("Filename doesn't contain enough digits for the amount of frames "
                             "this export will need (need %1 for %n frame(s)).", nullptr, frame_count)
                                .arg(QString::number(needed_digit_count)));
      return;
    }
  }

  // Validate if the file exists and whether the user wishes to overwrite it
  if (file_info.exists()) {
    if (QtUtils::MsgBox(this, QMessageBox::Warning, tr("Confirm Overwrite"),
                            tr("The file \"%1\" already exists. Do you want to overwrite it?")
                                          .arg(proposed_filename),
                            QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
      return;
    }
  }

  // Validate video resolution
  if (video_enabled_->isChecked()
      && (video_tab_->GetSelectedCodec() == ExportCodec::kCodecH264 || video_tab_->GetSelectedCodec() == ExportCodec::kCodecH265)
      && (video_tab_->width_slider()->GetValue()%2 != 0 || video_tab_->height_slider()->GetValue()%2 != 0)) {
    QtUtils::MsgBox(this, QMessageBox::Critical, tr("Invalid Parameters"),
                        tr("Width and height must be multiples of 2."));
    return;
  }

  ExportTask* task = new ExportTask(viewer_node_, color_manager_, GenerateParams());

  if (export_bkg_box_->isChecked()) {
    // Send to TaskManager to export in background
    TaskManager::instance()->AddTask(task);
    this->accept();
  } else {
    // Use modal dialog box
    TaskDialog* td = new TaskDialog(task, tr("Export"), this);
    connect(td, &TaskDialog::TaskSucceeded, this, &ExportDialog::ExportFinished);
    td->open();
  }
}

void ExportDialog::ExportFinished()
{
  TaskDialog* td = static_cast<TaskDialog*>(sender());

  if (td->GetTask()->IsCancelled()) {
    // If this task was cancelled, we stay open so the user can potentially queue another export
  } else {
    // Accept this dialog and close
    if (import_file_after_export_->isEnabled() && import_file_after_export_->isChecked()) {
      QString filename = filename_edit_->text().trimmed();
      emit RequestImportFile(filename);
    }

    this->accept();
  }
}

void ExportDialog::ImageSequenceCheckBoxChanged(bool e)
{
  QFileInfo current_fileinfo(filename_edit_->text());

  QString basename = current_fileinfo.completeBaseName();
  QString suffix = current_fileinfo.suffix();

  if (e) {
    if (!Encoder::FilenameContainsDigitPlaceholder(basename)) {
      basename.append(QStringLiteral("_[#####]"));
    }
  } else {
    basename = Encoder::FilenameRemoveDigitPlaceholder(basename);
  }

  // Set filename
  if (!suffix.isEmpty()) {
    basename.append('.');
    basename.append(suffix);
  }
  filename_edit_->setText(current_fileinfo.dir().filePath(basename));
}

void ExportDialog::SavePreset()
{
  ExportSavePresetDialog d(GenerateParams(), this);
  if (d.exec() == QDialog::Accepted) {
    LoadPresets();
    preset_combobox_->setCurrentText(d.GetSelectedPresetName());
  }
}

void ExportDialog::PresetComboBoxChanged()
{
  if (loading_presets_) {
    return;
  }
  
  QComboBox *c = static_cast<QComboBox *>(sender());

  int preset_number = c->currentData().toInt();
  if (preset_number == kPresetDefault) {
    SetDefaults();
  } else if (preset_number == kPresetLastUsed) {
    SetParams(viewer_node_->GetLastUsedEncodingParams());
  } else {
    SetParams(presets_.at(preset_number));
  }
}

void ExportDialog::AddPreferencesTab(QWidget *inner_widget, const QString &title)
{
  QScrollArea* scroll_area = new QScrollArea();
  scroll_area->setWidgetResizable(true);
  scroll_area->setWidget(inner_widget);
  preferences_tabs_->addTab(scroll_area, title);
}

void ExportDialog::BrowseFilename()
{
  ExportFormat::Format f = format_combobox_->GetFormat();

  QString browsed_fn = QFileDialog::getSaveFileName(this,
                                                    "",
                                                    filename_edit_->text().trimmed(),
                                                    QStringLiteral("%1 (*.%2)").arg(ExportFormat::GetName(f), ExportFormat::GetExtension(f)),
                                                    nullptr,

                                                    // We don't confirm overwrite here because we do it later
                                                    QFileDialog::DontConfirmOverwrite);

  if (!browsed_fn.isEmpty()) {
    filename_edit_->setText(browsed_fn);
  }
}

void ExportDialog::FormatChanged(ExportFormat::Format current_format)
{
  QString current_filename = filename_edit_->text().trimmed();
  QString previously_selected_ext = ExportFormat::GetExtension(previously_selected_format_);
  QString currently_selected_ext = ExportFormat::GetExtension(current_format);

  // If the previous extension was added, remove it
  if (current_filename.endsWith(previously_selected_ext, Qt::CaseInsensitive)) {
    current_filename.resize(current_filename.size() - previously_selected_ext.size() - 1);
  }

  // Add the extension and set it
  current_filename.append('.');
  current_filename.append(currently_selected_ext);
  filename_edit_->setText(current_filename);

  previously_selected_format_ = current_format;

  // Update video and audio comboboxes
  bool has_video_codecs = video_tab_->SetFormat(current_format);
  video_enabled_->setChecked(has_video_codecs);
  video_enabled_->setEnabled(has_video_codecs);

  bool has_audio_codecs = audio_tab_->SetFormat(current_format);
  audio_enabled_->setChecked(has_audio_codecs);
  audio_enabled_->setEnabled(has_audio_codecs);

  if (subtitles_enabled_->isEnabled()) {
    subtitle_tab_->SetFormat(current_format);
  }
}

void ExportDialog::ResolutionChanged()
{
  if (video_tab_->maintain_aspect_checkbox()->isChecked()) {
    // Keep aspect ratio maintained
    if (sender() == video_tab_->height_slider()) {

      // Convert height to float
      double new_width = video_tab_->height_slider()->GetValue();

      // Generate width from aspect ratio
      new_width *= video_aspect_ratio_;

      // Align to even number and set
      video_tab_->width_slider()->SetValue(new_width);

    } else {

      // Convert width to float
      double new_height = video_tab_->width_slider()->GetValue();

      // Generate height from aspect ratio
      new_height /= video_aspect_ratio_;

      // Align to even number and set
      video_tab_->height_slider()->SetValue(new_height);

    }
  }

  UpdateViewerDimensions();
}

void ExportDialog::LoadPresets()
{
  loading_presets_ = true;

  preset_combobox_->clear();
  presets_.clear();

  preset_combobox_->addItem(tr("Default"), kPresetDefault);

  if (viewer_node_->GetLastUsedEncodingParams().IsValid()) {
    preset_combobox_->addItem(tr("Last Used"), kPresetLastUsed);
  }

  preset_combobox_->insertSeparator(preset_combobox_->count());

  QStringList l = EncodingParams::GetListOfPresets();
  presets_.reserve(l.size());

  for (const QString &preset : l) {
    EncodingParams p;

    QFile f(EncodingParams::GetPresetPath().filePath(preset));
    if (f.open(QFile::ReadOnly)) {
      if (p.Load(&f)) {
        preset_combobox_->addItem(preset, int(presets_.size()));
        presets_.push_back(p);
      }
      f.close();
    }
  }

  loading_presets_ = false;
}

void ExportDialog::SetDefaultFilename()
{
  Project* p = viewer_node_->project();

  QDir doc_location;

  if (p->filename().isEmpty()) {
    doc_location.setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
  } else {
    doc_location = QFileInfo(p->filename()).dir();
  }

  QString file_location = doc_location.filePath(viewer_node_->GetLabel());
  filename_edit_->setText(file_location);
}

bool ExportDialog::SequenceHasSubtitles() const
{
  if (Sequence *s = dynamic_cast<Sequence*>(viewer_node_)) {
    TrackList *tl = s->track_list(Track::kSubtitle);
    for (Track *t : tl->GetTracks()) {
      if (!t->IsMuted() && !t->Blocks().empty()) {
        return true;
      }
    }
  }

  return false;
}

void ExportDialog::SetDefaults()
{
  if (!stills_only_mode_) {
    format_combobox_->SetFormat(ExportFormat::kFormatMPEG4Video);
  } else {
    format_combobox_->SetFormat(ExportFormat::kFormatPNG);
  }
  FormatChanged(format_combobox_->GetFormat());

  VideoParams vp = viewer_node_->GetVideoParams();
  AudioParams ap = viewer_node_->GetAudioParams();

  video_tab_->width_slider()->SetValue(vp.width());
  video_tab_->width_slider()->SetDefaultValue(vp.width());
  video_tab_->height_slider()->SetValue(vp.height());
  video_tab_->height_slider()->SetDefaultValue(vp.height());
  video_tab_->SetSelectedFrameRate(vp.frame_rate());
  video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
  video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(OLIVE_CONFIG("OnlinePixelFormat").toInt()));
  video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
  audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
  audio_tab_->sample_format_combobox()->SetAttemptToRestoreFormat(false);
  audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
  subtitles_enabled_->setChecked(SequenceHasSubtitles());
}

EncodingParams ExportDialog::GenerateParams() const
{
  VideoParams video_render_params(static_cast<int>(video_tab_->width_slider()->GetValue()),
                                  static_cast<int>(video_tab_->height_slider()->GetValue()),
                                  GetSelectedTimebase(),
                                  video_tab_->pixel_format_field()->GetPixelFormat(),
                                  VideoParams::kInternalChannelCount,
                                  video_tab_->pixel_aspect_combobox()->GetPixelAspectRatio(),
                                  video_tab_->interlaced_combobox()->GetInterlaceMode(),
                                  1);

  AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->GetSampleRate(),
                                  audio_tab_->channel_layout_combobox()->GetChannelLayout(),
                                  audio_tab_->sample_format_combobox()->GetSampleFormat());

  EncodingParams params;
  params.set_format(format_combobox_->GetFormat());
  params.SetFilename(filename_edit_->text().trimmed());
  params.SetExportLength(viewer_node_->GetLength());

  if (ExportCodec::IsCodecAStillImage(video_tab_->GetSelectedCodec()) && !video_tab_->IsImageSequenceSet()) {
    // Exporting as image without exporting image sequence, only export one frame
    rational export_time = video_tab_->GetStillImageTime();
    params.set_custom_range(TimeRange(export_time, export_time + GetSelectedTimebase()));
  } else if (range_combobox_->currentIndex() == kRangeInToOut) {
    // Assume if this combobox is enabled, workarea is enabled - a check that we make in this dialog's constructor
    params.set_custom_range(viewer_node_->GetWorkArea()->range());
  }

  if (video_tab_->scaling_method_combobox()->isEnabled()) {
    params.set_video_scaling_method(static_cast<EncodingParams::VideoScalingMethod>(video_tab_->scaling_method_combobox()->currentData().toInt()));
  }

  if (video_enabled_->isChecked()) {
    ExportCodec::Codec video_codec = video_tab_->GetSelectedCodec();

    video_render_params.set_color_range(video_tab_->color_range());

    params.EnableVideo(video_render_params, video_codec);

    params.set_video_threads(video_tab_->threads());

    if (video_tab_->isVisible()) {
      video_tab_->GetCodecSection()->AddOpts(&params);
    }

    params.set_color_transform(video_tab_->CurrentOCIOColorSpace());

    params.set_video_pix_fmt(video_tab_->pix_fmt());

    params.set_video_is_image_sequence(video_tab_->IsImageSequenceSet());
  }

  if (audio_enabled_->isChecked()) {
    ExportCodec::Codec audio_codec = audio_tab_->GetCodec();
    params.EnableAudio(audio_render_params, audio_codec);

    params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->GetValue() * 1000);
  }

  if (subtitles_enabled_->isEnabled()
      && subtitles_enabled_->isChecked()) {
    if (!subtitle_tab_->GetSidecarEnabled()) {
      // Export subtitles embedded in container
      params.EnableSubtitles(subtitle_tab_->GetSubtitleCodec());
    } else {
      // Export subtitles to a sidecar file
      params.EnableSidecarSubtitles(subtitle_tab_->GetSidecarFormat(), subtitle_tab_->GetSubtitleCodec());
    }
  }

  return params;
}

void ExportDialog::SetParams(const EncodingParams &e)
{
  format_combobox_->SetFormat(e.format());
  FormatChanged(format_combobox_->GetFormat());

  if (e.has_custom_range() && viewer_node_->GetWorkArea()->enabled()) {
    range_combobox_->setCurrentIndex(kRangeInToOut);
  }

  QtUtils::SetComboBoxData(video_tab_->scaling_method_combobox(), e.video_scaling_method());

  video_enabled_->setChecked(e.video_enabled());
  if (e.video_enabled()) {
    video_tab_->width_slider()->SetValue(e.video_params().width());
    video_tab_->height_slider()->SetValue(e.video_params().height());
    SetSelectedTimebase(e.video_params().time_base());
    video_tab_->pixel_format_field()->SetPixelFormat(e.video_params().format());
    video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(e.video_params().pixel_aspect_ratio());
    video_tab_->interlaced_combobox()->SetInterlaceMode(e.video_params().interlacing());

    video_tab_->SetSelectedCodec(e.video_codec());

    video_tab_->SetColorRange(e.video_params().color_range());

    video_tab_->SetThreads(e.video_threads());

    if (video_tab_->isVisible()) {
      video_tab_->GetCodecSection()->SetOpts(&e);
    }

    video_tab_->SetOCIOColorSpace(e.color_transform().output());

    video_tab_->SetPixFmt(e.video_pix_fmt());

    video_tab_->SetImageSequence(e.video_is_image_sequence());
  }

  audio_enabled_->setChecked(e.audio_enabled());
  if (e.audio_enabled()) {
    audio_tab_->sample_rate_combobox()->SetSampleRate(e.audio_params().sample_rate());
    audio_tab_->channel_layout_combobox()->SetChannelLayout(e.audio_params().channel_layout());
    audio_tab_->sample_format_combobox()->SetSampleFormat(e.audio_params().format());

    audio_tab_->SetCodec(e.audio_codec());

    audio_tab_->bit_rate_slider()->SetValue(e.audio_bit_rate() / 1000);
  }

  if (subtitles_enabled_->isEnabled()) {
    subtitles_enabled_->setChecked(e.subtitles_enabled());
    subtitle_tab_->SetSidecarEnabled(e.subtitles_are_sidecar());
    if (e.subtitles_enabled()) {
      subtitle_tab_->SetSubtitleCodec(e.subtitles_codec());
      if (e.subtitles_are_sidecar()) {
        subtitle_tab_->SetSidecarFormat(e.subtitle_sidecar_fmt());
      }
    }
  }
}

bool ExportDialog::eventFilter(QObject *o, QEvent *e)
{
  // Any parameters in scrollable areas, ignore wheel events so the user doesn't unwittingly change
  // them while trying to scroll through the pages
  if (e->type() == QEvent::Wheel) {
    while ((o = o->parent())) {
      if (o == video_tab_ || o == audio_tab_ || o == subtitle_tab_) {
        e->ignore();
        return true;
      }
    }
  }

  return super::eventFilter(o, e);
}

void ExportDialog::done(int r)
{
  preview_viewer_->ConnectViewerNode(nullptr);

  if (!stills_only_mode_) {
    viewer_node_->SetLastUsedEncodingParams(GenerateParams());
  }

  super::done(r);
}

rational ExportDialog::GetExportLength() const
{
  if (range_combobox_->currentIndex() == kRangeInToOut) {
    return viewer_node_->GetWorkArea()->range().length();
  } else {
    return viewer_node_->GetLength();
  }
}

int64_t ExportDialog::GetExportLengthInTimebaseUnits() const
{
  return Timecode::time_to_timestamp(GetExportLength(), GetSelectedTimebase());
}

void ExportDialog::UpdateViewerDimensions()
{
  preview_viewer_->SetViewerResolution(static_cast<int>(video_tab_->width_slider()->GetValue()),
                                       static_cast<int>(video_tab_->height_slider()->GetValue()));

  VideoParams vp = viewer_node_->GetVideoParams();

  QMatrix4x4 transform = EncodingParams::GenerateMatrix(
        static_cast<EncodingParams::VideoScalingMethod>(video_tab_->scaling_method_combobox()->currentData().toInt()),
        vp.width(),
        vp.height(),
        static_cast<int>(video_tab_->width_slider()->GetValue()),
        static_cast<int>(video_tab_->height_slider()->GetValue())
        );

  preview_viewer_->SetMatrix(transform);
}

}
