/// LSU EE 4702-1 (Fall 2022), GPU Programming
//
//
 /// Homework 1 -- Live Classroom Solution of Problem 3 in 2024.
 //
 //  Assignment: https://www.ece.lsu.edu/koppel/gpup/2022/hw01.pdf

 //
 //  This code is based on cpu-only/demo-01-frame-buffer.cc

#include <gp/coord.h>
#include "frame_buffer.h"

void
line_draw
( pFrame_Buffer& fb, int x0, int y0, int x1, int y1, uint32_t co)
{
  /// Problem 2
  //
  //  Write the frame buffer so that it shows a line from (x0,y0) to
  //  (x1,y1) of color co.
  //
  //  [✔] There should not be gaps between the pixels.
  //  [✔] Try to avoid writing the same pixel multiple times.

  /// SOLUTION -- Problem 2

  const int dx = x1 - x0;
  const int dy = y1 - y0;
  const int width = fb.width_get();

  if ( abs( dx ) < abs( dy ) )
    {
      // Iterate over y

      // Ensure that y0 < y1.
      if ( y0 > y1 ) { swap(x0,x1); swap(y0,y1); }

      float dxdy = float(dx) / dy;
      float x = x0;

      for ( int yi = y0;  yi < y1;  yi++ )
        {
          fb[ yi * width + x ] = co;
          x += dxdy;
        }
    }
  else
    {
      // Iterate over x

      // Ensure that x0 < x1.
      if ( x0 > x1 ) { swap(x0,x1); swap(y0,y1); }

      float dydx = float(dy) / dx;
      float y = y0;

      for ( int xi = x0;  xi < x1;  xi++ )
        {
          fb[ int(y) * width + xi ] = co;
          y += dydx;
        }
    }
}


void
olde_line_draw( pFrame_Buffer& fb, int x0, int y0, int x1, int y1, uint32_t co)
{
  /// Problem 2
  //
  //  Write the frame buffer so that it shows a line from (x0,y0) to
  //  (x1,y1) of color co.
  //
  //  [x] There should not be gaps between the pixels.
  //  [ ] Try to avoid writing the same pixel multiple times.

  // Width of window.
  const int wid = fb.width_get();

  float slope = x0 == x1 ? 10 : float( y1 - y0 ) / ( x1 - x0 );

  // The code below is to help you get started. Please remove it when
  // it is no longer needed.
  //
  fb[ y0 * wid + x0 ] = 0xffffff; // First pixel white.
  float ym = (y1+y0)/2.0;
  fb[ int(ym) * wid + x0 + (x1-x0)/2 ] = 0xff8080;
  fb[ int(ym) * wid + x0 + (x1-x0)/2 + 1 ] = 0xff8080;
  fb[ y1 * wid + x1 ] = 0x00ff00; // Last pixel green.

  for ( int xi = x0; xi < x1; xi++ )
    {
      int y = y0 + (xi - x0) * slope;
      fb[ y * wid + xi ] = 0x00ffff;
    }

}


