/*
 * Copyright 2011-2015 Blender Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "kernel_split_common.h"

/* Note on kernel_holdout_emission_blurring_pathtermination_ao kernel.
 * This is the sixth kernel in the ray tracing logic. This is the fifth
 * of the path iteration kernels. This kernel takes care of the logic to process
 * "material of type holdout", indirect primitive emission, bsdf blurring,
 * probabilistic path termination and AO.
 *
 * This kernels determines the rays for which a shadow_blocked() function associated with AO should be executed.
 * Those rays for which a shadow_blocked() function for AO must be executed are marked with flag RAY_SHADOW_RAY_CAST_ao and
 * enqueued into the queue QUEUE_SHADOW_RAY_CAST_AO_RAYS
 *
 * Ray state of rays that are terminated in this kernel are changed to RAY_UPDATE_BUFFER
 *
 * The input and output are as follows,
 *
 * rng_coop ---------------------------------------------|--- kernel_holdout_emission_blurring_pathtermination_ao ---|--- Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
 * throughput_coop --------------------------------------|                                                           |--- PathState_coop
 * PathRadiance_coop ------------------------------------|                                                           |--- throughput_coop
 * Intersection_coop ------------------------------------|                                                           |--- L_transparent_coop
 * PathState_coop ---------------------------------------|                                                           |--- per_sample_output_buffers
 * L_transparent_coop -----------------------------------|                                                           |--- PathRadiance_coop
 * shader_data ------------------------------------------|                                                           |--- ShaderData
 * ray_state --------------------------------------------|                                                           |--- ray_state
 * Queue_data (QUEUE_ACTIVE_AND_REGENERATED_RAYS) -------|                                                           |--- Queue_data (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS)
 * Queue_index (QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS) ---|                                                           |--- AOAlpha_coop
 * kg (globals + data) ----------------------------------|                                                           |--- AOBSDF_coop
 * parallel_samples -------------------------------------|                                                           |--- AOLightRay_coop
 * per_sample_output_buffers ----------------------------|                                                           |
 * sw ---------------------------------------------------|                                                           |
 * sh ---------------------------------------------------|                                                           |
 * sx ---------------------------------------------------|                                                           |
 * sy ---------------------------------------------------|                                                           |
 * stride -----------------------------------------------|                                                           |
 * work_array -------------------------------------------|                                                           |
 * queuesize --------------------------------------------|                                                           |
 * start_sample -----------------------------------------|                                                           |
 *
 * Note on Queues :
 * This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS and processes only
 * the rays of state RAY_ACTIVE.
 * There are different points in this kernel where a ray may terminate and reach RAY_UPDATE_BUFFER
 * state. These rays are enqueued into QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will
 * still be present in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has been
 * changed to RAY_UPDATE_BUFFER, there is no problem.
 *
 * State of queues when this kernel is called :
 * At entry,
 * QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and RAY_REGENERATED rays
 * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE rays.
 * QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty.
 * At exit,
 * QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, RAY_REGENERATED and RAY_UPDATE_BUFFER rays
 * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays
 * QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with flag RAY_SHADOW_RAY_CAST_AO
 */
