// ---------------------------------------------------------
// Class: Source
// ---------------------------------------------------------
// A light source with a certain range of influence
// ---------------------------------------------------------

import java.lang.*;
import processing.*;
import processing.core.*;

class Source {

  static final float SIZE = 10f;
  static final float RAD  = 5f;

  float x, y;
  float maxrange;
  boolean grabbed = false;
  boolean drawrange = false;
  int coloshiftt=0;

  // Constructor
  // -----------------------------
  Source(float x, float y, float r, int coloshift) {
    this.x = x;
    this.y = y;
    maxrange = r; 
    coloshiftt=coloshift;
  }

  // Set the location of this source making sure it is in bounds
  // -----------------------------
  void setLocation(int w, int h, float x, float y) {
    this.x = x;
    this.y = y;
    checkBounds(w,h);
  }

  // Draw this source
  // -----------------------------
  void drawMe(PApplet pa) {

    // draw the source
    pa.fill(255-coloshiftt,255-coloshiftt,0+coloshiftt);
    pa.stroke(255,150-coloshiftt,0);
    pa.ellipse(x,y,SIZE,SIZE);

    // draw a circle of maxradius around the source
    if (drawrange) {
      pa.stroke(255-coloshiftt,255,255-coloshiftt,45);
      pa.strokeWeight(2);
      pa.noFill();
      pa.ellipse(x,y,maxrange*2/3,maxrange*2/3);
    }

    pa.noStroke();
    pa.fill(255);
  }

  // Return true if this source has been clicked and set the grabbed flag
  // -----------------------------
  boolean hitTest(float xhit, float yhit, float big) {
    if ((xhit < x+RAD+15*big) && (xhit > x-RAD-15*big) && (yhit < y+RAD+15*big) && (yhit > y-RAD-15*big)) {
      grabbed = true;
    } 
    else {
      grabbed = false; 
    }
    return grabbed;
  }

  // Get the influence of this source at location (tx,ty)
  // Inverse influence is returned when positiveInfluence flag is false
  // -----------------------------
  float getValue(float tx, float ty, boolean positiveInfluence) {
    float d = distance(tx,ty,x,y);
    if (d >= maxrange) {
      // point is outside the radius of influence
      return ((positiveInfluence) ? 0 : 1);
    }
    // non-linear influence based on proximity to source
    float f = getInfluence(d, maxrange); 
    return ((positiveInfluence) ? f : 1-f);
  }

  // Do we want to draw the source range of influence?
  // -----------------------------
  boolean getDrawRange() {
    return drawrange;   
  }

  // Set the flag to draw the source range of influence
  // -----------------------------
  void setDrawRange(boolean b) {
    drawrange = b;   
  }

  // Keep our coordinates within the display bounds
  // -----------------------------
  void checkBounds(int w, int h) {
    if (x > w) x = w;
    if (x < 0) x = 0;
    if (y > h-100) y = h-100;
    if (y < 0) y = 0;   
  }

  // Get the distance between two points
  // -----------------------------
  float distance(float x1, float y1, float x2, float y2) {
    // return the distance between the points (x1,y1) and (x2,y2)
    return (float) Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
  }

  // Get the (non-linear) strength based on proximity
  // -----------------------------
  float getInfluence(float r, float rmax) {
    // get the ratio between 0-1 of how close r is to the source
    // (0 when r at range perimeter, 1 when r is at source center)
    float ratio = (rmax - (float)Math.min(r, rmax)) / rmax;

    // compute the (non-linear) strength (0-1) based on how close we are. 
    // -- note: cos(t) ranges from 1 to -1 as t goes from 0 to PI.
    // -- want: number from 0-1, so need to scale by 0.5 and shift by 0.5
    // -- want: increasing strength closer to source, so need to flip
//    return (float)(0.5 - 0.5*(Math.exp(-290/200*ratio)));
    return (float)(Math.pow(ratio,2));

  }
}
