
#ifndef __WFX_RENDERER_CAMERA_H__
#define __WFX_RENDERER_CAMERA_H__

#include <WFX/WFXIncludes.h>

#include <WFX/math/Vector3.h>
#include <WFX/math/Plane.h>
#include <WFX/scene/BoundingVolume.h>

namespace WFX {

    namespace renderer {

        /**
         * clipping planes
         */
        enum ClippingPlanes {
            FRUSTUM_NEAR = 0,
            FRUSTUM_FAR = 1,
            FRUSTUM_LEFT = 2,
            FRUSTUM_RIGHT = 3,
            FRUSTUM_TOP = 4,
            FRUSTUM_BOTTOM = 5,
            FRUSTUM_MAX = 6,
            MAX_PLANES = 32
        };

        /**
         * projection types:
         * ARCHITECTURAL or PERSPECTIVE
         */
        enum ProjectionMode {
            PM_ORTHOGRAPHIC,
            PM_PERSPECTIVE
        };

        /**
         * Render Mode
         */
        enum RenderMode {
            RM_POINTS,
            RM_WIREFRAME,
            RM_SOLID
        };
        
        /**
         * basic camera class
         * also stores the view Frustum
         */
        class Camera {
            public:
                /**
                 * Ctor/Dtor
                 */
                Camera();
                virtual ~Camera();

                /**
                 * sets cam pos, view and up
                 */
                void init(math::Vector3& p, math::Vector3& v, math::Vector3& u);

                /**
                 * "walks" the camera forward or backward along the view vector
                 * @param: r - the distance to move the camera
                 */
                void walk(Real r);

                /**
                 * "strafes" the camera to the left or right
                 * along a vector perpenticular to the view and up vectors
                 * @param: r - the distance to move the camera
                 */
                void strafe(Real r);

                /**
                 * rotates the camera around the position
                 * using axis-angle rotation
                 */
                void rotate(Real angle, math::Vector3& axis);
                /**
                 * calls rotate with a vector v = (View - Position).cross(Up).normalize()
                 */
                inline void rotate(Real angle) {
                    math::Vector3 v = View - Position;
                    v = v.cross(Up);
                    v.normalize();
                    rotate(angle,v);
                }

                /**  updates the camera */
                virtual void update() {
                }
                /**
                 * updates frustum
                 * this function is called by the renderer each time the camera is set 
                 * to be part of the render context
                 */
                void updateFrustum();
                
                /** returns the Projectionmode */
                ProjectionMode getProjectionMode() {
                    return PMode;
                }
                /** sets the ProjectionMode */
                void setProjectionMode(ProjectionMode pm) {
                    PMode = pm;
                }
                /** returns Rendermode  */
                RenderMode getRenderMode() {
                    return RMode;
                }
                /** sets RenderMode */
                void setRenderMode(RenderMode m) {
                    RMode = m;
                }
                /** return the Position */
                inline math::Vector3& getPos() {
                    return Position;
                }
                /** return the View vector */
                inline math::Vector3& getView() {
                    return View;
                }
                /** return the Up vector */
                inline math::Vector3& getUp() {
                    return Up;
                }
                /** return the Field of View Y-direction */
                inline Real getFOVY() {
                    return FOVY;
                }
                /** return the Far clipping distance */
                inline Real getFar() {
                    return Far;
                }
                /** return the Near clipping distance */
                inline Real getNear() {
                    return Near;
                }
                /** set the Far clipping distance */
                inline void setFar(Real r) {
                    Far = r;
                }
                /** set the Near clipping distance */
                inline void setNear(Real r) {
                    Near = r;
                }

                /** checks whether the given Boundingvolume is in the Frustum */
                bool culled(scene::BoundingVolume* b);
                /** checks whether the given Point is in the Frustum */
                bool pointInFrustum(WFX::math::Vector3& p);

                /** debug info */
                void printDebug();
                
                
            protected:
                /** Camera position, view and up vectors */
                math::Vector3 Position, View, Up;
                /** Projection mode */
                ProjectionMode PMode;
                /** rendermode */
                RenderMode RMode;
                /** culling planes */
                math::Plane CullPlanes[MAX_PLANES];
                /** Camera y-direction field-of-view */
                Real FOVY;
                /** far clipping distance */
                Real Far;
                /** near clipping distance */
                Real Near;
                
               
                
               
                
                
        };

    }

}


#endif //include guard


