/**
 * This is the Starter example with animation added. In the LookupTables example we showed how to
 * rotate an array along the signal path and then write it to the display. When we work with
 * large arrays, shifting numbers for every animation step is not an efficient procedure. Instead,
 * we just shift an index into an array and then copy the pixels to the display using the shifted
 * index. We'll use this technique in all our examples.
 *
 * Press ' ' to toggle animation.
 * Drag the mouse to change pixel-shifting speed and direction.
 * Press 'm' to turn mouse tracking on and off.
 * Press 'h' to print help to the console.
 *
 */

import net.paulhertz.pixelaudio.*;

PixelAudio pixelaudio;        // the library that we need to load in setup()
HilbertGen hGen;              // a PixelMapGen child class for making Hilbert curves
PixelAudioMapper mapper;      // a class that uses a PixelMapGen ("gen", for short) to map audio samples to RGB pixels
PImage mapImage;              // a bitmap image for display
PImage baseImage;             // a reference image, unchanging, used as source for animation
int[] spectrum;               // an array of colors that will be written along the "signal path" in mapImage
int shift = 64;               // amount we shift for each animation step
int totalShift = 0;           // total amount we have shifted the image
boolean isAnimating = false;  // toggle for animation
boolean isTrackMouse = true;  // track mouse position to set shift value

public void settings() {
  size(512, 512);             // Hilbert curves require dimensions that are powers of 2.
}

public void setup() {
  // 1. initialize PixelAudio library
  pixelaudio = new PixelAudio(this);
  // 2. create a PixelMapGen: here it's the Hilbert curve generator
  hGen = new HilbertGen(width, height);
  // 3. initialize a PixelAudioMapper object with the gen
  mapper = new PixelAudioMapper(hGen);
  // 4. create an image for display
  mapImage = createImage(width, height, RGB);
  // At this point, we might load audio, but we'll leave that for later examples.
  // Right now, let's use a color spectrum to look at the pattern the Hilbert curve makes
  // in the display image. We'll do this frequently in example code, since it shows how
  // an audio signal maps to the image.
  // 5. create rainbow color array "spectrum" and plant it in mapImage
  // The PixelAudioMapper variable "mapper" can access the geometry
  // of the signal path generated by hGen. PlantPixels(...) copies 
  // an array of RGB colors to the signal path mapped to an image.
  mapImage.loadPixels();
  spectrum = getColors(mapper.getSize());
  mapper.plantPixels(spectrum, mapImage.pixels, 0, mapper.getSize());
  mapImage.updatePixels();
  // 6. initialize baseImage, the reference image for pixel-shifting animation
  baseImage = mapImage.copy();
  // 7. show the help message
  showHelp();
}


/**
 * Creates an array of colors in rainbow (spectrum) order.
 * @param size    size of the desired array
 * @return an array of colors in rainbow (spectrum) order
 */
public int[] getColors(int size) {
  int[] colorWheel = new int[size];
  pushStyle();
  colorMode(HSB, colorWheel.length, 100, 100);
  int h = 0;
  for (int i = 0; i < colorWheel.length; i++) {
    colorWheel[i] = color(h, 66, 66);
    h++;
  }
  popStyle();
  return colorWheel;
}

public void draw() {
  image(mapImage, 0, 0);
  if (isAnimating) animate();
}

public void animate() {
  totalShift += shift;
  mapImage.loadPixels();
  // PixelAudioMapper has the method we need, copying baseImage to mapImage
  // along the signal path, using the accumulated pixel-shifing value totalShift
  mapper.copyPixelsAlongPathShifted(baseImage.pixels, mapImage.pixels, totalShift);
  mapImage.updatePixels();
}

public void mouseDragged() {
  if (isTrackMouse) {
    shift = abs(width/2 - mouseX) * 16;
    if (mouseY < height/2) shift = -shift;
  }
}

public void keyPressed() {
  switch(key) {
  case ' ': // toggle animation
    isAnimating = !isAnimating;
    println(isAnimating ? "-- animation is running" : "-- animation is paused");
    break;
  case 'm': // toggle isTrackMouse
    isTrackMouse = !isTrackMouse;
    println("-- mouse tracking is "+ isTrackMouse);
    break;
  case 'h': // print help to the console
    showHelp();
    break;
  default:
    break;
  }
}

public void showHelp() {
  println(" * Press ' ' to toggle animation.");
  println(" * Drag the mouse to change pixel-shifting speed and direction.");
  println(" * Press 'm' to turn mouse tracking on and off.");
  println(" * Press 'h' to print help to the console.");
}
