#ifndef __Light__
#define __Light__

#ifndef CORE_DLL 
    #ifdef CORE_BUILD
	    #define CORE_DLL __declspec(dllexport)
    #else
	    #define CORE_DLL __declspec(dllimport)
        #pragma comment(lib, "Core.lib")
    #endif
#endif

#include "Group.h"
#include "Shader.h"
#include "Camera.h"
#include "RenderTarget.h"
#include "DepthStencilBuffer.h"
#include "../../Core/mathlib.h"

class CORE_DLL Light : public Group {
public:
    Light();
    virtual ~Light();

    bool getShadowed();
    void setShadowed(bool shadowed);
    void enableShadows(bool state);

    Camera* getShadowCamera();
    RenderTarget* getShadowMap();
    RenderTarget* getResolvedShadowMap();

    void setSceneCamera(Camera* camera);

    float* getDiffuse();
    float* getSpecular();

    void setDiffuse(float r, float g, float b, float a);
    void setSpecular(float r, float g, float b, float a);

    virtual void apply() = 0;
    virtual void setLightView(const Matrix4& worldMatrix) = 0;

protected:
    Camera* _sceneCamera;
    static Camera* _shadowCamera;
    static RenderTarget* _shadowMap;
    static RenderTarget* _resolvedShadowMap;
    static DepthStencilBuffer* _depthStencilBuffer;
    static Shader* _shadowShader;
    bool _shadowed;
    bool _enableShadows;

    Shader::ScalarVariable* _shadowedVariable;
    Shader::ShaderResourceVariable* _shadowMapVariable;
    Shader::MatrixVariable* _lightViewMatrixVariable;
    Shader::MatrixVariable* _lightProjectionMatrixVariable;

    float _diffuse[4];
    float _specular[4];

    Shader::VectorVariable* _diffuseVariable;
    Shader::VectorVariable* _specularVariable;
};


class CORE_DLL DirectionalLight : public Light {
public:
    DirectionalLight();
    virtual ~DirectionalLight();

    const Vector3& getDirection();
    void setDirection(const Vector3& direction);

    void apply();

    void setLightView(const Matrix4& worldMatrix);

protected:
    Vector3 _direction;
    Shader::VectorVariable* _directionVariable;
};


class CORE_DLL PointLight : public Light {
public:
    PointLight();
    virtual ~PointLight();

    const Vector3& getPosition();
    void setPosition(const Vector3& position);

    float getConstAtten();
    float getLinearAtten();
    float getQuadAtten();

    void setConstAtten(float constAtten);
    void setLinearAtten(float linearAtten);
    void setQuadAtten(float quadAtten);

    void apply();

    void setLightView(const Matrix4& worldMatrix);

protected:
    Vector3 _position;
    float   _constAtten;
    float   _linearAtten;
    float   _quadAtten;

    Shader::VectorVariable* _positionVariable;
    Shader::ScalarVariable* _constAttenVariable;
    Shader::ScalarVariable* _linearAttenVariable;
    Shader::ScalarVariable* _quadAttenVariable;
};


class CORE_DLL SpotLight : public Light {
public:
    SpotLight();
    virtual ~SpotLight();

    const Vector3& getPosition();
    void setPosition(const Vector3& position);

    float getConstAtten();
    float getLinearAtten();
    float getQuadAtten();

    void setConstAtten(float constAtten);
    void setLinearAtten(float linearAtten);
    void setQuadAtten(float quadAtten);

    const Vector3& getDirection();
    void setDirection(const Vector3& direction);

    float getCutoff();
    float getExponent();

    void setCutoff(float cutoff);
    void setExponent(float exponent);

    void apply();

    void setLightView(const Matrix4& worldMatrix);

protected:
    Vector3 _position;
    float   _constAtten;
    float   _linearAtten;
    float   _quadAtten;
    Vector3 _direction;
    float   _cutoff;
    float   _exponent;

    Shader::VectorVariable* _positionVariable;
    Shader::ScalarVariable* _constAttenVariable;
    Shader::ScalarVariable* _linearAttenVariable;
    Shader::ScalarVariable* _quadAttenVariable;
    Shader::VectorVariable* _directionVariable;
    Shader::ScalarVariable* _cutoffVariable;
    Shader::ScalarVariable* _exponentVariable;
};

#endif