//=============================================================================
// Copyright (C) 2011-2017 The pmp-library developers
// 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 copyright holder 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 HOLDER 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.
//=============================================================================
#pragma once
//=============================================================================

#include <pmp/Window.h>
#include <pmp/MatVec.h>

#include <string>
#include <vector>

//=============================================================================

namespace pmp {

//=============================================================================

//! \addtogroup gl gl
//! @{

//! A simple GLFW viewer with trackball user interface
class TrackballViewer : public Window
{

public: //------------------------------------------------------ public methods
    //! constructor
    TrackballViewer(const char* title, int width, int height,
                    bool showgui = true);

    //! destructor
    virtual ~TrackballViewer();

    //! define the center and radius of the scene/
    //! used for trackball rotation
    void setScene(const vec3& center, float radius);

    //! adjust camera such that the whole scene (defined by set_scene()) is visible
    void viewAll();

protected: //----------------------------------- callbacks as member functions
    //! this function is called when the scene has to be rendered. it
    //! clears the buffers, calls the draw() method, and performs buffer swap
    virtual void display(void) override;

    //! this function handles keyboard events
    virtual void keyboard(int key, int code, int action, int mods) override;

    //! this function handles mouse button events
    virtual void mouse(int button, int action, int mods) override;

    //! this function handles mouse motion (passive/active position)
    virtual void motion(double xpos, double ypos) override;

    //! this function handles mouse scroll events
    virtual void scroll(double xoffset, double yoffset) override;

    //! this function is called if the window is resized
    virtual void resize(int width, int height) override;

protected: //------------------------------------------- handling of draw modes
    //! reset the list of draw modes
    void clearDrawModes();

    //! add a draw mode
    unsigned int addDrawMode(const std::string& drawMode);

    //! activate a draw mode
    void setDrawMode(const std::string& drawMode);

protected: //-------------------------------------------------------- rendering
    //! initialize all OpenGL states
    virtual void init();

    //! this function is responsible for rendering the scene
    virtual void draw(const std::string& drawMode) = 0;

protected: //-------------------------------------------- trackball interaction
    //! turn a mouse event into a rotation around the scene center. calls rotate().
    void rotation(int x, int y);

    //! turn a mouse event into a translation in the view plane. calls translate().
    void translation(int x, int y);

    //! turn a mouse event into a zoom, i.e., translation in z-direction. calls translate().
    void zoom(int x, int y);

    //! get 3D position under the mouse cursor
    bool pick(int x, int y, vec3& result);

    //! fly toward the position under the mouse cursor and set rotation center to it
    void flyTo(int x, int y);

    //! translate the scene and update modelview matrix
    void translate(const vec3& trans);

    //! rotate the scene (around its center) and update modelview matrix
    void rotate(const vec3& axis, float angle);

    //! virtual trackball: map 2D screen point to unit sphere. used by rotate().
    bool mapToSphere(const ivec2& point, vec3& result);

protected: //----------------------------------------------------- private data
    //! draw modes
    unsigned int             m_drawMode;
    unsigned int             m_nDrawModes;
    std::vector<std::string> m_drawModeNames;

    //! scene position and dimension
    vec3  m_center;
    float m_radius;

    //! projection parameters
    float m_near, m_far, m_fovy;

    //! OpenGL matrices
    mat4 m_projectionMatrix;
    mat4 m_modelviewMatrix;

    //! trackball helpers
    vec2  m_mousePos2D;
    ivec2 m_lastPoint2D;
    vec3  m_lastPoint3D;
    bool  m_lastPointOk;
    bool  m_buttonDown[7];
    int   m_modifiers;
    int   m_wheelPos;
};

//=============================================================================
//! @}
//=============================================================================
} // namespace pmp
//=============================================================================
