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

#pragma once

// ospray stuff
#include "../geometry/GeometricModel.ih"
#include "DifferentialGeometry.ih"
#include "OSPCommon.ih"
#include "Ray.ih"
// c++ shared
#include "GroupShared.h"
#include "InstanceShared.h"
#include "common/FeatureFlagsEnum.h"

#include "rkcommon/math/AffineSpace.ih"

OSPRAY_BEGIN_ISPC_NAMESPACE

struct Renderer;

inline uniform AffineSpace3f Instance_getTransform(
    const Instance *uniform self, const uniform float time)
{
  uniform AffineSpace3f xfm;
  rtcGetGeometryTransformFromScene(
      self->scene, self->geomID, time, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &xfm);

  return xfm;
}

#ifndef OSPRAY_TARGET_SYCL
inline AffineSpace3f Instance_getTransform(
    const Instance *uniform self, const float time)
{
  AffineSpace3f xfm;
  rtcGetGeometryTransformFromScene(
      self->scene, self->geomID, time, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &xfm);

  return xfm;
}
#endif

inline void Instance_postIntersect(const Instance *uniform self,
    const Renderer *uniform renderer,
    varying DifferentialGeometry &dg,
    const varying Ray &ray,
    uniform int64 flags,
    const uniform bool clip,
    const uniform FeatureFlagsHandler &ffh)
{
  GeometricModel **uniform models =
      clip ? self->group->clipModels : self->group->geometricModels;
  foreach_unique (geomID in ray.geomID)
    GeometricModel_postIntersect(models[geomID], renderer, dg, ray, flags, ffh);

  dg.instID =
      (self->userID == RTC_INVALID_GEOMETRY_ID) ? ray.instID : self->userID;

  uniform AffineSpace3f uxfm = self->xfm;
  uniform AffineSpace3f rcp_uxfm = self->rcp_xfm;

  const uniform FeatureFlagsGeometry ffg = getFeatureFlagsGeometry(ffh);
  if ((flags & DG_MOTIONBLUR) == DG_MOTIONBLUR && self->motionBlur
      && (ffg & FFG_MOTION_BLUR)) {
    uniform float utime = 0.f;
    uniform int cnt = 0;
    foreach_unique (t in ray.time) {
      utime = t;
      cnt++;
    }

    if (cnt > 1) { // varying xfm
      const AffineSpace3f xfm = Instance_getTransform(self, ray.time);
      const AffineSpace3f rcp_xfm = rcp(xfm);

      dg.lP = xfmPoint(rcp_xfm, dg.P);
      dg.Ns = xfmVector(transposed(rcp_xfm.l), dg.Ns);
      dg.Ng = xfmVector(transposed(rcp_xfm.l), dg.Ng);
      // scale dg.epsilon by max (epsilon is scalar and thus assumed to be
      // isotropic anyway and hence cannot better handle non-uniform scaling)
      dg.epsilon *= max(abs(xfm.l.vx.x), max(abs(xfm.l.vy.y), abs(xfm.l.vz.z)));
      if (flags & DG_TANGENTS) {
        dg.dPds = xfmVector(xfm, dg.dPds);
        dg.dPdt = xfmVector(xfm, dg.dPdt);
      }
      return;
    } else { // uniform xfm
      uxfm = Instance_getTransform(self, utime);
      rcp_uxfm = rcp(uxfm);
    }
  }

  dg.lP = xfmPoint(rcp_uxfm, dg.P);
  dg.Ns = xfmVector(transposed(rcp_uxfm.l), dg.Ns);
  dg.Ng = xfmVector(transposed(rcp_uxfm.l), dg.Ng);
  // scale dg.epsilon by max (epsilon is scalar and thus assumed to be
  // isotropic anyway and hence cannot better handle non-uniform scaling)
  dg.epsilon *= max(abs(uxfm.l.vx.x), max(abs(uxfm.l.vy.y), abs(uxfm.l.vz.z)));

  if (flags & DG_TANGENTS) {
    dg.dPds = xfmVector(uxfm, dg.dPds);
    dg.dPdt = xfmVector(uxfm, dg.dPdt);
  }
}

OSPRAY_END_ISPC_NAMESPACE
