/**
    05Robot - Object Manipulation GUI Module

    @author P.Kaczmarek,J.Tomczynski,T.Mankowski
    @version 1.0 2017
*/
#ifndef OBJECTMANIPULATIONWINDOW_H
#define OBJECTMANIPULATIONWINDOW_H

#include <QWidget>
#include "ui_ObjectManipulationWindow.h"
#include "Processor_ObjectExtraction.h"
#include <QVector3D>

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "mat_utils.h"

#include <QDebug>
#include <QMessageBox>
/** \addtogroup ObjectManipulationGUIModule
 *  @{
 */
/**
 @brief Object manipulation window
 
 Integrated object manipulation widget. Includes two modes: preview, where whole source image is visible with all detected objects highlighted. After selecting an object, switches to object manipulation mode, where view fits selected object. Viewport can be further adjusted and proper action selected using one of bin buttons.
 */
class ObjectManipulationWindow : public QWidget
{
    Q_OBJECT
public:
    /**
     @brief Log event types
     */
    enum interfaceLogEvents{manipulation_mode_selected, preview_mode_selected, move_up_selected, move_down_selected, move_left_selected, move_right_selected, rotate_right_selected, rotate_left_selected,zoom_in_selected, zoom_out_selected, zoom_rect_selected, cancel_action_selected, bin_button_selected, look_at_selected, robot_move_started, robot_move_finished, bin_button_toggled} ;
    Q_ENUM(interfaceLogEvents)
    /**
     @brief Base button size in pixels
     */
    Q_PROPERTY(int Interface_size READ interface_size WRITE set_interface_size DESIGNABLE true USER true)
    Q_CLASSINFO("Interface_size", "minimum=20;maximum=300;singleStep=1;");
    /**
     @brief Initial rotation in degrees units
     */
    Q_PROPERTY(int Initial_rotation READ initial_rotation WRITE set_initial_rotation DESIGNABLE true USER true)
    Q_CLASSINFO("Initial_rotation", "minimum=0;maximum=359;singleStep=1;");
    /**
     @brief Rotation step in degrees
     */
    Q_PROPERTY(int Rotation_step READ rotation_step WRITE set_rotation_step DESIGNABLE true USER true)
    Q_CLASSINFO("Rotation_step", "minimum=90;maximum=270;singleStep=90;");
    /**
     @brief View scroll step (fraction of view window)
     */
    Q_PROPERTY(float View_scroll_step READ view_scroll_step WRITE set_view_scroll_step DESIGNABLE true USER true)
    Q_CLASSINFO("View_scroll_step", "minimum=0.01;maximum=1;singleStep=0.01;");
    /**
     @brief Zoom in/out factor
     */
    Q_PROPERTY(float Zoom_in_out_factor READ zoom_in_out_factor WRITE set_zoom_in_out_factor DESIGNABLE true USER true)
    Q_CLASSINFO("Zoom_in_out_factor", "minimum=0.01;maximum=10.0;singleStep=0.1;");
    /**
     @brief Bin descriptors
     */
    Q_PROPERTY(QString Bin_descriptors READ bin_descriptors WRITE set_bin_descriptors DESIGNABLE true USER true)
    /**
     @brief Static image file to display, disables live camera input and switches to object manipulation mode
     */
    Q_PROPERTY(QString Image_file READ image_file WRITE set_image_file DESIGNABLE true USER true)
    /**
     @brief Object list file
     */
    Q_PROPERTY(QString Object_list_file READ object_list_file WRITE set_object_list_file DESIGNABLE true USER true)
    
private:
    /**
     @brief Structure describing single bin button
     */
    struct bin_button {
        /**
         @brief Default button
         */
        bin_button() {}
        /**
         @brief Button based on settings specified in comma-separated list of parameter=value pairs
         
         @param input string containing parameters
         */
        bin_button(QString input);
        /**
         @brief Generates button settings string as comma-separated list of parameter=value pairs
         */
        QString to_string() const;
        /**
         @brief button id, emitted as button is clicked
         */
        int id = -1;
        /**
         @brief horizontal button scale
         */
        float width = 1.0;
        /**
         @brief if true, button image will be replaced with object photo after clicking
         */
        bool photo = false;
        /**
         @brief button text label
         */
        QString label;
        /**
         @brief path to button icon
         */
        QString icon;
        /**
         @brief button color, in hex #RRGGBB format
         */
        QString color;
        /**
         @brief total bin capacity, -1 if unlimited
         */
        int capacity = -1;
        /**
         @brief current bin capacity
         */
        int current_capacity = 1;
        int state = 0;
    };

public:
    /**
     @brief Default constructor, initializes object manipulation window
     
     @param parent pointer to parent widget
     @param flags window flags
     */
    ObjectManipulationWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
    /**
     @brief Default destructor, cleans up
     */
    ~ObjectManipulationWindow();
    /**
     @brief Gets base button size in pixels
     */
    int interface_size() const;
    /**
     @brief Sets base button size in pixels
     */
    void set_interface_size(int interface_size_);
    /**
     @brief Gets initial rotation in degrees
     */
    int initial_rotation() const;
    /**
     @brief Sets initial rotation in degrees
     */
    void set_initial_rotation(int initial_rotation);
    /**
     @brief Gets rotation step in degrees
     */
    int rotation_step() const;
    /**
     @brief Sets rotation step in degrees
     */
    void set_rotation_step(int rotation_step);
    /**
     @brief Gets view scroll step (fraction of view window)
     */
    float view_scroll_step() const;
    /**
     @brief Sets view scroll step in pixels
     */
    void set_view_scroll_step(float view_scroll_step);
    /**
     @brief Gets zoom in/out factor
     */
    float zoom_in_out_factor() const;
    /**
     @brief Sets zoom in/out factor
     */
    void set_zoom_in_out_factor(float zoom_in_out_factor);
    /**
     @brief Gets bin descriptors
     */
    QString bin_descriptors() const;
    /**
     @brief Sets bin descriptors
     */
    void set_bin_descriptors(const QString &bin_descriptors);
    /**
     @brief Gets image file
     */
    QString image_file() const;
    /**
     @brief Sets image file
     */
    void set_image_file(const QString &image_file);
    /**
     @brief Gets object list file
     */
    QString object_list_file() const;
    /**
     @brief Sets object list file
     */
    void set_object_list_file(const QString &object_list_file);
    /**
       @brief Shows help for complex gestures
      */
    void show_gestures_legend(int is_show);
private:
    void generate_buttons();
    cv::Mat get_object_image(cv::Size dst_size = cv::Size(0, 0), float angle = 0.0, float zoom = 0.0, float view_offset_x = 0.0, float view_offset_y = 0.0);
    
