/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  lebiniou is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include "context.h"


uint32_t version = 0;
uint32_t options = BO_DISPLACE | BO_LENS | BO_SCHEMES;
char dname[] = "Hodge";
enum LayerMode mode = LM_XOR;
char desc[] = "Cellular automaton";

/*
 * Based on:
 * http://www.fourmilab.ch/cellab/manual/rules.html#Hodge
 */

#define B    5
#define N    ((1 << B) - 1)
#define SHFT (8*sizeof(Pixel_t) - B)
#define G    5


static Buffer8_t *game[2] = { NULL, NULL };
static uint8_t initialized = 0;


int8_t
create(Context_t *ctx)
{
  game[0] = Buffer8_new();
  game[1] = Buffer8_new();

  return 1;
}


void
destroy(Context_t *ctx)
{
  Buffer8_delete(game[0]);
  Buffer8_delete(game[1]);
}


void
on_switch_on(Context_t *ctx)
{
  Pixel_t *p;

  if (initialized) {
    return;
  }

  if (b_rand_boolean()) {
    Buffer8_copy(active_buffer(ctx), game[0]);
  } else {
    Buffer8_randomize(game[0]);
  }

  for (p = game[0]->buffer; p < (game[0]->buffer + BUFFSIZE*sizeof(Pixel_t)); p++) {
    *p &= N;
  }

  initialized = 1;
}


static inline Pixel_t
five_bits(const Buffer8_t *buff, const int x, const int y)
{
  const Pixel_t p = get_pixel_nc(buff, x, y);

  return (p & N);
}


static inline uint16_t
sum(const Buffer8_t *buff, const int x, const int y)
{
  uint16_t s = 0;
  int dx, dy;

  for (dy = -1; dy <= 1; dy++)
    for (dx = -1; dx <= 1; dx++) {
      if (!dx && !dy) {
        continue;
      } else {
        s += get_pixel_nc(buff, x+dx, y+dy);
      }
    }

  return s;
}


void
run(Context_t *ctx)
{
  /* Quick version, no pointers. Left as an exercise
   * to the reader :)
   */
  int x, y;
  Buffer8_t *tmp, *dst;

  dst = passive_buffer(ctx);

#define game_src game[0]
#define game_dst game[1]

  for (y = 1; y < MAXY; y++)
    for (x = 1; x < MAXX; x++) {
      const Pixel_t old = five_bits(game_src, x, y);
      Pixel_t new;

      if (old == N) {
        new = 0;
      } else {
        const uint16_t s = sum(game_src, x, y);
        assert(old < N);
        new = ((s >> SHFT) + G) & 255;
        if (new > N) {
          new = N;
        }
      }

      set_pixel_nc(game_dst, x, y, new);
      set_pixel_nc(dst, x, y, new);
    }

  tmp = game[0];
  game[0] = game[1];
  game[1] = tmp;
}
