// Copyright (c) 2012-2017 VideoStitch SAS
// Copyright (c) 2018 stitchEm

__device__ float2
FUNCTION_NAME_6(mapFunctionCubemap, XPos, fromSphereToInput, distortionMetersTransform, distortionPixelsTransform, equiangular) (
  float2 uv,
  const int faceDim,
  const vsfloat3x4 pose,
  const float2 inputScale,
  const vsDistortion distortion,
  const float2 centerShift) {

  /* Coordinates are in pixels, transform to the unit cube */
  uv = (uv / faceDim) * 2.f;
  if (equiangular) {
    uv.x = tanf_vs(uv.x * PI_F_VS / 4.f);
    uv.y = tanf_vs(uv.y * PI_F_VS / 4.f);
  }

  /** From +x face to unit-sphere */
  float3 pt = make_float3(1.f, -uv.y, uv.x);
  pt *= invLength3(pt);

  /** transform the point to be in the camera space*/
  pt = transformSphere(pt, pose);

  /** From Spherical space to input space */
  uv = fromSphereToInput(pt);

  /** Add distortion to point coordinates before scaling*/
  uv = distortionMetersTransform(uv, distortion);

  /** From meters to pixels*/
  uv.x *= inputScale.x;
  uv.y *= inputScale.y;

  /** Add distortion to point coordinates after scaling*/
  uv = distortionPixelsTransform(uv, distortion);

  /** Shift of principal point (Wrt image center)*/
  uv += centerShift;
  return uv;
}

__device__ float2
FUNCTION_NAME_6(mapFunctionCubemap, XNeg, fromSphereToInput, distortionMetersTransform, distortionPixelsTransform, equiangular)(
  float2 uv,
  const int faceDim,
  const vsfloat3x4 pose,
  const float2 inputScale,
  const vsDistortion distortion,
  const float2 centerShift) {

  /* Coordinates are in pixels, transform to the unit cube */
  uv = (uv / faceDim) * 2.f;
  if (equiangular) {
    uv.x = tanf_vs(uv.x * PI_F_VS / 4.f);
    uv.y = tanf_vs(uv.y * PI_F_VS / 4.f);
  }

  /** From -x face to unit-sphere */
  float3 pt = make_float3(-1.f, -uv.y, -uv.x);
  pt *= invLength3(pt);

  /** transform the point to be in the camera space*/
  pt = transformSphere(pt, pose);

  /** From Spherical space to input space */
  uv = fromSphereToInput(pt);

  /** Add distortion to point coordinates before scaling*/
  uv = distortionMetersTransform(uv, distortion);

  /** From meters to pixels*/
  uv.x *= inputScale.x;
  uv.y *= inputScale.y;

  /** Add distortion to point coordinates after scaling*/
  uv = distortionPixelsTransform(uv, distortion);

  /** Shift of principal point (Wrt image center)*/
  uv += centerShift;
  return uv;
}

__device__ float2
FUNCTION_NAME_6(mapFunctionCubemap, YPos, fromSphereToInput, distortionMetersTransform, distortionPixelsTransform, equiangular)(
  float2 uv,
  const int faceDim,
  const vsfloat3x4 pose,
  const float2 inputScale,
  const vsDistortion distortion,
  const float2 centerShift) {

  /* Coordinates are in pixels, transform to the unit cube */
  uv = (uv / faceDim) * 2.f;
  if (equiangular) {
    uv.x = tanf_vs(uv.x * PI_F_VS / 4.f);
    uv.y = tanf_vs(uv.y * PI_F_VS / 4.f);
  }

  /** From +y face to unit-sphere */
  float3 pt = make_float3(uv.x, 1.f, -uv.y);
  pt *= invLength3(pt);

  /** transform the point to be in the camera space*/
  pt = transformSphere(pt, pose);

  /** From Spherical space to input space */
  uv = fromSphereToInput(pt);

  /** Add distortion to point coordinates before scaling*/
  uv = distortionMetersTransform(uv, distortion);

  /** From meters to pixels*/
  uv.x *= inputScale.x;
  uv.y *= inputScale.y;

  /** Add distortion to point coordinates after scaling*/
  uv = distortionPixelsTransform(uv, distortion);

  /** Shift of principal point (Wrt image center)*/
  uv += centerShift;
  return uv;
}

