import controlP5.*;
import netP5.*;



ControlP5 cp5;

// === EQ bands (fixed centers/Q, draggable gains in dB) ===
float band1Gain = 0;
float band2Gain = 0;
final float band1Center = 0.3;
final float band2Center = 0.7;
final float bandQ       = 10;
boolean draggingBand1 = false;
boolean draggingBand2 = false;

// === Other effect parameters ===
float delayTime       = 0.5;
float delayFeedback   = 0.4;
float distortionAmount= 0.5;
float distortionMix   = 0.5;
float chorusAmount    = 0.5;
float chorusDryWet    = 0.5;

void setup() {
  size(1680, 780, P3D);
  // Use orthographic projection to render a perfect cube without perspective distortion
  ortho(0, width, height, 0, -1000, 1000);
  frameRate(125);
  background(21,21,30);
  noStroke();
  surface.setResizable(false);
  surface.setLocation(100, 100);

  cp5 = new ControlP5(this);

  // Envelope knobs
  cp5.addKnob("AmpAttack").setLabel("Attack").setRange(0,1).setValue(0.4)
     .setPosition(85,350).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("AmpRelease").setLabel("Release").setRange(0,1).setValue(0.4)
     .setPosition(215,350).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("FilterAttack").setLabel("Attack").setRange(0,1).setValue(0.4)
     .setPosition(405,350).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("FilterRelease").setLabel("Release").setRange(0,1).setValue(0.4)
     .setPosition(535,350).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("PitchAttack").setLabel("Attack").setRange(0,1).setValue(0.4)
     .setPosition(725,350).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("PitchRelease").setLabel("Release").setRange(0,1).setValue(0.4)
     .setPosition(855,350).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));

  // Distortion knobs
  cp5.addKnob("distortionAmount").setLabel("Amount").setRange(0,1).setValue(distortionAmount)
     .setPosition(405,550).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("distortionMix").setLabel("Mix").setRange(0,1).setValue(distortionMix)
     .setPosition(535,550).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));

  // Chorus knobs
  cp5.addKnob("chorusAmount").setLabel("Amount").setRange(0,1).setValue(chorusAmount)
     .setPosition(725,550).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("chorusDryWet").setLabel("Dry/Wet").setRange(0,1).setValue(chorusDryWet)
     .setPosition(855,550).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));

  // Delay knobs
  cp5.addKnob("delayTime").setLabel("Time").setRange(0,1).setValue(delayTime)
     .setPosition(1045,550).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
  cp5.addKnob("delayFeedback").setLabel("Feedback").setRange(0,1).setValue(delayFeedback)
     .setPosition(1175,550).setRadius(45).setViewStyle(Knob.ARC)
     .setColorBackground(color(21,21,30)).setColorForeground(color(255,178,44))
     .setColorActive(color(250,170,40)).setFont(createFont("Futura",15));
}

void draw() {
  background(21,21,30);

  // Panels grid
  stroke(64); fill(12);
  rect(50,100,300,180,8);
  rect(370,100,300,180,8);
  rect(690,100,300,180,8);
  rect(1010,100,300,180,8);
  rect(1330,100,300,180,8);
  rect(50,300,300,180,8);
  rect(370,300,300,180,8);
  rect(690,300,300,180,8);
  rect(50,500,300,180,8);
  rect(370,500,300,180,8);
  rect(690,500,300,180,8);
  rect(1010,500,300,180,8);
  rect(1330,500,300,180,8);

  // Labels
  textAlign(CENTER, TOP);
  fill(255); textSize(18);
  text("Oscillator #1",  200, 110);
  text("Oscillator #2",  520, 110);
  text("Oscillator #3",  840, 110);
  text("Filter",        1160,110);
  text("LFO",           1480,110);
  text("Amp Env",       200, 330);
  text("Filter Env",    520, 330);
  text("Pitch Env",     840, 330);
  text("EQ",            200, 530);
  text("Distortion",    520, 530);
  text("Chorus",        840, 530);
  text("Delay",        1160, 530);
  text("Reverb",       1480, 530);

  // Waveforms & graphs
  SinWave();
  SawtoothWave();
  TriangleWave();
  drawFilterGraph();
  drawLFOWaveform();
  drawEQGraph();

  // --- Reverb: smaller wireframe cube ---
  pushMatrix();
    translate(1480, 590, 0);
    rotateX(frameCount * 0.01f);
    rotateY(frameCount * 0.013f);
    rotateZ(frameCount * 0.007f);
    noFill();
    stroke(255, 178, 44);
    strokeWeight(2);

    float s = 20;  // half-edge length = 20 → full cube = 40

    // front face (z = +s)
    line(-s,-s, s,  s,-s, s);
    line( s,-s, s,  s, s, s);
    line( s, s, s, -s, s, s);
    line(-s, s, s, -s,-s, s);

    // back face (z = -s)
    line(-s,-s,-s,  s,-s,-s);
    line( s,-s,-s,  s, s,-s);
    line( s, s,-s, -s, s,-s);
    line(-s, s,-s, -s,-s,-s);

    // side edges
    line(-s,-s, s, -s,-s,-s);
    line( s,-s, s,  s,-s,-s);
    line( s, s, s,  s, s,-s);
    line(-s, s, s, -s, s,-s);
  popMatrix();
}

