#pragma once

#include <queue>
#include <functional>
#include "Array.h"
#include "Mat3.h"
#include "Punkt.h"
#include "Rect2.h"

namespace Framework
{
    typedef Vec2< float > Vertex;
    class Bild;

    struct WeltInfo
    {
        float airResistance;
        bool hasSize;
        bool circular;
        Punkt size;
    };

    class Object2D : public virtual ReferenceCounter
    {
    protected:
        std::queue< std::function< void() > > actions;
        Vertex position;
        Vertex speed;
        float rSpeed;
        float rotation;
        float size;
        bool collision;

    public:
        DLLEXPORT Object2D();
        DLLEXPORT virtual ~Object2D();
        //! Übergibt einen Void Funktionspointer auf eine Aktion die einmalig vom Hauptthread ausgeführt werden soll. (Passiert nach dem Tick)
        DLLEXPORT void postAction( std::function< void() > action );
        //! Fügt einen Schub in die ausbreitungsrichtung der Explusion zur Bewegung des Objektes hinzu
        //! \param worldPos Die Position des Explusionsuhrsprungs
        //! \param intensity Die Intensität der Explusion
        DLLEXPORT void explosion( Vertex worldPos, float intensity );
        //! Übt auf das Objekt einen Impuls aus, der sowohl die geschwindigkeit, als auch die Drehungsgeschwindigkeit des Objektes beeinflusst
        //! \param start Die Startposition des Impulses in der Welt
        //! \param speed Die Geschwindigkeit des Impulses in der Welt
        //! \param strength Stärke des aufpralls
        DLLEXPORT virtual void impuls( Vertex start, Vertex speed, float strength = 1.f );
        //! Setzt die Geschwindigkeit des Objektes in der Welt
        //! \param speed Anzahl der zurückgelegten Koordinaten pro Sekunde
        DLLEXPORT void setSpeed( Vertex speed );
        //! Setzt die Geschwindigkeit des Objektes in der Welt
        //! \param x Anzahl der zurückgelegten x Koordinaten pro Sekunde
        //! \param y Anzahl der zurückgelegten y Koordinaten pro Sekunde
        DLLEXPORT void setSpeed( float x, float y );
        //! Setzt die Position des Objektes in der Welt
        //! \param pos Die Position in Weltkoordinaten
        DLLEXPORT void setPosition( Vertex pos );
        //! Setzt die Position des Objektes in der Welt
        //! \param x Die X Position in Weltkoordinaten
        //! \param y Die Y Position in Weltkoordinaten
        DLLEXPORT void setPosition( float x, float y );
        //! Setzt die Rotationsgeschwindigkeit in Radiant pro Sekunde
        //! \param ds Die neue Rptationsgeschwindigkeit
        DLLEXPORT void setDrehungSpeed( float ds );
        //! Setzt die Drehung des Models gegen den Uhrzeigersinn
        //! \param drehung Der winkel in Bogenmas
        DLLEXPORT void setDrehung( float drehung );
        //! Fügt zum aktuellen Drehungswinkel etwas hinzu
        //! \param drehung Der Winkel in Bogenmas, der hinzugefügt werden soll
        DLLEXPORT void addDrehung( float drehung );
        //! Setzt die Skallierung des Modells
        //! \param size Der Faktor, mit dem Skalliert wird
        DLLEXPORT void setSize( float size );
        //! Addiert zur Skallierung einen bestimmten Wert hinzu
        //! \param size Der Wert, der zur skallierung hinzugefügt werden soll
        DLLEXPORT void addSize( float size );
        //! Legt fest, ob andere Objekte mit diesem Objekt zusammenschtoßen können
        //! \param handle 0, falls keine Kollisionen existieren
        DLLEXPORT void setCollision( bool handle );
        //! Prüft ob eine Collision mit einem anderen Objekt vorliegt und passt die Geschwindigkeiten beider Objekte entsprechend an
        DLLEXPORT virtual bool handleCollision( Object2D *obj );
        //! Verarbeitet vargangene Zeit und aktualisiert die Position und die Drehung des Objektes in der Welt
        //! \param zeit Die vergangene Zeit in Sekunden
        DLLEXPORT virtual bool tick( const WeltInfo &info, double zeit );
        //! Zeichnet das Objekt in ein Bild
        //! \param kamMat Due Kamera Matrix, welche einen Punkt aus Weltkoordinaten in Bildschirmkoordinaten umwandelt
        //! \param zRObj Das Bild, in welches gezeichnet werden soll
        //! \param ignoreTransparentFlag wenn 1, dann werden auch collisionen mit transparenten polygonen beachtet
        virtual void render( Mat3< float > &kamMat, Bild &zRObj, const char *ignoreTransparentFlag ) = 0;
        //! Gibt zurück, ob ein Punkt in dem Objekt enthalten ist
        //! \param p Der Punkt
        //! \param ignoreTransparentFlag wenn 1, dann werden auch collisionen mit transparenten polygonen beachtet
        DLLEXPORT virtual bool istPunktInnen( Vertex p, bool ignoreTransparentFlag = 0 ) const;
        //! Überprüft, ob eine Linie im Objekt enthalten ist
        //! \param a Der startpunkt der Linie
        //! \param b Der endpunkt der Linie
        //! \param ignoreTransparentFlag wenn 1, dann werden auch collisionen mit transparenten polygonen beachtet
        DLLEXPORT virtual bool istLinieInnen( Vertex a, Vertex b, bool ignoreTransparentFlag = 0 ) const;
        //! Überprüft, ob sich das Objekt mit einem anderen Überschneidet
        //! \param zObj Ein Zeiger auf das andere Objekt ohne erhöhten Reference Counter
        //! \param sp Ein Zeiger auf einen Punkt, in dem der Schnittpunkt gespeichert wird
        //! \param end 0, falls alle Ecken beider Objekte überprüft werdden sollen. 1, falls nur die Punkte dieses Models im anderen gesucht werden sollen
        DLLEXPORT virtual bool istModelInnen( const Object2D *zObj, Vertex *sp = 0, bool end = 0, bool ignoreTransparent = 0 ) const;
        //! Gibt eine Matrix zurück, welche einen Punkt aus Objektkoordinaten in Bildschirmkoordinaten umrechnet
        DLLEXPORT Mat3< float > getObjectMatrix() const;
        //! Gibt eine Matrix zurück, welche einen Punkt aus Bildschirmkoordinaten in Objektkoordinaten umrechnet
        DLLEXPORT Mat3< float > getInverseObjectMatrix() const;
        //! Rechnet einen Punkt aus Welt Koordinaten in Objektkoordinaten um
        //! \param worldPos Die Position des Punktes in der Welt
        DLLEXPORT Vertex getObjectPos( Vertex worldPos ) const;
        //! Rechnet eine Richtung aus Weltkoordinaten in Objektkoordinaten um
        //! \param worldDir Die Richtung in Weltkoordinaten
        DLLEXPORT Vertex getObjectDir( Vertex worldDir ) const;
        //! Rechnet einen Punkt aus Objektkoordinaten in Weltkoordinaten um
        //! \param worldPos Die Position des Punktes in Objektkoordinaten
        DLLEXPORT Vertex getWorldPos( Vertex objectPos ) const;
        //! Rechnet eine Richtung aus Objektkoordinaten in Weltkoordinaten um
        //! \param worldDir Die Richtung in Objektkoordinaten
        DLLEXPORT Vertex getWorldDir( Vertex objectDir ) const;
        //! Gibt die Geschwindigkeit des Objektes zurück
        DLLEXPORT Vertex getSpeed() const;
        //! gibt die Position des Objektes zurück
        DLLEXPORT Vertex getPosition() const;
        //! gibt die Drehgeschwindigkeit des Objektes zurück
        DLLEXPORT float getDrehungSpeed() const;
        //! gibt die Drehung des Objektes zurück
        DLLEXPORT float getDrehung() const;
        //! gibt den Skallierungs Objektes zurück
        DLLEXPORT float getSize() const;
        //! Gibt eine Boundingbox zurück, in der sich alle Punkte des Objektes befinden ( in Weltkoordinaten)
        DLLEXPORT virtual Rect2< float > getBoundingBox() const = 0;
        //! Bestimmt den Aufschlagspunkt eines Strahls, der von pos ausgehend in Richtung dir abgegeben wird.
        //! \param pos Der Stützvektor der Linie
        //! \param dir Der richtungsvektor der Linie
        //! \param hitPoint eine referenz auf die variable in dem der schnittpunkt gespeichert werden soll
        //! \return 1, fals ein Schnittpunkt existiert
        DLLEXPORT virtual bool calcHitPoint( Vertex pos, Vertex dir, Vertex &hitpoint ) const;
        //! Ermittelt die Fläche des Objektes, welche rechtwinklich zum Bewegungsvektor steht
        DLLEXPORT virtual float getLuftWiederstand() const;
        //! Gibt die Masse des Objektes zurück
        DLLEXPORT virtual float getMasse() const;
        //! Legt fest, ob andere Objekte mit diesem Objekt zusammenschtoßen können
        //! \return 0, falls keine Kollisionen existieren
        DLLEXPORT bool canCollide();
    };

