class Param {
  String name;
  int type;
  float minValue, maxValue;
  float value;
  int[] osciModes;
  Param(String name_, int type_, float minValue_, float maxValue_, int[] osciModes_) {
    name = name_;
    type = type_; //FLOATVAL or INTVAL
    minValue = minValue_;
    maxValue = maxValue_;
    osciModes = osciModes_;
  }
}

class Shader {
  int id;
  int pos, previousPos;
  boolean previousActive;
  String name;
  ArrayList<Param> params = new ArrayList<Param>();
  ArrayList<Float> paramValues = new ArrayList<Float>();
  boolean paramChanged = true;

  PGraphics canvas = createGraphics(renderer.width, renderer.height);
  PGraphics result;
  int direction = 0;
  int directionParamIndex = -1;

  Shader() {
    canvas.beginDraw();
    canvas.image(renderer, 0, 0, canvas.width, canvas.height);
    canvas.endDraw();
  }

  void getValuesFromGUI() {
    try { //problematic
      params = gui.bricks.get(pos).params;
    }
    catch(Exception e) {
      println(e);
    }

    //this is only for checking if a param has changed. paramValues could also be used for a save/load function
    if (paramValues.size() != params.size()) {
      for (int i = 0; i < params.size(); i++) {
        paramValues.add(0.0);
      }
    }
    paramChanged = false;
    for (int i = 0; i < params.size(); i++) {
      if (paramValues.get(i) != params.get(i).value) {
        paramValues.set(i, params.get(i).value);
        paramChanged = true;
      }
    }

    if (pos != previousPos) {
      paramChanged = true;
    }
    previousPos = pos;

    try{
    if (previousActive != gui.bricks.get(pos).active) { //fails if brick has not yet been loaded but shader has
      paramChanged = true;
    }
    previousActive = gui.bricks.get(pos).active;
    } catch (Exception e){
      println("Brick not yet loaded, couldn't catch active state");
    }

    if (!paramChanged) {
      if (imageChanged) paramChanged = true;
      for (int i = this.pos; i >= 0; i--) {
        if (gui.shaderList.get(i).paramChanged) paramChanged = true;
      }
    }


    if (frameRate < 1) println("Rendering " + this + " on position " + pos + "...");
  }

  void changeParam(int paramNo, Param newParam) {
    gui.bricks.get(pos).params.remove(paramNo);
    gui.bricks.get(pos).params.add(paramNo, newParam);
    gui.bricks.get(pos).exchangeSlider(paramNo, newParam);
    paramChanged = true;
  }

  void shade() {
    if (renderSizeChanged || paramChanged) {
      this.setupCanvas();
      this.apply();
      this.render();
    } else {
      // println("nothing changed");
      renderer.beginDraw();
      renderer.image(result, 0, 0);
      renderer.endDraw();
    }/*
    this.setupCanvas();
     this.apply();
     this.render();
     */
  }

  void setupCanvas() {
    if (directionParamIndex >= 0) direction = (int)this.params.get(directionParamIndex).value;
    int cw = (direction == 0 || direction == 2) ? renderer.width : renderer.height;
    int ch = (direction == 0 || direction == 2) ? renderer.height : renderer.width;
    canvas = createGraphics(cw, ch);
    canvas.beginDraw();
    canvas.imageMode(CENTER);
    canvas.clear();
    canvas.pushMatrix();
    canvas.translate(cw/2, ch/2);
    canvas.rotate((PI/2)*direction);
    canvas.image(renderer, 0, 0);
    canvas.popMatrix();
    canvas.endDraw();
  }

  void apply() {
  }; //override me with an actual fragment shader

  void render() {
    renderer.beginDraw();
    renderer.imageMode(CENTER);
    renderer.pushMatrix();
    renderer.translate(renderer.width/2, renderer.height/2);
    renderer.rotate(-(PI/2)*direction);
    renderer.image(canvas, 0, 0);
    renderer.popMatrix();
    renderer.imageMode(CORNER);
    renderer.endDraw();
    result = createGraphics(renderer.width, renderer.height);
    result.beginDraw();
    result.image(renderer, 0, 0);
    result.endDraw();
    if (pos == gui.bricks.size()-1) {
      //println("Finished rendering");
      if (imageChanged) { //kind of hacky but it does synchronize the threads. sometimes.
        stepOne++;
        if (stepOne == 2) {
          stepOne = 0;
          imageChanged = false;
        }
      }
    }
  }
  @ Override String toString() {
    return getClass().getName();
  }
}
int stepOne = 0;
