struct LIGHTING_DIST
{
    float3 color;
    float3 specular;
};

#if POINT_LIGHT_COUNT != 0
    #define PLTYPE vector<float, POINT_LIGHT_COUNT>
 
    LIGHTING_DIST point_lights_calculation(float3 normalWS, float3 position, float3 polygon_normal, float3 reflection_vec)
    {       
        LIGHTING_DIST ld;
        ld.color = 0;
        ld.specular = 0;
        PLTYPE pl_diffuse = 0;
        PLTYPE pl_specular = 0;
        PLTYPE squaredDistances = 0;
        PLTYPE polyNdotL = 0;
        
        PLTYPE lvec[3];
        lvec[0] = point_light_pos[0] - position.x;
        lvec[1] = point_light_pos[1] - position.y;
        lvec[2] = point_light_pos[2] - position.z;

        squaredDistances = lvec[0]*lvec[0] + lvec[1]*lvec[1] + lvec[2]*lvec[2];

        PLTYPE isqd = 1 / sqrt(squaredDistances);

        polyNdotL = lvec[0] * polygon_normal.x + lvec[1] * polygon_normal.y + lvec[2] * polygon_normal.z;
        PLTYPE isLightBehind = step(0, polyNdotL);

        pl_diffuse = lvec[0] * normalWS.x + lvec[1] * normalWS.y + lvec[2] * normalWS.z;
        pl_diffuse *= isqd;

        #if DOUBLE_SIDE_LIGHTING != 0
            pl_diffuse = abs(pl_diffuse);
            isLightBehind = 1;
        #endif
        
        #if SPECULAR == 1
            pl_specular = lvec[0] * reflection_vec.x + lvec[1] * reflection_vec.y + lvec[2] * reflection_vec.z;
            pl_specular = pl_specular * isqd;
            pl_specular = saturate(pl_specular);
        #endif
        
        PLTYPE attenuation = 1 - squaredDistances * point_light_inv_range;
        attenuation = saturate(attenuation);
        attenuation *= isLightBehind;

        pl_diffuse *= attenuation; 
    
        #if SPECULAR == 1
            float A = 3.802;
            float B = -2.802;
            pl_specular = saturate(pl_specular*A + B); 
            pl_specular = pow(pl_specular, 2) * attenuation;
        #endif

        [unroll]for(int i = 0; i < POINT_LIGHT_COUNT; ++i)
        {
            ld.color += pl_diffuse[i] * point_light_color_intensity[i];
            ld.specular += pl_specular[i] * point_light_color_intensity[i];
        };
        return ld;
    };
#endif

#if SPOT_LIGHT_COUNT != 0
    #define SLTYPE vector<float, SPOT_LIGHT_COUNT>
    LIGHTING_DIST spot_lights_calculation(float3 normalWS, float3 position, float3 polygon_normal, float3 reflection_vec)   
    {
        LIGHTING_DIST ld;
        ld.color = 0;
        ld.specular = 0;

        SLTYPE pl_diffuse = 0;
        SLTYPE pl_specular = 0;
        SLTYPE squared_distance = 0;
        SLTYPE polyNDotL = 0;
        SLTYPE cos_alpha = 0;

        SLTYPE lvec[3];
        lvec[0] = spot_light_pos[0] - position.x;
        lvec[1] = spot_light_pos[1] - position.y;
        lvec[2] = spot_light_pos[2] - position.z;

        squared_distance = lvec[0]*lvec[0] + lvec[1]*lvec[1] + lvec[2]*lvec[2];
        SLTYPE isqd = 1/sqrt(squared_distance);

        cos_alpha = -lvec[0]*spot_light_direction[0] - lvec[1]*spot_light_direction[1] - lvec[2]*spot_light_direction[2];
        cos_alpha *= isqd;

        polyNDotL = lvec[0]*polygon_normal.x + lvec[1]*polygon_normal.y + lvec[2]*polygon_normal.z;
        SLTYPE isLightBehind = step(0, polyNDotL);

        pl_diffuse = lvec[0]*normalWS.x + lvec[1]*normalWS.y + lvec[2]*normalWS.z;
        pl_diffuse *= isqd;

        #if DOUBLE_SIDE_LIGHTING != 0
            pl_diffuse = abs(pl_diffuse);
            isLightBehind = 1;
        #endif

        #if SPECULAR == 1
            pl_specular = lvec[0]*reflection_vec.x + lvec[1]*reflection_vec.y + lvec[2]*reflection_vec.z;
            pl_specular = saturate(pl_specular);
        #endif

        SLTYPE attenuation = 1 - squared_distance*spot_light_inv_range;
        attenuation = saturate(attenuation);
        attenuation *= isLightBehind;
        SLTYPE spot_light_cone = cos_alpha <= spot_light_angles[1] ? (cos_alpha - spot_light_angles[0])*spot_light_angles[2] : 1;
        attenuation *= spot_light_cone;

        #if SPECULAR == 1
            float A = 3.802;
            float B = -2.802;
            pl_specular = saturate(pl_specular * A + B); 
            pl_specular = pow(pl_specular, 2) * attenuation;
        #endif

        pl_diffuse = pl_diffuse*attenuation;

        [unroll]for(int i = 0; i < SPOT_LIGHT_COUNT; ++i)
        {
            ld.color += pl_diffuse[i] * spot_light_color_intensity[i];
            ld.specular += pl_specular[i] * spot_light_color_intensity[i];
        };
        return ld;
       };
#endif

#if DIRECTIONAL_LIGHT_COUNT != 0
    LIGHTING_DIST directional_light_calculation(float3 directional_light_dir, float3 normalWS, float3 polygon_normal, float3 reflection_vec, float3 directional_light_color_intensity_i)
    {
        LIGHTING_DIST ld;
        ld.color = 0;
        ld.specular = 0;

        float3 directional_light_dir_inverse = -directional_light_dir;
        float is_light_behind = step(0, dot(polygon_normal, directional_light_dir_inverse));
        ld.color = dot(directional_light_dir_inverse, normalWS)*is_light_behind*directional_light_color_intensity_i;
        #if SPECULAR == 1
            float A = 3.802;
            float B = -2.802; 
            ld.specular = pow(saturate(dot(directional_light_dir_inverse, reflection_vec)*A + B), 2)*is_light_behind*directional_light_color_intensity_i;
        #endif
        return ld;
    };
#endif  