// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "../math/vec.isph"

struct Ray1
{
  uniform Vec3f org;     //!< Ray origin
  uniform float tnear;   //!< Start of ray segment
  uniform Vec3f dir;     //!< Ray direction
  uniform float time;    //!< Time of this ray for motion blur.
  uniform float tfar;    //!< End of ray segment
  uniform int mask;      //!< used to mask out objects during traversal
  uniform int id;        //!< ray ID
  uniform int flags;     //!< ray flags
  uniform Vec3f Ng;      //!< Geometric normal.
  uniform float u;       //!< Barycentric u coordinate of hit
  uniform float v;       //!< Barycentric v coordinate of hit
  uniform int primID;    //!< primitive ID
  uniform int geomID;    //!< geometry ID
  uniform int instID[RTC_MAX_INSTANCE_LEVEL_COUNT];    //!< instance ID
#if defined(RTC_GEOMETRY_INSTANCE_ARRAY)
  uniform int instPrimID[RTC_MAX_INSTANCE_LEVEL_COUNT];    //!< instance ID
#endif
  varying int align[0];  //!< aligns ray on stack to at least 16 bytes
};

inline uniform RTCRayHit* uniform RTCRayHit1_(uniform Ray1& ray)
{
  uniform RTCRayHit* uniform ray_ptr = (uniform RTCRayHit* uniform)&ray;
  return ray_ptr;
}

/*! Ray structure. Contains all information about a ray including
 *  precomputed reciprocal direction. */
struct Ray
{
  Vec3f org;     //!< Ray origin
  float tnear;   //!< Start of ray segment
  Vec3f dir;     //!< Ray direction
  float time;    //!< Time of this ray for motion blur.
  float tfar;    //!< End of ray segment
  int mask;      //!< used to mask out objects during traversal
  int id;        //!< ray ID
  int flags;     //!< ray flags
  Vec3f Ng;      //!< Geometric normal.
  float u;       //!< Barycentric u coordinate of hit
  float v;       //!< Barycentric v coordinate of hit
  int primID;    //!< primitive ID
  int geomID;    //!< geometry ID
  int instID[RTC_MAX_INSTANCE_LEVEL_COUNT];    //!< instance ID
#if defined(RTC_GEOMETRY_INSTANCE_ARRAY)
  int instPrimID[RTC_MAX_INSTANCE_LEVEL_COUNT];    //!< instance ID
#endif
};

inline varying RTCRayHit* uniform RTCRayHit_(varying Ray& ray)
{
  varying RTCRayHit* uniform ray_ptr = (varying RTCRayHit* uniform)&ray;
  return ray_ptr;
}

inline varying RTCRay* uniform RTCRay_(varying Ray& ray)
{
  varying RTCRay* uniform ray_ptr = (varying RTCRay* uniform)&ray;
  return ray_ptr;
}

inline varying RTCHit* uniform RTCHit_(varying Ray& ray)
{
  varying RTCHit* uniform hit_ptr = (varying RTCHit* uniform)&(ray.Ng.x);
  return hit_ptr;
}

/*! Constructs a ray from origin, direction, and ray segment. Near
 *  has to be smaller than far. */
inline Ray make_Ray(const Vec3f org, 
                    const Vec3f dir, 
                    const float tnear = 0.0f, 
                    const float tfar = inf,
                    const float time = 0.0f,
                    const int mask   = -1,
                    const int geomID = -1,
                    const int primID = -1)
{
  Ray ray;
  ray.org   = org;
  ray.tnear = tnear;
  ray.dir   = dir;  
  ray.time  = time;
  ray.tfar  = tfar;
  ray.mask  = mask;
  ray.geomID = geomID;
  ray.primID = primID;
  ray.instID[0] = RTC_INVALID_GEOMETRY_ID;
#if defined(RTC_GEOMETRY_INSTANCE_ARRAY)
  ray.instPrimID[0] = RTC_INVALID_GEOMETRY_ID;
#endif
  return ray;
}

inline void init_Ray(Ray &ray,
                     const Vec3f org, 
                     const Vec3f dir,                      
                     const float tnear = 0.0f, 
                     const float tfar = inf,
                     const float time = 0.0f,
                     const int mask   = -1,
                     const int geomID = -1,
                     const int primID = -1)
{
  ray.org   = org;
  ray.tnear = tnear;
  ray.dir   = dir;
  ray.time  = time;
  ray.tfar  = tfar;
  ray.mask  = mask;
  ray.geomID = geomID;
  ray.primID = primID;
  ray.instID[0] = RTC_INVALID_GEOMETRY_ID;
#if defined(RTC_GEOMETRY_INSTANCE_ARRAY)
  ray.instPrimID[0] = RTC_INVALID_GEOMETRY_ID;
#endif
}

inline bool noHit(const Ray& r) { return r.geomID < 0; }

inline bool hadHit(const Ray& r) { return r.geomID >= 0; }

/*! ray query context passed to intersect/occluded calls */
struct RayQueryContext
{
  RTCRayQueryContext context;
  void* userRayExt;               //!< can be used to pass extended ray data to callbacks
  void* tutorialData;
};

inline void InitIntersectionContext(uniform RayQueryContext* uniform context)
{
  rtcInitRayQueryContext(&context->context);
  context->userRayExt = NULL;
}