    int m_show_complex_gestures_help;
    int m_interface_size;
    float m_rotation_step;
    float m_initial_rotation;
    float m_view_scroll_step;
    float m_zoom_in_out_factor;
    QString m_image_file;
    QString m_object_list_file;
    
    bool interface_active_ = true;
    
    enum update_mode_t {
        update_mode_live, update_mode_single, update_mode_pause
    };
    
    update_mode_t update_mode_ = update_mode_live;
    
    Ui::ObjectManipulationWindow ui;
    QVector<QPair<QVector2D, QToolButton*> > buttons_;
    
    QButtonGroup bin_buttons_group_;
    QVector<bin_button> bin_buttons_;
    
    cv::Mat current_image_;
    QVector<sObject> current_objects_;
    
    void refresh();
    void update_object_selection_view();
    void redraw_object_selection_view() {
        ui.graphicsView_preview->fitInView(ui.graphicsView_preview->scene()->sceneRect(), Qt::AspectRatioMode::KeepAspectRatio);
    }
    void update_object_manipulation_view();
    void redraw_object_manipulation_view() {
        ui.graphicsView_object->fitInView(ui.graphicsView_object->scene()->sceneRect(), Qt::AspectRatioMode::KeepAspectRatio);
    }

    /* current view */
    int highlighted_object_ = -1;
    int selected_object_ = -1;
    
    //cv::Mat current_transformation_;
    float current_zoom_ = 0.0;
    float current_angle_ = 0.0;
    float current_offset_x_ = 0.0;
    float current_offset_y_ = 0.0;
    
    cv::Size manipulation_preview_size_ = cv::Size(1600, 800);//cv::Size(1024, 768)
    
    void reset_view() {
        current_zoom_ = 0.0;
        current_angle_ = m_initial_rotation;
        current_offset_x_ = 0.0;
        current_offset_y_ = 0.0;
        
        update_object_manipulation_view();
    }
    