    class Welt2D : public virtual ReferenceCounter
    {
    private:
        RCArray< Object2D > *objects;
        WeltInfo info;

        void render( Mat3< float > &kamMat, Punkt size, Bild &zRObj, int xOffset, int yOffset, const char *kamName );

    public:
        DLLEXPORT Welt2D();
        DLLEXPORT ~Welt2D();
        DLLEXPORT void setAirResistance( float resistance );
        DLLEXPORT void setSize( int width, int height );
        DLLEXPORT void setSize( bool hasSize );
        DLLEXPORT void setCircular( bool circular );
        DLLEXPORT Object2D *zObjectAt( int x, int y, bool ignoreTransparentFlag = 0 );
        DLLEXPORT Object2D *getObjectAt( int x, int y, bool ignoreTransparentFlag = 0 );
        DLLEXPORT void addObject( Object2D *obj );
        DLLEXPORT void removeObject( Object2D *zObj );
        DLLEXPORT void removeAll();
        DLLEXPORT void explosion( Vertex worldPos, float intensity, float maxRad );
        DLLEXPORT void impuls( Vertex worldPos, Vertex worldDir );
        DLLEXPORT bool tick( double zeit );
        DLLEXPORT void render( Mat3< float > &kamMat, Punkt size, Bild &zRObj, const char *kamName );
        DLLEXPORT const WeltInfo &getWorldInfo() const;
        DLLEXPORT Iterator< Object2D * > getMembers();
    };
}