/********************************************************************
 * Copyright (C) 2015 Liangliang Nan <liangliang.nan@gmail.com>
 * https://3d.bk.tudelft.nl/liangliang/
 *
 * This file is part of Easy3D. If it is useful in your research/work,
 * I would be grateful if you show your appreciation by citing it:
 * ------------------------------------------------------------------
 *      Liangliang Nan.
 *      Easy3D: a lightweight, easy-to-use, and efficient C++ library
 *      for processing and rendering 3D data.
 *      Journal of Open Source Software, 6(64), 3255, 2021.
 * ------------------------------------------------------------------
 *
 * Easy3D is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 3
 * as published by the Free Software Foundation.
 *
 * Easy3D 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 "viewer.h"

#include <easy3d/renderer/texture.h>
#include <easy3d/renderer/shape.h>
#include <easy3d/util/dialog.h>
#include <easy3d/util/resource.h>


using namespace easy3d;

ImageViewer::ImageViewer(const std::string& title, const std::string& image_file)
    : Viewer(title)
    , scale_(1.0f)
{
    image_file_ = image_file;

    manual_ =
            "------------ Image Viewer usage ---------- \n"
            "Press 'Ctrl + O' to open an image\n"
            "Use wheel to zoom in/out\n"
            "Press 'Space' to reset the view\n"
            "------------------------------------------ \n";
}


ImageViewer::~ImageViewer() {
    delete texture_;

    // Not needed: it will be called in the destructor of the base class
    //Viewer::cleanup();
}


void ImageViewer::init() {
    Viewer::init();
    texture_ = Texture::create(image_file_);
    fit_screen();
}


void ImageViewer::compute_image_region(int& x, int& y, int& w, int& h) const {
    w = static_cast<int>(texture_->width() * scale_);
    h = static_cast<int>(texture_->height() * scale_);
    x = static_cast<int>((width() - w) * 0.5f);
    y = static_cast<int>((height() - h) * 0.5f);
}


bool ImageViewer::key_press_event(int key, int modifiers) {
    if (key == KEY_O && modifiers == MODIF_CTRL) {
        const std::string title = "Please choose an image file";
        const std::string default_path = resource::directory() + "/data/";
        const std::vector<std::string> filters = {
            "Image Files (*.png *.jpg *.bmp *.ppm *.tga)", "*.png *.jpg *.bmp *.ppm *.tga"
        };

        const std::string& file_name = dialog::open(title, default_path, filters);
        if (file_name.empty())
            return false;

        if (texture_)
            delete texture_;
        texture_ = Texture::create(file_name);
        fit_screen();
        return texture_ != nullptr;
    }
    if (key == KEY_SPACE) {
        fit_screen();
        return true;
    }
    else
        return Viewer::key_press_event(key, modifiers);
}


void ImageViewer::fit_screen(const Model *model) {
    (void)model;
    if (texture_ == nullptr)
        return;
    const int image_w = texture_->width();
    const int image_h = texture_->height();
    float image_as = image_w / static_cast<float>(image_h);
    float viewer_as = width() / static_cast<float>(height());
    if (image_as < viewer_as) // thin
        scale_ = height() / static_cast<float>(image_h);
    else
        scale_ = width() / static_cast<float>(image_w);
    update();
}


bool ImageViewer::mouse_scroll_event(int x, int y, int dx, int dy) {
    (void)x;
    (void)y;
    (void)dx;
    if (dy > 0)
        scale_ *= 1.1f;
    else if (dy < 0)
        scale_ /= 1.1f;
    update();

    return false;
}


void ImageViewer::draw() const {
	if (texture_ == nullptr)
		return;

    int x, y, w, h;
    compute_image_region(x, y, w, h);

    const Rect quad(x, x + w, y, y + h);
    shape::draw_quad_filled(quad, texture_->id(), width(), height(), -0.9f);
}