    QRectF target_view_rect_;

    
protected:
    void resizeEvent(QResizeEvent *event);

private slots:
    void new_object_data(cv::Mat image, QVector<sObject> objects);
    
public slots:
    /**
     @brief Triggers interface redraw
     */
    void on_stackedWidget_currentChanged(int /* index */) {
        update_interface();
    }
    
    /* user interface */
    
    /**
     @brief Triggers single image preview update in paused mode
     */
    void on_toolButton_refresh_clicked();
    /**
     @brief Rotates image counterclockwise
     */
    void on_toolButton_rotate_left_clicked();
    /**
     @brief Rotates image clockwise
     */
    void on_toolButton_rotate_right_clicked();
    /**
     @brief Zooms in image
     */
    void on_toolButton_zoom_in_clicked();
    /**
     @brief Zooms out image
     */
    void on_toolButton_zoom_out_clicked();
    /**
     @brief Moves current viewport left
     */
    void on_toolButton_view_left_clicked();
    /**
     @brief Moves current viewport right
     */
    void on_toolButton_view_right_clicked();
    /**
     @brief Moves current viewport up
     */
    void on_toolButton_view_up_clicked();
    /**
     @brief Moves current viewport down
     */
    void on_toolButton_view_down_clicked();
    /**
     @brief Resets viewport to fit current selected object
     */
    void on_toolButton_view_reset_clicked();
    /**
     @brief Returns to preview mode
     */
    void on_toolButton_cancel_clicked();
    /**
     @brief Resets buttons capacity counters
     */
    void reset_buttons();
    
    /**
     @brief Sets current object manipulation viewport
     
     @param rectangle new viewport in window coordinates
     */
    void zoom_rect_widget(QRect rectangle);
    /**
     @brief Sets current viewport center
     
     @param point point to look at, in window coordinates
     */
    void look_at(QPointF point);
    /**
     @brief Sets current object manipulation viewport
     
     @param rectangle new viewport in image coordinates
     */
    void zoom_rect(QRectF rectangle);
    /**
     @brief Starts setting current object manipulation viewport
     
     @param point top-left point of new viewport
     */
    void zoom_rect_start(QPointF point);
    /**
     @brief Ends setting current object manipulation viewport
     
     @param point bottom-right point of new viewport
     */
    void zoom_rect_stop(QPointF point);
    /**
     @brief Redraws current interface
     */
    void update_interface();
    /**
     @brief Performs action assigned to selected bin button
     
     @param button_i button index
     */
    void bin_button_clicked(int button_i);
    /**
     @brief Tries to select an object, if an object was under given coordinates switches to object manipulation mode
     
     @param point mouse location, in preview image coordinates
     */
    void preview_clicked(QPointF point);
    /**
     @brief Mouse movement over preview window slot, highlights underlying objects
     
     @param point mouse location, in preview image coordinates
     */
    void preview_mouse_moved(QPointF point);
    /**
     @brief Switches interface to preview mode, resets all selections, turns on live camera preview
     */
    void preview_mode();
    /**
     @brief Switches interface to object manipulation mode if an object was selected, pauses camera preview
     */
    void manipulation_mode();
    /**
     @brief Locks interface as robot starts performing trajectory
     */
    void commandStarted();
    /**
     @brief Unlocks interface after robot finishes performing trajectory
     */
    void commandExecuted();
    /**
     @brief Locks interface when robot emergency signal is active
     */
    void robotEmergencyStopped();
public:
    /**
     @brief returns preview QGraphicsView widget
     */
    QGraphicsView* getPreviewWidget();
    /**
     @brief returns object manipulaion QGraphicsView widget
     */
    QGraphicsView* getManipulationWidget();
    enum previev_types_t {preview_id=0, object_manipultion_id=1};
    previev_types_t getPreviewId() {
        return (previev_types_t)ui.stackedWidget->currentIndex();
    }

signals:
    /**
     @brief emited on all manipulation events
     */
    void logEvent(ObjectManipulationWindow::interfaceLogEvents eventID, int selected_object, int param, QPointF point, QPointF size, float angle);
    /**
     @brief emited after clicking a bin button in object manipulation mode
     
     @param pointInMeters point to pick object at, in scene coordinates, in meters
     @param angle angle to grab object, in radians
     @param bin id of category in which object should be placed
     */
    void pickObject(QVector3D pointInMeters, float object_height, float angle, int bin);

};
/** @}*/
#endif // OBJECTMANIPULATIONWINDOW_H