__device__ float2
FUNCTION_NAME_6(mapFunctionCubemap, YNeg, fromSphereToInput, distortionMetersTransform, distortionPixelsTransform, equiangular)(
  float2 uv,
  const int faceDim,
  const vsfloat3x4 pose,
  const float2 inputScale,
  const vsDistortion distortion,
  const float2 centerShift) {

  /* Coordinates are in pixels, transform to the unit cube */
  uv = (uv / faceDim) * 2.f;
  if (equiangular) {
    uv.x = tanf_vs(uv.x * PI_F_VS / 4.f);
    uv.y = tanf_vs(uv.y * PI_F_VS / 4.f);
  }

  /** From -y face to unit-sphere */
  float3 pt = make_float3(uv.x, -1.f, uv.y);
  pt *= invLength3(pt);

  /** transform the point to be in the camera space*/
  pt = transformSphere(pt, pose);

  /** From Spherical space to input space */
  uv = fromSphereToInput(pt);

  /** Add distortion to point coordinates before scaling*/
  uv = distortionMetersTransform(uv, distortion);

  /** From meters to pixels*/
  uv.x *= inputScale.x;
  uv.y *= inputScale.y;

  /** Add distortion to point coordinates after scaling*/
  uv = distortionPixelsTransform(uv, distortion);

  /** Shift of principal point (Wrt image center)*/
  uv += centerShift;
  return uv;
}

__device__ float2
FUNCTION_NAME_6(mapFunctionCubemap, ZPos, fromSphereToInput, distortionMetersTransform, distortionPixelsTransform, equiangular)(
  float2 uv,
  const int faceDim,
  const vsfloat3x4 pose,
  const float2 inputScale,
  const vsDistortion distortion,
  const float2 centerShift) {

  /* Coordinates are in pixels, transform to the unit cube */
  uv = (uv / faceDim) * 2.f;
  if (equiangular) {
    uv.x = tanf_vs(uv.x * PI_F_VS / 4.f);
    uv.y = tanf_vs(uv.y * PI_F_VS / 4.f);
  }

  /** From +z face to unit-sphere */
  float3 pt = make_float3(uv.x, -uv.y, -1.f);
  pt *= invLength3(pt);

  /** transform the point to be in the camera space*/
  pt = transformSphere(pt, pose);

  /** From Spherical space to input space */
  uv = fromSphereToInput(pt);

  /** Add distortion to point coordinates before scaling*/
  uv = distortionMetersTransform(uv, distortion);

  /** From meters to pixels*/
  uv.x *= inputScale.x;
  uv.y *= inputScale.y;

  /** Add distortion to point coordinates after scaling*/
  uv = distortionPixelsTransform(uv, distortion);

  /** Shift of principal point (Wrt image center)*/
  uv += centerShift;
  return uv;
}

__device__ float2
FUNCTION_NAME_6(mapFunctionCubemap, ZNeg, fromSphereToInput, distortionMetersTransform, distortionPixelsTransform, equiangular)(
  float2 uv,
  const int faceDim,
  const vsfloat3x4 pose,
  const float2 inputScale,
  const vsDistortion distortion,
  const float2 centerShift) {

  /* Coordinates are in pixels, transform to the unit cube */
  uv = (uv / faceDim) * 2.f;
  if (equiangular) {
    uv.x = tanf_vs(uv.x * PI_F_VS / 4.f);
    uv.y = tanf_vs(uv.y * PI_F_VS / 4.f);
  }

  /** From +x face to unit-sphere */
  float3 pt = make_float3(-uv.x, -uv.y, 1.f);
  pt *= invLength3(pt);

  /** transform the point to be in the camera space*/
  pt = transformSphere(pt, pose);

  /** From Spherical space to input space */
  uv = fromSphereToInput(pt);

  /** Add distortion to point coordinates before scaling*/
  uv = distortionMetersTransform(uv, distortion);

  /** From meters to pixels*/
  uv.x *= inputScale.x;
  uv.y *= inputScale.y;

  /** Add distortion to point coordinates after scaling*/
  uv = distortionPixelsTransform(uv, distortion);

  /** Shift of principal point (Wrt image center)*/
  uv += centerShift;
  return uv;
}