/*
 * Copyright 2011-2013 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.
 */

CCL_NAMESPACE_BEGIN

/* Voronoi */

ccl_device void voronoi_neighbors(
    float3 p, NodeVoronoiDistanceMetric distance, float e, float da[4], float3 pa[4])
{
  /* Compute the distance to and the position of the closest neighbors to p.
   *
   * The neighbors are randomly placed, 1 each in a 3x3x3 grid (Worley pattern).
   * The distances and points are returned in ascending order, i.e. da[0] and pa[0] will
   * contain the distance to the closest point and its coordinates respectively.
   */

  da[0] = 1e10f;
  da[1] = 1e10f;
  da[2] = 1e10f;
  da[3] = 1e10f;

  pa[0] = make_float3(0.0f, 0.0f, 0.0f);
  pa[1] = make_float3(0.0f, 0.0f, 0.0f);
  pa[2] = make_float3(0.0f, 0.0f, 0.0f);
  pa[3] = make_float3(0.0f, 0.0f, 0.0f);

  int3 xyzi = quick_floor_to_int3(p);

  for (int xx = -1; xx <= 1; xx++) {
    for (int yy = -1; yy <= 1; yy++) {
      for (int zz = -1; zz <= 1; zz++) {
        int3 ip = xyzi + make_int3(xx, yy, zz);
        float3 fp = make_float3(ip.x, ip.y, ip.z);
        float3 vp = fp + cellnoise3(fp);

        float d;
        switch (distance) {
          case NODE_VORONOI_DISTANCE:
            d = len_squared(p - vp);
            break;
          case NODE_VORONOI_MANHATTAN:
            d = reduce_add(fabs(vp - p));
            break;
          case NODE_VORONOI_CHEBYCHEV:
            d = max3(fabs(vp - p));
            break;
          case NODE_VORONOI_MINKOWSKI: {
            float3 n = fabs(vp - p);
            if (e == 0.5f) {
              d = sqr(reduce_add(sqrt(n)));
            }
            else {
              d = powf(reduce_add(pow3(n, e)), 1.0f / e);
            }
            break;
          }
        }

        /* To keep the shortest four distances and associated points we have to keep them in sorted
         * order. */
        if (d < da[0]) {
          da[3] = da[2];
          da[2] = da[1];
          da[1] = da[0];
          da[0] = d;

          pa[3] = pa[2];
          pa[2] = pa[1];
          pa[1] = pa[0];
          pa[0] = vp;
        }
        else if (d < da[1]) {
          da[3] = da[2];
          da[2] = da[1];
          da[1] = d;

          pa[3] = pa[2];
          pa[2] = pa[1];
          pa[1] = vp;
        }
        else if (d < da[2]) {
          da[3] = da[2];
          da[2] = d;

          pa[3] = pa[2];
          pa[2] = vp;
        }
        else if (d < da[3]) {
          da[3] = d;
          pa[3] = vp;
        }
      }
    }
  }
}

ccl_device void svm_node_tex_voronoi(
    KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
{
  uint4 node2 = read_node(kg, offset);

  uint co_offset, coloring, distance, feature;
  uint scale_offset, e_offset, fac_offset, color_offset;

  decode_node_uchar4(node.y, &co_offset, &coloring, &distance, &feature);
  decode_node_uchar4(node.z, &scale_offset, &e_offset, &fac_offset, &color_offset);

  float3 co = stack_load_float3(stack, co_offset);
  float scale = stack_load_float_default(stack, scale_offset, node2.x);
  float exponent = stack_load_float_default(stack, e_offset, node2.y);

  float dist[4];
  float3 neighbor[4];
  voronoi_neighbors(co * scale, (NodeVoronoiDistanceMetric)distance, exponent, dist, neighbor);

  float3 color;
  float fac;
  if (coloring == NODE_VORONOI_INTENSITY) {
    switch (feature) {
      case NODE_VORONOI_F1:
        fac = dist[0];
        break;
      case NODE_VORONOI_F2:
        fac = dist[1];
        break;
      case NODE_VORONOI_F3:
        fac = dist[2];
        break;
      case NODE_VORONOI_F4:
        fac = dist[3];
        break;
      case NODE_VORONOI_F2F1:
        fac = dist[1] - dist[0];
        break;
    }

    color = make_float3(fac, fac, fac);
  }
  else {
    /* NODE_VORONOI_CELLS */
    switch (feature) {
      case NODE_VORONOI_F1:
        color = neighbor[0];
        break;
      case NODE_VORONOI_F2:
        color = neighbor[1];
        break;
      case NODE_VORONOI_F3:
        color = neighbor[2];
        break;
      case NODE_VORONOI_F4:
        color = neighbor[3];
        break;
      /* Usefulness of this vector is questionable. Note F2 >= F1 but the
       * individual vector components might not be. */
      case NODE_VORONOI_F2F1:
        color = fabs(neighbor[1] - neighbor[0]);
        break;
    }

    color = cellnoise3(color);
    fac = average(color);
  }

  if (stack_valid(fac_offset))
    stack_store_float(stack, fac_offset, fac);
  if (stack_valid(color_offset))
    stack_store_float3(stack, color_offset, color);
}

CCL_NAMESPACE_END