void
render_hw01(pFrame_Buffer& fb)
{
  // Window Size, in Pixels
  //
  const int win_width = fb.width_get();
  const int win_height = fb.height_get();

  //                           RRGGBB
  const uint32_t color_red = 0xff0000;

  /// Compute square coordinates based on window size.
  //
  const int sq_slen = min(win_width,win_height) * .45;
  const int sq_x0 = sq_slen * 0.1,   sq_y0 = win_height - 150;
  const int sq_x1 = sq_x0 + sq_slen, sq_y1 = sq_y0 - sq_slen;
  //
  // One vertex of the square is at (sq_x0,sq_y0) and the opposite
  // vertex is at (sq_x1,sq_y1).


  /// Problem 1
  //
  // Modify the code below so that the square properly uses the
  // coordinates in variables sq_x0, etc.
  //
  /// SOLUTION -- Problem 1
  //
  // Draw the horizontal lines.
  //
  for ( int x=sq_x0; x<=sq_x1; x++ )
    {
      fb[ sq_y1 * win_width + x ] = color_red;
      fb[ sq_y0 * win_width + x ] = 0xffff00;
    }
  //
  // Draw the vertical lines.
  //
  for ( int y=sq_y0; y>=sq_y1; y-- )
    {
      fb[ y * win_width + sq_x0 ] = 0xff00ff;
      fb[ y * win_width + sq_x1 ] = 0xff00;
    }


  /// Demo Code: Draw Sine Waves  -- No need to modify this.
  //
  int num_waves = 4;
  float plot_ht = sq_slen/float(num_waves);
  for ( float yb = sq_y0 - plot_ht/2,
          freq = 4 * M_PI / float( win_width - 40 - sq_x1 );
        yb > sq_y1; yb -= plot_ht, freq *= 2 )
    for ( int x = sq_x1 + 20;  x < win_width - 20;  x++ )
      {
        float y = yb + sin( x * freq ) * plot_ht * .45;
        fb[ int(y) * win_width + x ] = 0xff00;
      }


  /// Demo Code: Draw Lines Radiating From a Point -- No need to modify.
  //
  //  This code calls the line_draw routine.
  //
  int n_lines = 24;
  float c_r1 = sq_slen/6;  // Inner radius
  float c_r2 = sq_slen/2;  // Outer radius.
  pCoor c_ctr( win_width - c_r2 - 20, c_r2 + 20 ); // Center of lines.
  float delta_theta = 2 * M_PI / n_lines;
  for ( int i=0; i<n_lines; i++ )
    {
      float theta = i * delta_theta;
      pVect dir( cosf(theta), sinf(theta), 0 );
      pCoor p1 = c_ctr + c_r1 * dir;  // Point on inner radius.
      pCoor p2 = c_ctr + c_r2 * dir;  // Point on outer radius.
      line_draw(fb, p1.x, p1.y, p2.x, p2.y, 0xffffff);
    }


  /// Problem 3
  //
  //  Copy the pixels around the mouse pointer into the square,
  //  (sq_x0+1,sq_y0-1) to (sq_x1-1,sq_y1+1), so that the square shows
  //  a zoomed (magnified) version of the area around the mouse
  //  pointer. (Do not overwrite the lines drawn above.) Use variable
  //  zoom as the factor, so that each pixel under the mouse pointer
  //  is copied to 4*4=16 pixels in the square.
  //
  fb.fb_stats_off(); // Turn off "redundancy" statistics.


  /// Problem 3 -- Put code to draw the blue square here (below).
  //
  // Put the code to draw the blue square AFTER the code to copy
  // the frame buffer contents.


  // Coordinates of mouse pointer.
  //
  const int mouse_x = fb.mouse_x;
  const int mouse_y = fb.mouse_y;
  const int zoom = 4;

  const int box_hside = ( sq_x1 - sq_x0 ) / ( 2 * zoom );


  // The code below is to help you get started. Please remove it when
  // you are done.
  //
  // Copy the pixel under the mouse pointer to the middle of the
  // square.
  //
  for ( int x = mouse_x - box_hside;  x < mouse_x + box_hside; x++ )
    for ( int y = mouse_y - box_hside;  y < mouse_y + box_hside; y++ )
      {
        int box_center_x = sq_x0 + sq_slen/2;
        int box_center_y = ( sq_y1 + sq_slen/2 );
        int dx = x - mouse_x;
        int xprime = box_center_x + dx * zoom;
        int yprime = box_center_y + ( y - mouse_y ) * zoom;
        for ( int xx = 0; xx < zoom; xx++ )
          for ( int yy = 0; yy < zoom; yy++ )
            fb[ ( yprime + yy) * win_width + xprime + xx ]
              = fb[ y * win_width + x ];


      }

        fb[ ( sq_y1 + sq_slen/2 ) * win_width + sq_x0 + sq_slen/2 ]
          = fb[ mouse_y * win_width + mouse_x ];

  line_draw(fb, mouse_x - box_hside, mouse_y - box_hside,
            mouse_x + box_hside, mouse_y - box_hside, 0xffffffff );
  line_draw(fb, mouse_x - box_hside, mouse_y + box_hside,
            mouse_x + box_hside, mouse_y + box_hside, 0xffffffff );
  line_draw(fb, mouse_x - box_hside, mouse_y - box_hside,
            mouse_x - box_hside, mouse_y + box_hside, 0xffffffff );
  line_draw(fb, mouse_x + box_hside, mouse_y - box_hside,
            mouse_x + box_hside, mouse_y + box_hside, 0xffffffff );

}


int
main(int argc, char **argv)
{
  pFrame_Buffer demo_frame_buffer(argc,argv);
  demo_frame_buffer.show(render_hw01);
  return 0;
}