/*
 * Renderer 5. The MIT License.
 * Copyright (c) 2022 rlkraft@pnw.edu
 * See LICENSE for details.
*/

package renderer.pipeline;

import renderer.scene.*;
import renderer.scene.primitives.Point;
import renderer.framebuffer.*;
import static renderer.pipeline.PipelineLogger.*;

import java.awt.Color;

/**
   Rasterize a clipped {@link Point} into shaded pixels
   in a {@link FrameBuffer.Viewport}, but  (optionally)
   do not rasterize any part of the {@link Point} that
   is not contained in the {@link Camera}'s view rectangle.
*/
public final class Rasterize_Clip_Point
{
   /**
      Rasterize a {@link Point} into shaded pixels
      in a {@link FrameBuffer.Viewport}.

      @param model  {@link Model} that the {@link Point} {@code pt} comes from
      @param pt     {@code Point} to rasterize into the {@code FrameBuffer.Viewport}
      @param vp     {@link FrameBuffer.Viewport} to hold rasterized, shaded pixels
   */
   public static void rasterize(final Model model,
                                final Point pt,
                                final FrameBuffer.Viewport vp)
   {
      final String     CLIPPED = "Clipped: ";
      final String NOT_CLIPPED = "         ";

      // Make local copies of several values.
      final int w = vp.getWidthVP();
      final int h = vp.getHeightVP();

      final int vIndex = pt.vIndexList.get(0);
      final Vertex v = model.vertexList.get(vIndex);

      final int cIndex = pt.cIndexList.get(0);
      final float[] c = model.colorList.get(cIndex).getRGBComponents(null);
      float r = c[0], g = c[1], b = c[2];

      if (Rasterize.doGamma)
      {
         // Apply gamma-encoding (gamma-compression) to the two colors.
         // https://www.scratchapixel.com/lessons/digital-imaging/digital-images
         // http://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/
         final double gammaInv = 1.0 / Rasterize.GAMMA;
         r = (float)Math.pow(r, gammaInv);
         g = (float)Math.pow(g, gammaInv);
         b = (float)Math.pow(b, gammaInv);
      }

      // Solve the "dangling edge" problem.
      double x = ((v.x - 0.5) / 1.0001) + 0.5;
      double y = ((v.y - 0.5) / 1.0001) + 0.5;
      // This is explained on page 142 of
      //    "Jim Blinn's Corner: A Trip Down The Graphics Pipeline"
      //     by Jim Blinn, 1996, Morgan Kaufmann Publishers.

      // Round the point's coordinates to the nearest logical pixel.
      x = Math.round( x );  // "snap-to-pixel"
      y = Math.round( y );

      if (Rasterize.debug)
      {
         logMessage(String.format("Snapped to (x_pp, y_pp) = (%9.4f, %9.4f)", x,y));
      }

      final int radius = pt.radius;

      // Iterate over the Point's square grid of logical pixels.
      for (int y_pp = (int)y - radius; y_pp <= (int)y + radius; ++y_pp)
      {
         for (int x_pp = (int)x - radius; x_pp <= (int)x + radius; ++x_pp)
         {
            if (Rasterize.debug)
            {
               final String clippedMessage;
               if ( ! Rasterize.doClipping
                 || (x_pp > 0 && x_pp <= w && y_pp > 0 && y_pp <= h) ) // clipping test
               {
                  clippedMessage = NOT_CLIPPED;
               }
               else
               {
                  clippedMessage = CLIPPED;
               }
               logPixel(clippedMessage, x, y, x_pp - 1, h - y_pp, r, g, b, vp);
            }
            // Log the pixel before setting it so that an array out-
            // of-bounds error will be right after the pixel's address.

            if ( ! Rasterize.doClipping
              || (x_pp > 0 && x_pp <= w && y_pp > 0 && y_pp <= h) ) // clipping test
            {
               vp.setPixelVP(x_pp - 1, h - y_pp, new Color(r, g, b));
            }
         }
      }
   }



   // Private default constructor to enforce noninstantiable class.
   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
   private Rasterize_Clip_Point() {
      throw new AssertionError();
   }
}
