/**
  Simple SAF program for exploring the Mandelbrot set, arrows to move, A/B to
  zoom.

  by drummyfish, released under CC0 1.0, public domain
*/

#define SAF_PROGRAM_NAME "Mandelbrot"
#define SAF_SETTING_ENABLE_SOUND 0

#include "../saf.h"

/*
  Checks whether the point is in the mandelbrot set, returns number of
  iterations, up until 255, to which the point did not diverge to infinity, i.e.
  255 means probably inside the set.
*/

uint8_t iteratePoint(double x, double y)
{
  // writing this without floats is left as an excercise for the reader

  double current[2];

  current[0] = x;
  current[1] = y;

  uint8_t iterations = 0;

  while (1)
  {
    double 
      newX = current[0] * current[0] - current[1] * current[1] + x,
      newY = 2 * current[0] * current[1] + y;

    current[0] = newX;
    current[1] = newY;

#define INF 999999999999

    if (iterations == 255 ||
        current[0] > INF || current[0] < -INF ||
        current[1] > INF || current[1] < -INF)
      break;

#undef INF

    iterations++;
  }

  return iterations;
}

double
  cx = -1,
  cy = -1,
  step = 2.0 / SAF_SCREEN_WIDTH; 

void draw(void)
{
  double dx, dy = cy, halfStep = step / 2;

  for (int y = 0; y < SAF_SCREEN_HEIGHT; ++y)
  {
    dx = cx;

    for (int x = 0; x < SAF_SCREEN_WIDTH; ++x)
    {
      // antialias (sample at 4 points):

      uint8_t r = 0,g = 0,b = 0;
      uint16_t rr = 0, gg = 0, bb = 0;

      SAF_colorToRGB(iteratePoint(dx,dy), &r, &g, &b);
      rr = r; gg = g; bb = b;
      SAF_colorToRGB(iteratePoint(dx + halfStep,dy), &r, &g, &b);
      rr += r; gg += g; bb += b;
      SAF_colorToRGB(iteratePoint(dx,dy + halfStep), &r, &g, &b);
      rr += r; gg += g; bb += b;
      SAF_colorToRGB(iteratePoint(dx + halfStep,dy + halfStep), &r, &g, &b);
      rr += r; gg += g; bb += b;

      SAF_drawPixel(x,y,SAF_colorFromRGB(rr / 4, gg / 4, bb / 4));

      dx += step;
    }

    dy += step;
  }
}

void SAF_init(void)
{
  draw();
}

uint8_t SAF_loop(void)
{
  uint8_t redraw = 1;

#define STEP 10

  if (SAF_buttonJustPressed(SAF_BUTTON_LEFT))
    cx -= step * STEP;
  else if (SAF_buttonJustPressed(SAF_BUTTON_RIGHT))
    cx += step * STEP;
  else if (SAF_buttonJustPressed(SAF_BUTTON_UP))
    cy -= step * STEP;
  else if (SAF_buttonJustPressed(SAF_BUTTON_DOWN))
    cy += step * STEP;
  else if (SAF_buttonJustPressed(SAF_BUTTON_A))
  {
    // zoom and shift
    step /= 2;
    cx += step / 2 * SAF_SCREEN_WIDTH;
    cy += step / 2 * SAF_SCREEN_HEIGHT;
  }
  else if (SAF_buttonJustPressed(SAF_BUTTON_B))
  {
    step *= 2;
    cx -= step / 4 * SAF_SCREEN_WIDTH;
    cy -= step / 4 * SAF_SCREEN_HEIGHT;
  }
  else
    redraw = 0;

#undef STEP

  if (redraw) // only redraw when needed, it's expensive
    draw();

  return 1;
}
