#pragma once

#include "Array.h"
#include "Critical.h"
#include "GraphicsApi.h"
#include "Model3DCollection.h"
#include "Vec3.h"

namespace Framework
{
    class Drawable3D;    //! Drawing.h
    class Render3D;      //! Render3D.h
    struct MouseEvent3D; //! MouseEvent.h
    class Model3D;
    class DXBuffer;
    class World3D;

    //! Stores all 3D drawings of a scene
    class World3D : public Model3DCollection
    {
    protected:
        DiffuseLight* diffuseLights;
        RCArray<Model3DCollection> modelCollections;
        int diffuseLightCount;
        PointLight* pointLights;
        int pointLightCount;

    private:
        RCArray<Model3D>* members;
        bool rend;
        ReadWriteLock lock;

    public:
        //! Constructor
        DLLEXPORT World3D();
        //! Destructor
        DLLEXPORT virtual ~World3D();
        //! returns a lock for reading the object
        DLLEXPORT Lock& readLock();
        //! returns a lock for writing the object
        DLLEXPORT Lock& writeLock();
        //! Adds an object to the world
        //! \param obj The object to add
        DLLEXPORT void addDrawable(Model3D* obj);
        //! Removes an object from the world
        //! \param obj The object to remove (without increased reference
        //! counter)
        DLLEXPORT void removeDrawable(Model3D* zObj);
        //! Adds a collection of objects to the world
        //! \param collection The collection to add
        DLLEXPORT void addCollection(Model3DCollection* collection);
        //! removes a collection of models from the world
        //! \param zCollection The collection to remove
        DLLEXPORT void removeCollection(Model3DCollection* zCollection);
        //! Processes a mouse event
        //! \param me The mouse event to process
        DLLEXPORT void doMouseEvent(MouseEvent3D& me);
        //! Processes elapsed time
        //! \param tickval The time in seconds since the last call of this
        //! function \return true if the object changed, false otherwise.
        DLLEXPORT virtual bool tick(double tickval);
        //! Calculates the color of a view ray from a specific point
        //! in a specific direction \param point The origin of the ray
        //! \param dir The direction of the ray \return The color of the ray
        DLLEXPORT virtual int traceRay(Vec3<float>& point, Vec3<float>& dir);
        //! Executes a function on each model
        DLLEXPORT virtual void forAll(std::function<void(Model3D*)> f) override;
        //! Executes a tick function on each model
        DLLEXPORT virtual bool tick(
            std::function<void(Model3D*)> f, double time) override;
        //! Executes a render function on each model
        DLLEXPORT virtual void render(std::function<void(Model3D*)> f) override;
        //! Returns the number of point light sources
        DLLEXPORT int getPointLightCount() const;
        //! Returns the number of diffuse light sources
        DLLEXPORT int getDiffuseLightCount() const;
        //! Copies all light sources into the buffers
        //! \param zDiffuse The buffer for diffuse light sources
        //! \param zPoints The buffer for point light sources
        DLLEXPORT void copyLight(DXBuffer* zDiffuse, DXBuffer* zPoints) const;
        //! Adds a new diffuse light source
        //! \param light The new light source
        DLLEXPORT void addDiffuseLight(DiffuseLight light);
        //! Adds a new point light source
        //! \param light The new light source
        DLLEXPORT void addPointLight(PointLight light);
        //! Returns a reference to a diffuse light source
        //! \param index The index of the light source
        DLLEXPORT DiffuseLight& getDiffuseLight(int index) const;
        //! Returns a reference to a point light source
        //! \param index The index of the light source
        DLLEXPORT PointLight& getPointLight(int index) const;
        //! removes a specific fiffuse light from the world
        //! \param index the index of the light
        DLLEXPORT void removeDiffuseLight(int index);
        //! removes a specific point light from the world
        //! \param index the index of the light
        DLLEXPORT void removePointLight(int index);
    };
} // namespace Framework