void drawEQGraph() {
    int x0=50, y0=500, w=300, h=180;
    int midY = y0 + h/2;
    stroke(64); fill(12);
    rect(x0,y0,w,h,8);
    stroke(80); fill(255);
    textSize(12); textAlign(RIGHT, CENTER);
    for(int i=0; i<=4; i++){
        float db = map(i, 0, 4, +20, -60);
        float yy = y0 + i*(h/4);
        line(x0-5, yy, x0, yy);
        text(nf(db,1,0)+" dB", x0-8, yy);
    }
    fill(255,178,44); textSize(14);
    textAlign(LEFT, BOTTOM);
    text("Low Band: "+nf(band1Gain,1,1)+" dB",  x0+8,  y0+h-8);
    textAlign(RIGHT, BOTTOM);
    text("High Band: "+nf(band2Gain,1,1)+" dB", x0+w-8, y0+h-8);
    noFill(); stroke(255,178,44);
    beginShape();
        float g1n = map(band1Gain, -60, 20, -1, 1);
        float g2n = map(band2Gain, -60, 20, -1, 1);
        for(int i=0; i<=w; i++){
            float norm = i/(float)w;
            float d1 = g1n * exp(-sq((norm-band1Center)*bandQ));
            float d2 = g2n * exp(-sq((norm-band2Center)*bandQ));
            float y = midY - (d1 + d2)*(h/2);
            vertex(x0+i, y);
        }
    endShape();
    noStroke(); fill(255,178,44);
    float hx1 = x0 + band1Center*w;
    float hy1 = midY - map(band1Gain,-60,20,-1,1)*(h/2);
    ellipse(hx1, hy1, 12, 12);
    float hx2 = x0 + band2Center*w;
    float hy2 = midY - map(band2Gain,-60,20,-1,1)*(h/2);
    ellipse(hx2, hy2, 12, 12);
}

void mousePressed() {
    int x0=50, y0=500, w=300, h=180;
    float hy1 = map(band1Gain, 20, -60, y0, y0+h);
    float hy2 = map(band2Gain, 20, -60, y0, y0+h);
    if (dist(mouseX, mouseY, x0+band1Center*w, hy1) < 10) draggingBand1 = true;
    else if (dist(mouseX, mouseY, x0+band2Center*w, hy2) < 10) draggingBand2 = true;
}

void mouseDragged() {
    int y0=500, h=180;
    if (draggingBand1) {
        float cy = constrain(mouseY, y0, y0+h);
        band1Gain = map(cy, y0+h, y0, -60, 20);
    }
    if (draggingBand2) {
        float cy = constrain(mouseY, y0, y0+h);
        band2Gain = map(cy, y0+h, y0, -60, 20);
    }
}

void mouseReleased() {
    draggingBand1 = draggingBand2 = false;
}

// ControlP5 callbacks (no-op)
void AmpAttack(float v)     {}
void AmpRelease(float v)    {}
void FilterAttack(float v)  {}
void FilterRelease(float v) {}
void PitchAttack(float v)   {}
void PitchRelease(float v)  {}
void distortionAmount(float v){ distortionAmount = v; }
void distortionMix(float v)   { distortionMix = v; }
void chorusAmount(float v)    { chorusAmount = v; }
void chorusDryWet(float v)    { chorusDryWet = v; }
void delayTime(float v)       { delayTime = v; }
void delayFeedback(float v)   { delayFeedback = v; }

// Waveform & helpers
void SinWave() {
  stroke(255,178,44); noFill();
  float phase = frameCount * 0.025f;
  int yBase=210, amp=60, x0=70, x1=330;
  beginShape();
    for(int x=x0; x<=x1; x++){
      float t = map(x,x0,x1,0,TWO_PI*2);
      vertex(x,yBase+sin(t+phase)*amp);
    }
  endShape();
}

void SawtoothWave() {
  stroke(255,178,44); noFill();
  float phase=frameCount*0.025f; int yBase=210, x0=390, x1=650;
  beginShape();
    for(int x=x0; x<=x1; x++){
      float t=map(x,x0,x1,0,2)+phase, p=t-floor(t), v=-1+p*2;
      vertex(x,yBase+v*60);
    }
  endShape();
}

void TriangleWave() {
  stroke(255,178,44); noFill();
  float phase=frameCount*0.04f; int yBase=210, amp=60, x0=710, x1=970;
  beginShape();
    for(int x=x0; x<=x1; x++){
      float t=map(x,x0,x1,0,4)+phase; int s=floor(t); float f=t-s;
      float v=(s%2==0)?-1+f*2:1-f*2;
      vertex(x,yBase+v*amp);
    }
  endShape();
}

void drawFilterGraph() {
  stroke(255,178,44); noFill();
  int x0=1030, y0=140, w=260, h=180;
  beginShape();
    for(int i=0;i<=w;i++){
      float n=i/(float)w, m=1/sqrt(1+pow(n/0.5f,2));
      vertex(x0+i, y0+(1-m)*h);
    }
  endShape();
}

void drawLFOWaveform() {
  stroke(255,178,44); noFill();
  int x0=1330, y0=100, w=260, h=180, m=20;
  float phase=frameCount*0.02f;
  beginShape();
    for(int i=0;i<=w;i++){
      float a=map(i,0,w,0,TWO_PI);
      vertex(x0+m+i, y0+h/2+sin(a)*(h/2-10));
    }
  endShape();
  float p=(phase%TWO_PI)/TWO_PI;
  noStroke(); fill(255,178,44);
  ellipse(x0+m+p*w, y0+h/2+sin(phase)*(h/2-10), 12,12);
}
