/* autogenerated by Processing revision 1293 on 2024-09-29 */
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;

import ch.bildspur.vision.*;
import ch.bildspur.vision.result.*;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PImage;
import processing.video.Capture;
import processing.sound.*;
import processing.video.Capture;
import processing.core.PImage;
import processing.sound.*;

import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class testwalk_mouse extends PApplet {

/**
 * Alpha Mask. 
 * 
 * Loads a "mask" for an image to specify the transparency 
 * in different parts of the image. The two images are blended
 * together using the mask() method of PImage. 
 */

PImage img;
PImage imgC;

//import for YOLO









//Camera camera;
Persons persons = new Persons();
PImage inputImage;

int x1,x2,x3,x4 = 0;
int y1,y2,y3,y4 = 0;
int start = millis();

//DeepVision deepVision = new DeepVision(this);
//YOLONetwork yolo;
//ResultList<ObjectDetectionResult> detections;

Sight[] sights;
boolean calibrated = false;

public void setup() {
  /* size commented out by preprocessor */;
  frameRate(30);

  img = loadImage("ka-dark.png");
  img.resize(1920,1080);
  
  SoundFile[] sounds =  {new SoundFile(this, "1.mp3"), new SoundFile(this, "2.mp3"), new SoundFile(this, "3.mp3"), new SoundFile(this, "4.mp3"), new SoundFile(this, "5.mp3")}; 
  
    for (SoundFile sound : sounds){
      sound.play();
    }
  
  sights = new Sight[]{
    new Sight( (int) (0.31f * img.width) , (int) (0.12f * img.height) , "hka.png" , "HKA" ,sounds),
    new Sight( (int) (0.573f * img.width), (int) (0.137f * img.height), "bahn.png", "bahn" ,sounds),
    new Sight( (int) (0.478f * img.width), (int) (0.124f * img.height), "schlossgarten.png", "schlossgarten" ,sounds),
    //new Sight( (int) (0.524 * img.width), (int) (0.085 * img.height), "schlosssee.png", "schlosssee" ,sounds),
    new Sight( (int) (0.710f * img.width), (int) (0.020f * img.height), "hardtwald.png", "hardtwald" ,sounds),
    new Sight( (int) (0.863f * img.width), (int) (0.081f * img.height), "friedhof.png", "friedhof" ,sounds),
    new Sight( (int) (0.800f * img.width), (int) (0.342f * img.height), "Oststadt.png", "Oststadt" ,sounds),
    new Sight( (int) (0.742f * img.width), (int) (0.361f * img.height), "bernarduskirche.png", "bernarduskirche" ,sounds),
    new Sight( (int) (0.850f * img.width), (int) (0.524f * img.height), "gottesaue.png", "gottesaue" ,sounds),
    new Sight( (int) (0.885f * img.width), (int) (0.478f * img.height), "schlachthof.png", "schlachthof" ,sounds),
    // new Sight( (int) (0.938 * img.width), (int) (0.530 * img.height), "messplatz.png", "messplatz" ,sounds),
    new Sight( (int) (0.727f * img.width), (int) (0.563f * img.height), "Citypark.png", "Citypark" ,sounds),
    new Sight( (int) (0.693f * img.width), (int) (0.295f * img.height), "kit.png", "kit" ,sounds),
    new Sight( (int) (0.607f * img.width), (int) (0.351f * img.height), "Kronenplatz.png", "Kronenplatz" ,sounds),
    // new Sight( (int) (0.459 * img.width), (int) (0.900 * img.height), "hbf.png", "hbf" ,sounds),
    new Sight( (int) (0.470f * img.width), (int) (0.694f * img.height), "zoo.png", "zoo" ,sounds),
    new Sight( (int) (0.548f * img.width), (int) (0.625f * img.height), "Werderplatz.png", "Werderplatz" ,sounds),
    new Sight( (int) (0.567f * img.width), (int) (0.572f * img.height), "Schauburg.png", "Schauburg" ,sounds),
    new Sight( (int) (0.546f * img.width), (int) (0.515f * img.height), "theater.png", "theater" ,sounds),
    new Sight( (int) (0.498f * img.width), (int) (0.470f * img.height), "ettlingertor.png", "ettlingertor" ,sounds),
    new Sight( (int) (0.410f * img.width), (int) (0.458f * img.height), "Gericht.png", "Gericht" ,sounds),
    new Sight( (int) (0.451f * img.width), (int) (0.420f * img.height), "natur.png", "natur" ,sounds),
    new Sight( (int) (0.469f * img.width), (int) (0.396f * img.height), "Friedrichsplatz.png", "Friedrichsplatz" ,sounds),
    new Sight( (int) (0.510f * img.width), (int) (0.377f * img.height), "marktplatz.png", "marktplatz" ,sounds),
    new Sight( (int) (0.493f * img.width), (int) (0.339f * img.height), "rathausturm.png", "rathausturm" ,sounds),
    new Sight( (int) (0.441f * img.width), (int) (0.367f * img.height), "stephan.png", "stephan" ,sounds),
    new Sight( (int) (0.383f * img.width), (int) (0.325f * img.height), "euro.png", "euro" ,sounds),
    new Sight( (int) (0.404f * img.width), (int) (0.302f * img.height), "yangda.png", "yangda" ,sounds),
    new Sight( (int) (0.267f * img.width), (int) (0.277f * img.height), "Christuskirche.png", "Christuskirche" ,sounds),
    new Sight( (int) (0.208f * img.width), (int) (0.403f * img.height), "sophienstraße.png", "sophienstraße" ,sounds),
    new Sight( (int) (0.217f * img.width), (int) (0.648f * img.height), "zkm.png", "zkm" ,sounds),
    new Sight( (int) (0.089f * img.width), (int) (0.743f * img.height), "gka.png", "gka" ,sounds),
    new Sight( (int) (0.519f * img.width), (int) (0.199f * img.height), "schloss.png", "gka" ,sounds),
    
  };
  
  img.loadPixels();
  
  imgC = loadImage("circle_squared.png");
  int sizeC = 300;
  imgC.resize(sizeC,sizeC);
  
  // Only need to load the pixels[] array once, because we're only
  // manipulating pixels[] inside draw(), not drawing shapes.
  loadPixels();
  
  inputImage = new PImage(1920, 1080, RGB);
  
  
  PFont f = createFont("ttf", 18);
  textFont(f);
}


public void draw() {
  
  int sizeC = 300;  
  
  int[] personsX = new int[1];
  int[] personsY = new int[1];
  
  personsX[0] = mouseX;
  personsY[0] = mouseY;
  persons.calculatePersonPositions(personsX, personsY);
  
  // colour variations
  for (int x = 0; x < img.width; x++) {
    for (int y = 0; y < img.height; y++ ) {
      // Calculate the 1D location from a 2D grid
      int loc = x + y*img.width;
      // Get the R,G,B values from image
      float r,g,b;
      r = red (img.pixels[loc]);
      g = green (img.pixels[loc]);
      b = blue (img.pixels[loc]);
      // Calculate an amount to change brightness based on proximity to the mouse
      float maxdist = 50;//dist(0,0,width,height);
      float d = persons.mindist(x, y);
      float adjustbrightness = 0;
      if(d < 102)
        adjustbrightness = 0;
      else
        adjustbrightness = 32*(maxdist-d)/maxdist;
      if(adjustbrightness < -128)
        adjustbrightness = -128;
      
      if(adjustbrightness < 0){
        float gray = 0.2126f * r + 0.7152f * g + 0.0722f * b;
        r = r * (128 + adjustbrightness)/128 + gray * (0-adjustbrightness)/128;
        g = g * (128 + adjustbrightness)/128 + gray * (0-adjustbrightness)/128;
        b = b * (128 + adjustbrightness)/128 + gray * (0-adjustbrightness)/128;
      } 
      
      // Constrain RGB to make sure they are within 0-255 color range
      r = constrain(r, 0, 255);
      g = constrain(g, 0, 255);
      b = constrain(b, 0, 255);
      // Make a new color and set pixel in the window
      int c = color(r, g, b);
      //if (adjustbrightness == -128)
      //  c = color(r);
      pixels[y*width + x] = c;
    }
  }
  updatePixels();

  
  for (Persons.Person p: persons.persons){
  // rotation of circle on user position
    float speedOfRotation = .0002f; // higher values go faster, e.g. .02 is double speed
    float amtToRotate = millis()*speedOfRotation;
    pushMatrix();
    translate(p.x, p.y);
    rotate(amtToRotate);
    imageMode(CENTER);
    image(imgC,0, 0);
    popMatrix();
  }
  
  
  imageMode(CENTER);
  for (Sight sight: sights){
    float d = persons.mindist(sight.x,sight.y);
    sight.drawSight(d);
  }
  //fill(0);
  
  //text(mouseX / (float)img.width + " : " + mouseY / (float)img.height ,mouseX,mouseY);
  
  
}



public class Camera{
  
  private final String CAM_NAME = "USB Camera VID:1133 PID:2085";
  private Capture cam;
  private int x1,x2,x3,x4 = 0;
  private int y1,y2,y3,y4 = 0;
  
  public Camera(testwalk_mouse window){
    
    for (String camName : Capture.list()){
      if(camName.equals(CAM_NAME)){
          this.cam = new Capture(window,CAM_NAME, 20);
      }
    }
    
    if (this.cam == null){
      println("external Camera not found");
      this.cam = new Capture(window, "pipeline:autovideosrc");
    }
    
    this.cam.start();
  }
  
  
  public PImage read(){
    if (cam.available()) {
      cam.read();
    }
    
    return cam;
  }
  
  /**
  *  Transforms Pixel coordinates from a position on the camera Image to a position on the MapImage.
  *  Works under the assumption, the map is a rectangle on the camera Image which is parallel to the camera Image (not rotated).
  *
  *  @param x X coordinate on the camera image
  *  @param y Y coordinate on the camera image
  *  @return    Int Array with coordinates on the MapImage. X coordinate at first position, Y coordinate at second position
  **/
  public int[] CamPosToMap(int x, int y){
    int[] newPos = new int[2];
    
    // 
    newPos[0] =(int) ((x -x1) / (float)(x1 - x2) * img.width) *-1;
    newPos[1] =(int) ((y -y1) / (float)(y1 - y3) * img.height) * -1;
    return newPos;
  }
  
  
  public void calibrate(){
    
    //draw Colored Rectangles, to find on the Camera Stream
    fill(color(255,0,0));
    rect(0,0,img.width / 2, img.height / 2);
    
    fill(color(0,255,0));
    rect(img.width / 2,0, img.width , img.height / 2);
    
    fill(color(0,0,255));
    rect(0,img.height / 2,img.width / 2, img.height);
    
    
    if (cam.available()) {
      cam.read();
    } else {
      return;
    }
    
    //Find the colored Rectangles on the Video by searcheing the colors and calculate the center.
    //Discovered Colors are set to black, to verify they have bin found 
    //and to aviode impact on calibration, wehn camera image is drawwn.
    int delta = 50;
    
    int[] sumX = new int[]{0,0,0,0};
    int[] sumY = new int[]{0,0,0,0};
    int[] count = new int[]{0,0,0,0};
    
    for (int x = 0; x < cam.width; x++) {
      for (int y = 0; y < cam.height; y++ ) {
        int loc = x + y*cam.width;
        // Get the R,G,B values from image
        float r,g,b;
        r = red (cam.pixels[loc]);
        g = green (cam.pixels[loc]);
        b = blue (cam.pixels[loc]);
        
        if(r -g > delta && r -b > delta){
          count[0] ++;
          sumX[0] += x;
          sumY[0] += y;
          r = 0;
          g = 0;
          b = 0;
        }
        else if(g -r > delta && g -b > delta){
          count[1] ++;
          sumX[1] += x;
          sumY[1] += y;
          r = 0;
          g = 0;
          b = 0;
        }
        else if(b -r > delta && b -g > delta){
          count[2] ++;
          sumX[2] += x;
          sumY[2] += y;
          r = 0;
          g = 0;
          b = 0;
        }
        else {
          count[3] ++;
          sumX[3] += x;
          sumY[3] += y;
        }
        
        int c = color(r, g, b);
        //if (adjustbrightness == -128)
        //  c = color(r);
        cam.pixels[loc] = c;
      }
    }
    
    image(cam,img.width/2,img.height/2);
    
    stroke(color(255,255,0));
    noFill();
    
    //calculate centers of the colored Rectangles
    // x1,y1 = top left
    // x2,y2 = top right
    // x3,y3 = bottom left
    // x4,y4 = bottom right
    
    int x1_mid = sumX[0] / max(1,count[0]);
    int x2_mid = sumX[1] / max(1,count[1]);
    int x3_mid = sumX[2] / max(1,count[2]);
    
    int y1_mid = sumY[0] / max(1,count[0]);
    int y2_mid = sumY[1] / max(1,count[1]);
    int y3_mid = sumY[2] / max(1,count[2]);
    
    int x4_mid = x3_mid + (x2_mid - x1_mid);
    int y4_mid = y2_mid + (y3_mid - y1_mid);
    
    //calculate edge coordinates
    x1 = x1_mid - (x2_mid - x1_mid)/2;
    y1 = y1_mid - (y3_mid - y1_mid)/2;
    
    x2 = x2_mid + (x2_mid - x1_mid)/2;
    y2 = y2_mid - (y3_mid - y1_mid)/2;
    
    x3 = x3_mid - (x4_mid - x3_mid)/2;
    y3 = y3_mid + (y3_mid - y1_mid)/2;
    
    x4 = x4_mid + (x4_mid - x3_mid)/2;
    y4 = y4_mid + (y4_mid - y2_mid)/2;
    
    //draw center points on camera image on the bottom right of the window
    line(x1_mid + img.width/2, y1_mid + img.height/2, x2_mid + img.width/2, y2_mid + img.height/2);
    line(x1_mid + img.width/2, y1_mid + img.height/2, x3_mid + img.width/2, y3_mid + img.height/2);
    line(x2_mid + img.width/2, y2_mid + img.height/2, x4_mid + img.width/2, y4_mid + img.height/2);
    line(x3_mid + img.width/2, y3_mid + img.height/2, x4_mid + img.width/2, y4_mid + img.height/2);
    
    //draw edge points
    line(x1 + img.width/2, y1 + img.height/2, x2 + img.width/2, y2 + img.height/2);
    line(x1 + img.width/2, y1 + img.height/2, x3 + img.width/2, y3 + img.height/2);
    line(x2 + img.width/2, y2 + img.height/2, x4 + img.width/2, y4 + img.height/2);
    line(x3 + img.width/2, y3 + img.height/2, x4 + img.width/2, y4 + img.height/2);
    
    /*
    x1 -= img.width/2;
    x2 -= img.width/2;
    x3 -= img.width/2;
    x4 -= img.width/2;
    
    y1 -= img.height/2;
    y2 -= img.height/2;
    y3 -= img.height/2;
    y4 -= img.height/2;
    */
    /*rect(
      sumX[0] / max(1,count[0]) + img.width/2, 
      sumY[0] / max(1,count[0]) + img.height/2,
      -(sumX[0] / max(1,count[0]) + img.width/2) + (sumX[1] / max(1,count[1]) + img.width/2),
      -(sumY[0] / max(1,count[0]) + img.height/2)+ (sumY[2] / max(1,count[2]) + img.height/2));
      */
      
     /*
    fill(0);
    
    println("cam : " + cam.width + " " + cam.height);
    text("box : " + sumX[0] / max(1,count[0]) + ":" + sumY[0] / max(1,count[0]) + " --> " 
      + sumX[1] / max(1,count[1]) + ":" + sumY[2] / max(1,count[2]), img.width/2,img.height/4*3);
    
    int loc = cam.width/4 + cam.height /4 * cam.width;
    text("red : " + red(cam.pixels[loc]) + " : " + green(cam.pixels[loc]) + " : " + blue(cam.pixels[loc]), img.width/4, img.height/4);
    
    loc = cam.width/4*3 + cam.height /4 * cam.width;
    text("green : " + red(cam.pixels[loc]) + " : " + green(cam.pixels[loc]) + " : " + blue(cam.pixels[loc]), img.width/4*3,img.height/4);
    
    loc = cam.width/4 + cam.height /4 * cam.width * 3;
    text("blue : " + red(cam.pixels[loc]) + " : " + green(cam.pixels[loc]) + " : " + blue(cam.pixels[loc]), img.width/4,img.height/4*3);
    
    println("counts : " + count[0] + ":" + count[1] + ":" + count[2] + ":" + count[3]);
    */
  }
  

}
public class Persons{
   
  private final int DISTANZE_NO_MOVE =10;
  private final int DISTANZE_SAME_PERSON = 200;
  private final int FRAMES_BEFORE_DELETE = 10;
  
  public class Person{
    public int x;
    public int y;
    public int framesSinceLastSeen;
    public boolean matched = false;
    public int matched_id;
    
    public Person(int x, int y){
      this.x = x;
      this.y = y;
      this.framesSinceLastSeen = 0;
    }
  }
  
  public ArrayList<Person> persons = new ArrayList<Person>();
  
  public Persons(){
  }
  
  public void calculatePersonPositions(int[] personsX, int[] personsY){
    
    boolean matched[] = new boolean[personsX.length];
    for (int j=0;j<personsX.length;j++){
      matched[j] = false;
    }
    
    for (int j=0;j<personsX.length;j++){
      float d_closest_Person = Float.MAX_VALUE;
      Person p = null;
      int matched_id = 0;
      
      for (int i=0;i<personsX.length;i++){
        for (Person person_to_match : persons){
          
          if(!person_to_match.matched){
            float d = dist( person_to_match.x, person_to_match.y, personsX[i], personsY[i]);
            
            if(d < d_closest_Person){
              d_closest_Person = d;
              p = person_to_match;
              matched_id = i;
              println("matched");
            }
          }
        }
      }
      
      println("--------------");
      
      if(d_closest_Person < DISTANZE_SAME_PERSON && p!= null){
        p.matched = true;
        p.matched_id = matched_id;
        matched[matched_id] = true;
      } else {
        //No more Persons to match found
        break;
      }
      
    }
    
    
    //update Coordinates
    ArrayList<Person> personsToRemove = new ArrayList<Person>();
    for (Person p : persons){
      if(p.matched){
          float d = dist( p.x, p.y, personsX[p.matched_id], personsY[p.matched_id]);
          if(d > DISTANZE_NO_MOVE) {
            p.x = personsX[p.matched_id];
            p.y = personsY[p.matched_id];
            p.framesSinceLastSeen = 0;
          }
      }else{
        p.framesSinceLastSeen ++;
        if( p.framesSinceLastSeen > this.FRAMES_BEFORE_DELETE){
          personsToRemove.add(p);
        }
      }
    }
    
    //remove Persons not seen for too long
    for (Person p : personsToRemove){
        persons.remove(p);
    }
    
    //create new Persons for deteced Persons, that were not matched
    for (int i=0;i<personsX.length;i++){
      if(!matched[i]){
        persons.add(new Person(personsX[i], personsY[i]));
      }
    }
    
    //reset matched persons 
    for (Person p : persons){
      p.matched = false;
    }
    
  }
  
  
  
  
  public float mindist(int x,int y){
    float d = Float.MAX_VALUE;
    
    for (Person p : persons){
      float d_new = dist(x,y,p.x,p.y);
      if(d_new < d){
        d = d_new;
      }
    }
    
    return d;
  }
}


public class Sight{
  
  private int x;
  private int y;
  private String name;
  private PImage img;
  private final int IMG_SIZE = 300;
  private final int CIRCLE_SIZE = 100;
  private final int ANIMATION_DURATION  = 1000;
  private final float CIRCLE_DELAY = ANIMATION_DURATION / 3 * 0.8f;
  private SoundFile[] sounds;
  private int start = 0;
  
  public Sight(int x,int y ,String fileName, String name, SoundFile[] sounds){
    this.x = x;
    this.y = y;
    this.img = loadImage(fileName);
    this.img.resize(IMG_SIZE,IMG_SIZE);
    this.name = name;
    this.sounds = sounds;
    
  }
  
  private void playSound(){
    int rand = (int) (Math.random() * sounds.length);
    
    for (SoundFile sound : sounds){
      try{
        sound.stop();
      }catch (Exception e){
        
      }
    }
    try{
      sounds[rand].cue(0);
      sounds[rand].play();
    }catch (Exception e){
        
    }
  }
  
 public void drawSight(float minDist){
   
   
   if(minDist < 200 || millis() - start < ANIMATION_DURATION){
     
     if(start == 0){
       start = millis();
       playSound();
     }
     if(millis() - start < ANIMATION_DURATION){
       PImage scaledImg = this.img.copy();
       
       
       if (millis() - start < ANIMATION_DURATION / 3){
         //println(millis() - start);
         scaledImg.resize(IMG_SIZE,max(1,IMG_SIZE * (millis() - start) / (ANIMATION_DURATION /3)));
       }else if(millis() - start > CIRCLE_DELAY && millis() - start < ANIMATION_DURATION){
         int c = color(255,255,255,128);
         fill(c);
         noStroke();
         circle(this.x,this.y,CIRCLE_SIZE * (millis() - start -CIRCLE_DELAY) / (ANIMATION_DURATION - CIRCLE_DELAY) );
       }
       
       image(scaledImg,this.x,this.y);
       
     }else{
       if (millis() - start < ANIMATION_DURATION * 2){
         int c = color(255,255,255,128);
         noFill();
         strokeWeight(5);
         stroke(c);
         circle(this.x,this.y,CIRCLE_SIZE * (millis() - start -CIRCLE_DELAY) / (ANIMATION_DURATION - CIRCLE_DELAY) );
       }
       
       image(this.img,this.x,this.y);
     }
     
   }else{
     start = 0;
     return;
   }
   
 }
}


  public void settings() { size(1920, 1080); }

  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "testwalk_mouse" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
