/*
 * Copyright (c) 2012, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QDateTime>
#include <QMessageBox>
#include <QImageWriter>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton> // Included so we know that QPushButton inherits QAbstractButton
#include <QFileDialog>
#include <QCheckBox>
#include <QTimer>
#include <QScreen>
#include <QWindow>

#include "scaled_image_widget.h"
#include "screenshot_dialog.h"

namespace rviz
{
ScreenshotDialog::ScreenshotDialog(QWidget* main_window,
                                   QWidget* render_window,
                                   const QString& default_save_dir)
  : QWidget(nullptr) // This should be a top-level window to act like a dialog.
  , main_window_(main_window)
  , render_window_(render_window)
  , save_full_window_(false)
  , delay_timer_(new QTimer(this))
  , first_time_(true)
  , default_save_dir_(default_save_dir)
{
  image_widget_ = new ScaledImageWidget(.5);

  takeScreenshotNow();

  QCheckBox* full_window_checkbox = new QCheckBox("Save entire rviz window");

  button_box_ =
      new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Retry | QDialogButtonBox::Cancel);

  QVBoxLayout* main_layout = new QVBoxLayout;
  main_layout->addWidget(image_widget_, 100);
  main_layout->addWidget(new QLabel("Image will be saved at the original resolution."));
  main_layout->addWidget(full_window_checkbox);
  main_layout->addWidget(button_box_);

  setLayout(main_layout);

  connect(button_box_, &QDialogButtonBox::clicked, this, &ScreenshotDialog::onButtonClicked);
  connect(full_window_checkbox, &QCheckBox::toggled, this, &ScreenshotDialog::setSaveFullWindow);
  connect(delay_timer_, &QTimer::timeout, this, &ScreenshotDialog::onTimeout);
}

void ScreenshotDialog::showEvent(QShowEvent* event)
{
  if (first_time_)
  {
    QPoint center = main_window_->rect().center();
    move(center.x() - width() / 2, center.y() - height() / 2);

    first_time_ = false;
  }
  QWidget::showEvent(event);
}

void ScreenshotDialog::setSaveFullWindow(bool save_full_window)
{
  save_full_window_ = save_full_window;
  takeScreenshot();
}

void ScreenshotDialog::takeScreenshot()
{
  main_window_->raise();
  delay_timer_->start(100);
}

void ScreenshotDialog::onTimeout()
{
  delay_timer_->stop();
  takeScreenshotNow();
  raise();
  activateWindow();
}

void ScreenshotDialog::takeScreenshotNow()
{
  const QWidget* w = save_full_window_ ? main_window_ : render_window_;
  screenshot_ = w->windowHandle()->screen()->grabWindow(w->winId());
  image_widget_->setImage(screenshot_);
}

void ScreenshotDialog::onButtonClicked(QAbstractButton* clicked)
{
  if (clicked == button_box_->button(QDialogButtonBox::Save))
  {
    save();
  }
  else if (clicked == button_box_->button(QDialogButtonBox::Retry))
  {
    takeScreenshot();
  }
  else if (clicked == button_box_->button(QDialogButtonBox::Cancel))
  {
    close();
  }
}

void ScreenshotDialog::save()
{
  QString default_save_file = default_save_dir_ + "/rviz_screenshot_" +
                              QDateTime::currentDateTime().toString("yyyy_MM_dd-hh_mm_ss") + ".png";
  QString filename = QFileDialog::getSaveFileName(this, "Save image", default_save_file);
  if (filename != "")
  {
    QString with_slashes = QDir::fromNativeSeparators(filename);
    QString file_part = with_slashes.section('/', -1);
    default_save_dir_ = QDir::toNativeSeparators(with_slashes.section('/', 0, -2));
    Q_EMIT savedInDirectory(default_save_dir_);

    // If filename has no dot, like "image" or has a dot in the zeroth
    // position, like ".image", add ".png" to give a default file
    // format.
    if (file_part.lastIndexOf(".") <= 0)
    {
      filename += ".png";
    }
    QImageWriter writer(filename);
    if (writer.write(screenshot_.toImage()))
    {
      close();
    }
    else
    {
      QString error_message;
      if (writer.error() == QImageWriter::UnsupportedFormatError)
      {
        QString suffix = filename.section('.', -1);
        QString formats_string;
        QList<QByteArray> formats = QImageWriter::supportedImageFormats();
        formats_string = formats[0];
        for (int i = 1; i < formats.size(); i++)
        {
          formats_string += ", " + formats[i];
        }

        error_message = "File type '" + suffix + "' is not supported.\n" +
                        "Supported image formats are: " + formats_string + "\n";
      }
      else
      {
        error_message = "Failed to write image to file " + filename;
      }

      QMessageBox::critical(this, "Error", error_message);
    }
  }
}

} // end namespace rviz
