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

#pragma once

#define _CRT_SECURE_NO_WARNINGS

/* size of screen tiles */
#define TILE_SIZE_X 8
#define TILE_SIZE_Y 8

/* vertex and triangle layout */
struct Vertex   { float x,y,z,r;  }; // FIXME: rename to Vertex4f
struct Triangle { int v0, v1, v2; };

#include "../device_default.isph"

/* include optional vector library */
#include "../math/math.isph"
#include "../math/vec.isph"
#include "../math/affinespace.isph"
#include "../core/ray.isph"
#include "camera.isph"
#include "scene_device.h"
#include "noise.isph"
#if !defined(ISPC)
#include "../../../common/algorithms/parallel_for.h"
#endif

#if defined(EMBREE_SYCL_TUTORIAL)
inline sycl::nd_range<2> make_nd_range(unsigned int size0, unsigned int size1)
{
  const sycl::range<2> wg_size = sycl::range<2>(4,4);

  /* align iteration space to work group size */
  size0 = ((size0 + wg_size[0] - 1) / wg_size[0]) * wg_size[0];
  size1 = ((size1 + wg_size[1] - 1) / wg_size[1]) * wg_size[1];

  return sycl::nd_range(sycl::range(size0,size1),wg_size);
}
#endif

enum Shader { 
  SHADER_DEFAULT, 
  SHADER_EYELIGHT,
  SHADER_OCCLUSION,
  SHADER_UV,
  SHADER_TEXCOORDS,
  SHADER_TEXCOORDS_GRID,
  SHADER_NG,
  SHADER_CYCLES,
  SHADER_GEOMID,
  SHADER_GEOMID_PRIMID,
  SHADER_AO
};

extern RTCDevice g_device;
extern uniform RTCRayQueryFlags g_iflags_coherent;
extern uniform RTCRayQueryFlags g_iflags_incoherent;
extern uniform Shader shader;

/* error reporting function */
unmasked void error_handler(void* uniform userPtr, uniform RTCError code, const uniform int8* uniform str = NULL);

/* returns time stamp counter */
#if defined(EMBREE_SYCL_SUPPORT) && defined(__SYCL_DEVICE_ONLY__)
inline int64 get_tsc() { return 0; }
#else
extern "C" uniform int64 get_tsc();
#endif

#if defined(__WIN32__) && defined(__INTEL_LLVM_COMPILER)
/* declare some standard library functions */
extern "C" __declspec(dllimport) void abort ();
extern "C" __declspec(dllimport) void exit(int);
extern "C" __declspec(dllimport) int puts ( const char* str );
extern "C" __declspec(dllimport) int putchar ( int character );
#else
/* declare some standard library functions */
extern "C" void abort ();
extern "C" void exit(uniform int);
extern "C" uniform int puts ( const uniform int8* uniform str );
extern "C" uniform int putchar ( uniform int character );
#endif

/* face forward for shading normals */
inline Vec3f faceforward( const Vec3f& N, const Vec3f& I, const Vec3f& Ng ) {
  Vec3f NN = N; return dot(I, Ng) < 0 ? NN : neg(NN);
}

/* GLFW keys codes */
#if !defined(GLFW_KEY_F1)
#define GLFW_KEY_F1                 290
#define GLFW_KEY_F2                 291
#define GLFW_KEY_F3                 292
#define GLFW_KEY_F4                 293
#define GLFW_KEY_F5                 294
#define GLFW_KEY_F6                 295
#define GLFW_KEY_F7                 296
#define GLFW_KEY_F8                 297
#define GLFW_KEY_F9                 298
#define GLFW_KEY_F10                299
#define GLFW_KEY_F11                300
#define GLFW_KEY_F12                301
#endif

extern void device_key_pressed_default(uniform int key);
extern void (* uniform key_pressed_handler)(uniform int key);

export void renderFrameStandard(uniform int* uniform pixels,
                         const uniform unsigned int width,
                         const uniform unsigned int height,
                         const uniform float time,
                         const uniform ISPCCamera& camera);

uniform unsigned int getNumHWThreads();

#if defined(ISPC)
#define ALIGNED_STRUCT_(x)
#define __aligned(x)
#define MAYBE_UNUSED
#endif

/* draws progress bar */
extern "C" unmasked void progressStart();
extern "C" unmasked uniform bool progressMonitor(void* uniform ptr, const uniform double n);
extern "C" unmasked void progressEnd();

SYCL_EXTERNAL Vec2f  getTextureCoordinatesSubdivMesh(void* uniform mesh, const unsigned int primID, const float u, const float v);
SYCL_EXTERNAL float  getTextureTexel1f(const uniform Texture* uniform texture, float u, float v);
SYCL_EXTERNAL Vec3f  getTextureTexel3f(const uniform Texture* uniform texture, float u, float v);

enum ISPCInstancingMode { ISPC_INSTANCING_NONE, ISPC_INSTANCING_GEOMETRY, ISPC_INSTANCING_GROUP };

/* ray statistics */
#if !defined(TASKING_PPL) // not supported with PPL because threadIndex is not unique and atomics are too expensive
#define RAY_STATS
#endif

struct RayStats
{
  int numRays;
  int pad[32-1];
};

#if defined(RAY_STATS)
#if defined(ISPC)
inline void RayStats_addRay(uniform RayStats& stats)       { stats.numRays += popcnt(lanemask()); }
inline void RayStats_addShadowRay(uniform RayStats& stats) { stats.numRays += popcnt(lanemask()); }
#else // C++
__forceinline void RayStats_addRay(RayStats& stats)        { stats.numRays++; }
__forceinline void RayStats_addShadowRay(RayStats& stats)  { stats.numRays++; }
#endif
#else // disabled
inline void RayStats_addRay(uniform RayStats& stats)       {}
inline void RayStats_addShadowRay(uniform RayStats& stats) {}
#endif

extern uniform RayStats* uniform g_stats;

inline bool nativePacketSupported(RTCDevice device)
{
  if (sizeof(float) == 1*4) return true;
  else if (sizeof(float) == 4*4) return rtcGetDeviceProperty(device,RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED);
  else if (sizeof(float) == 8*4) return rtcGetDeviceProperty(device,RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED);
  else if (sizeof(float) == 16*4) return rtcGetDeviceProperty(device,RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED);
  else return false;
}
