// ---------------------------------------------------------
// Class: SensoryField
// ---------------------------------------------------------
// Contains all the sensory sources in a field of given
// width and height. Uses a 2D array to add up the sensory
// values of all the light sources. This can be displayed
// on screen as a PImage.
// ---------------------------------------------------------

import processing.*;
import processing.core.*;
import java.util.*;

class SensoryField {

  int w, h;
  int blocksize; // the resolution of our sensory field
  float[][] field; // 2D array of sensory values from 0-255
  ArrayList sources; // light sources in this sensory field
  boolean visible = false; // we want to draw the ground
  boolean drawranges = false; // draw the range of each source
  PImage ground; // the image corresponding to the field
int colshiftt=0;
  // Constructor
  // -----------------------------
  SensoryField(int w, int h, int bsize, int colshift) {
    this.w = w;
    this.h = h;
    blocksize = bsize;
    field = new float[w][h];
    sources = new ArrayList();
    colshiftt=colshift;
  }

  // Get the sense value at pixel (x,y)
  // -----------------------------
  float getValue(int x, int y) {
    if (x > -1 && x < w && y > -1 && y < h) {
      return field[x][y]; 
    } 
    else {
      return 0; 
    }
  }

  // Add another source to this sensory field
  // -----------------------------
  void addSource(Source s) {
    sources.add(s);
    update();
  }

  // Remove the source at index i
  // -----------------------------
  void removeSource(int i) {
   // pa.println(sources.size());
   if(sources.size()>i){
    sources.remove(i);
    update();}
  //  else{pa.println("would have failed");}
  }

  // Get source at index i
  // -----------------------------
  Source getSource(int i) {
    return (Source)sources.get(i);
  }

  // How many sources are there in this sensory field?
  // -----------------------------
  int numSources() {
    return sources.size(); 
  }

  // Do we want to draw the ground
  // -----------------------------
  boolean getVisible() {
    return visible; 
  }

  // Set the flag to draw the ground
  // -----------------------------
  void setVisible(boolean visible) {
    this.visible = visible;
  }

  // Do we want to draw the source ranges of influence?
  // -----------------------------
  boolean getDrawRanges() {
    return drawranges;   
  }

  // Set the flag to draw the source ranges of influence
  // -----------------------------
  void setDrawRanges(boolean b) {
    drawranges = b;
    for (int i=0; i<sources.size(); i++) {
      Source s = (Source)sources.get(i);
      s.setDrawRange(b); 
    }
  }

  // Draw the sensory field
  // -----------------------------
  void drawMe(PApplet pa) {
    // if the ground is visible then draw the ground image
    if (visible && ground != null) {
      
      pa.image(ground,0,0);
      
    }
    // draw all the sources
    for (int i=0; i<sources.size(); i++) {
      Source s = (Source)sources.get(i);
      s.drawMe(pa);
    }
  }

  // Update everything: the sensory field values and also
  // the ground image if we're going to draw it
  // -----------------------------
  void updateAll(PApplet pa) {
    update();
    if (visible) {
      updateGround(pa);
    }
  }

  // Update the sensory field values
  // -----------------------------
  void update() { 
    float sum;

    // step through all the blocks in our sensory field and compute the
    // sensory value at each block and at each pixel within each block
    for (int i=0; i<w; i+=blocksize) {
      for (int j=0; j<h; j+=blocksize) {
        sum = 0;
        for (int s=0; s<sources.size(); s++) {

          // first sum the sensory values from each source at this spot
          sum += this.getSource(s).getValue(i,j,true); 

          // now use that value to fill in all the pixels of this block
          // note that the value cannot exceed 255
          for (int p=0; p<blocksize; p++) {
            for (int q=0; q<blocksize; q++) {
              field[i+p][j+q] = (float)Math.min(sum*255,255);
            }
          }
        }
      } 
    }
  }

  // Update the ground by creating a new image for it. this is faster
  // than just drawing out the pixels every time, since we can just
  // draw the image the same image to the screen until it changes
  // -----------------------------
  void updateGround(PApplet pa) {
    if (sources.size() == 0) {
      ground = null;
    } 
    else {
      ground = new PImage(w,h);
      
      for (int i=0; i<w; i++) {
        for (int j=0; j<h; j++) {
          int v = (int)(getValue(i,j));
          // darker yellow where sense value is stronger
          ground.set(i,j,pa.color(v+colshiftt, (int)Math.min(200,v), v/8+colshiftt));
        }
      }
    }
  }
}
