#pragma once

#include "Mat4.h"
#include "Point.h"
#include "Drawing3D.h"

//! DirectX 11 Types

struct D3D11_VIEWPORT;

namespace Framework
{
    struct MouseEvent; //! MouseEvent.h
    class Render3D;      //! Render3D.h
    class World3D;        //! World3D.h

    struct ViewPort
    {
        float x;
        float y;
        float width;
        float height;
        float front;
        float back;
    };

    //! A 3D camera that draws a section of a 3D world into a specific
    //! area of the screen
    class Cam3D : public virtual Framework::ReferenceCounter
    {
    public:
        class Style
        {
        public:
            static const __int64 Movable = 0x1;
            static const __int64 Rotatable = 0x2;
            static const __int64 Zoomable = 0x4;
            static const __int64 Tick = 0x8;
        };

    protected:
        bool rend;

    private:
        Mat4<float> view;
        Mat4<float> proj;

        float openingAngle;
        float minZ;
        float maxZ;
        Vec3<float> pos;
        float rotX;
        float rotY;
        float rotZ;

        ViewPort viewport;
        World3D* welt;
        __int64 style;
        float speed;

        //! Updates the view and projection matrices
        void updateMatrix();

    public:
        //! Constructor
        DLLEXPORT Cam3D();
        //! Destructor
        DLLEXPORT ~Cam3D();
        //! Sets the position of the camera in the 3D world
        DLLEXPORT void setPosition(Vec3<float> pos);
        //! Zooms in by moving the camera slightly towards the target
        //! \param val The distance the camera should move
        DLLEXPORT void scrollIn(float val);
        //! Zooms out by moving the camera slightly away from the target
        //! \param val The distance the camera should move
        DLLEXPORT void scrollOut(float val);
        //! Aligns the camera so that it points exactly at a specific point
        //! \param ziel The point the camera should point at
        DLLEXPORT void setAusrichtung(Vec3<float> ziel);
        //! Sets the rotation of the camera around individual axes
        //! \param rotation The rotation around individual axes
        DLLEXPORT void setRotation(Vec3<float> rotation);
        //! Sets the position of the image on the screen
        //! \param p A point with x and y coordinates in pixels
        DLLEXPORT void setScreenPosition(Point p);
        //! Sets the position of the image on the screen
        //! \param x The x coordinate in pixels
        //! \param y The y coordinate in pixels
        DLLEXPORT void setScreenPosition(int x, int y);
        //! Sets the size of the image on the screen
        //! \param p A point with x as width and y as height in pixels
        DLLEXPORT void setScreenSize(Point p);
        //! Sets the size of the image on the screen
        //! \param br The width in pixels
        //! \param hi The height in pixels
        DLLEXPORT void setScreenSize(int br, int hi);
        //! Sets the world to be drawn
        //! \param w The world
        DLLEXPORT void setWorld(World3D* w);
        //! Sets the style of the camera
        //! \param style The new style consisting of flags from the
        //! associated Style class
        DLLEXPORT void setStyle(__int64 style);
        //! Sets the style of the camera
        //! \param style All style flags to be changed
        //!  add_remove: 1 if the style should be added. 0 if the style
        //!  should be removed
        DLLEXPORT void setStyle(__int64 style, bool add_remove);
        //! Adds style flags
        //! \param style The style to add
        DLLEXPORT void addStyle(__int64 style);
        //! Removes style flags
        //! \param style The style to remove
        DLLEXPORT void removeStyle(__int64 style);
        //! Set the movement speed per second if the camera has style Movable
        DLLEXPORT void setMovementSpeed(float speed);
        //! Processes elapsed time
        //! \param tickval The time in seconds since the last call of this
        //! function \return true if the image needs to be redrawn, false otherwise.
        DLLEXPORT virtual bool tick(double tv);
        //! Processes a mouse event
        //! \param me The mouse event to process
        DLLEXPORT virtual void doMouseEvent(MouseEvent& me);
        //! Processes a keyboard event
        //! \param te The keyboard event to process
        DLLEXPORT virtual void doKeyboardEvent(KeyboardEvent& te);
        //! Returns whether specific styles are set
        //! \param style The styles to check
        //! \return 1 if all styles in style are set
        DLLEXPORT bool hasStyle(__int64 style) const;
        //! Returns whether specific styles are not set
        //! \param style The styles to check
        //! \return 1 if all styles in style are not set
        DLLEXPORT bool hasStyleNot(__int64 style) const;
        //! Returns a pointer to the viewport
        DLLEXPORT const ViewPort* zViewPort() const;
        //! Returns the position of the camera in the world
        DLLEXPORT const Vec3<float>& getWorldPosition() const;
        //! Returns the position in the world
        //! \param screen The position on the screen to translate
        DLLEXPORT const Vec3<float> getWorldPosition(Point screen) const;
        //! Returns the direction of the camera in the world
        //! \param screen The position on the screen to translate
        DLLEXPORT const Vec3<float> getWorldDirection(Point screen) const;
        //! Returns the projection matrix of the camera
        DLLEXPORT const Mat4<float>& getProjectionMatrix() const;
        //! Returns the view matrix of the camera
        DLLEXPORT const Mat4<float>& getViewMatrix() const;
        //! Returns the rotation around individual axes
        DLLEXPORT const Vec3<float> getRotation() const;
        //! Returns the position of the camera on the screen
        DLLEXPORT const Point getScreenPos() const;
        //! Returns the size of the camera on the screen
        DLLEXPORT const Point getScreenSize() const;
        //! Returns the world
        DLLEXPORT World3D* getWorld() const;
        //! Returns the world without increased reference counter
        DLLEXPORT World3D* zWorld() const;
    };
} // namespace Framework