ccl_device void kernel_holdout_emission_blurring_pathtermination_ao(
        ccl_global char *globals,
        ccl_constant KernelData *data,
        ccl_global char *shader_data,          /* Required throughout the kernel except probabilistic path termination and AO */
        ccl_global float *per_sample_output_buffers,
        ccl_global uint *rng_coop,             /* Required for "kernel_write_data_passes" and AO */
        ccl_global float3 *throughput_coop,    /* Required for handling holdout material and AO */
        ccl_global float *L_transparent_coop,  /* Required for handling holdout material */
        PathRadiance *PathRadiance_coop,       /* Required for "kernel_write_data_passes" and indirect primitive emission */
        ccl_global PathState *PathState_coop,  /* Required throughout the kernel and AO */
        Intersection *Intersection_coop,       /* Required for indirect primitive emission */
        ccl_global float3 *AOAlpha_coop,       /* Required for AO */
        ccl_global float3 *AOBSDF_coop,        /* Required for AO */
        ccl_global Ray *AOLightRay_coop,       /* Required for AO */
        int sw, int sh, int sx, int sy, int stride,
        ccl_global char *ray_state,            /* Denotes the state of each ray */
        ccl_global unsigned int *work_array,   /* Denotes the work that each ray belongs to */
#ifdef __WORK_STEALING__
        unsigned int start_sample,
#endif
        int parallel_samples,                  /* Number of samples to be processed in parallel */
        int ray_index,
        char *enqueue_flag,
        char *enqueue_flag_AO_SHADOW_RAY_CAST)
{
	/* Load kernel globals structure and ShaderData structure */
	KernelGlobals *kg = (KernelGlobals *)globals;
	ShaderData *sd = (ShaderData *)shader_data;

#ifdef __WORK_STEALING__
	unsigned int my_work;
	unsigned int pixel_x;
	unsigned int pixel_y;
#endif
	unsigned int tile_x;
	unsigned int tile_y;
	int my_sample_tile;
	unsigned int sample;

	ccl_global RNG *rng = 0x0;
	ccl_global PathState *state = 0x0;
	float3 throughput;

	if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {

		throughput = throughput_coop[ray_index];
		state = &PathState_coop[ray_index];
		rng = &rng_coop[ray_index];
#ifdef __WORK_STEALING__
		my_work = work_array[ray_index];
		sample = get_my_sample(my_work, sw, sh, parallel_samples, ray_index) + start_sample;
		get_pixel_tile_position(&pixel_x, &pixel_y,
		                        &tile_x, &tile_y,
		                        my_work,
		                        sw, sh, sx, sy,
		                        parallel_samples,
		                        ray_index);
		my_sample_tile = 0;
#else  /* __WORK_STEALING__ */
		sample = work_array[ray_index];
		/* Buffer's stride is "stride"; Find x and y using ray_index. */
		int tile_index = ray_index / parallel_samples;
		tile_x = tile_index % sw;
		tile_y = tile_index / sw;
		my_sample_tile = ray_index - (tile_index * parallel_samples);
#endif  /* __WORK_STEALING__ */
		per_sample_output_buffers +=
		    (((tile_x + (tile_y * stride)) * parallel_samples) + my_sample_tile) *
		    kernel_data.film.pass_stride;

		/* holdout */
#ifdef __HOLDOUT__
		if((ccl_fetch(sd, flag) & (SD_HOLDOUT|SD_HOLDOUT_MASK)) &&
		   (state->flag & PATH_RAY_CAMERA))
		{
			if(kernel_data.background.transparent) {
				float3 holdout_weight;

				if(ccl_fetch(sd, flag) & SD_HOLDOUT_MASK)
					holdout_weight = make_float3(1.0f, 1.0f, 1.0f);
				else
					holdout_weight = shader_holdout_eval(kg, sd);

				/* any throughput is ok, should all be identical here */
				L_transparent_coop[ray_index] += average(holdout_weight*throughput);
			}

			if(ccl_fetch(sd, flag) & SD_HOLDOUT_MASK) {
				ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER);
				*enqueue_flag = 1;
			}
		}
#endif  /* __HOLDOUT__ */
	}

	if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
		PathRadiance *L = &PathRadiance_coop[ray_index];
		/* Holdout mask objects do not write data passes. */
		kernel_write_data_passes(kg,
		                         per_sample_output_buffers,
		                         L,
		                         sd,
		                         sample,
		                         state,
		                         throughput);
		/* Blurring of bsdf after bounces, for rays that have a small likelihood
		 * of following this particular path (diffuse, rough glossy.
		 */
		if(kernel_data.integrator.filter_glossy != FLT_MAX) {
			float blur_pdf = kernel_data.integrator.filter_glossy*state->min_ray_pdf;
			if(blur_pdf < 1.0f) {
				float blur_roughness = sqrtf(1.0f - blur_pdf)*0.5f;
				shader_bsdf_blur(kg, sd, blur_roughness);
			}
		}

#ifdef __EMISSION__
		/* emission */
		if(ccl_fetch(sd, flag) & SD_EMISSION) {
			/* TODO(sergey): is isect.t wrong here for transparent surfaces? */
			float3 emission = indirect_primitive_emission(
			        kg,
			        sd,
			        Intersection_coop[ray_index].t,
			        state->flag,
			        state->ray_pdf);
			path_radiance_accum_emission(L, throughput, emission, state->bounce);
		}
#endif  /* __EMISSION__ */

		/* Path termination. this is a strange place to put the termination, it's
		 * mainly due to the mixed in MIS that we use. gives too many unneeded
		 * shader evaluations, only need emission if we are going to terminate.
		 */
		float probability = path_state_terminate_probability(kg, state, throughput);

		if(probability == 0.0f) {
			ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER);
			*enqueue_flag = 1;
		}

		if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
			if(probability != 1.0f) {
				float terminate = path_state_rng_1D_for_decision(kg, rng, state, PRNG_TERMINATE);
				if(terminate >= probability) {
					ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER);
					*enqueue_flag = 1;
				} else {
					throughput_coop[ray_index] = throughput/probability;
				}
			}
		}
	}

#ifdef __AO__
	if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
		/* ambient occlusion */
		if(kernel_data.integrator.use_ambient_occlusion ||
		   (ccl_fetch(sd, flag) & SD_AO))
		{
			/* todo: solve correlation */
			float bsdf_u, bsdf_v;
			path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);

			float ao_factor = kernel_data.background.ao_factor;
			float3 ao_N;
			AOBSDF_coop[ray_index] = shader_bsdf_ao(kg, sd, ao_factor, &ao_N);
			AOAlpha_coop[ray_index] = shader_bsdf_alpha(kg, sd);

			float3 ao_D;
			float ao_pdf;
			sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);

			if(dot(ccl_fetch(sd, Ng), ao_D) > 0.0f && ao_pdf != 0.0f) {
				Ray _ray;
				_ray.P = ray_offset(ccl_fetch(sd, P), ccl_fetch(sd, Ng));
				_ray.D = ao_D;
				_ray.t = kernel_data.background.ao_distance;
#ifdef __OBJECT_MOTION__
				_ray.time = ccl_fetch(sd, time);
#endif
				_ray.dP = ccl_fetch(sd, dP);
				_ray.dD = differential3_zero();
				AOLightRay_coop[ray_index] = _ray;

				ADD_RAY_FLAG(ray_state, ray_index, RAY_SHADOW_RAY_CAST_AO);
				*enqueue_flag_AO_SHADOW_RAY_CAST = 1;
			}
		}
	}
#endif  /* __AO__ */
}
