import processing.core.*; 
import processing.xml.*; 

import ddf.minim.*; 
import java.lang.Thread; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class AquavasionSmack extends PApplet {

//imports


// audio
AudioPlayer music;
AudioPlayer music2;
AudioPlayer music3;
AudioPlayer player;
Minim minim;
boolean fading = false;
boolean fading2 = false;
// variables
int xVal;
int yVal;
int sizeVal;
int scoreVal = 0;
int finalScore = 0;
int multiplier = 0;
boolean game = true;
PShape lvlone;
PImage hammer;
// text & highscore
PFont font;
PFont font2;
String input = "";
String useinput = "";
boolean type = false;
ArrayList scores;
int arrayVal = 0;
ArrayList arr;
int noofarr = 0;
// timer related
int starttime = 0; //these are for the timer
int delaytimeVal = 0;
int timerCountdown = 3300;
ArrayList times;
// timer visual
float r = 40; // radius
float a = mouseX; // position x
float b = mouseY; // position y
float nx, ny;
int amountOfSeconds = 60;
float t;  // angle radians
int timeLeft;
// FileAccess
PrintWriter file;
// conditionals
boolean runonceTimer = true;
boolean success = true;

//prototyping
boolean beatscore = false;  //confetti effect FIXME

public void setup() {
  setupme();
}

public void draw() {
  TimerMe timer = (TimerMe)times.get(0);
  TimerMe timer1 = (TimerMe)times.get(1); //FIXME
  TimerMe timer2 = (TimerMe)times.get(2); //FIXME

  starttime = timer.startLeft();

  Collections.sort(scores);
  textFont(font);
  background(10, 100, 150);
  shapeMode(CORNER);
  shape(lvlone, 0, 0, width, height);

  //timeLeft = delaytime-(millis() - starttime)/1000;
  noStroke();
  fill(100, 255, 100, 150);
  //ellipse(a,b, 2*r,2*r);
  //delaytime = timer.delayLeft();
  t=((PI*2/timer.delayLeft())*timer.timeLeft())-PI/2;
  nx = a+r*cos(t);
  ny = b+r*sin(t);
  //    if(game) { stroke(0); line(a,b, a,b-r); line(a,b, nx,ny); }  //show timer hands and stuff
  fill(255, 100);
  pushMatrix();
  translate(a, b);
  rotate(radians(-90));
  float m = map(timer.timeLeft(), 0, timer.delayLeft(), 0, PI*2);
  arc(0, 0, r*2, r*2, 0, m);
  popMatrix();
  if (scoreVal >= 1 && !type) {
    textAlign(CENTER);
    if (floor(timer.timeLeft()/1000) >= 5) {
      multiplier = 0;
    }
    if (timer.timeLeft() < timerCountdown) {
      if(!fading2 && timer.timeLeft() > 0) {
        audioUpdate(3, 100);
        multiplier = 10;
        arr.add(new Clickers(3, 0, 0, 0));
        if(timerCountdown > 300) timerCountdown -= 300;
        fading2 = true;
      }
      fill(255, 200);
      textSize(37);
      text("quickly!", width/2,height/6);
      textSize(60);
      text(timer.timeLeft(), width/2,height/3.5f);
      textSize(24);
    }
    fill(255, 200);
    textSize(40);
    if (floor(timer.timeLeft()/1000) < 10) text("0"+floor(timer.timeLeft()/1000), a,b*5/4);
    else if (floor(timer.timeLeft()/1000) >= 10) text(floor(timer.timeLeft()/1000), a,b*5/4);
    textSize(24);
    textAlign(LEFT);
  }
  fill(255, 255);

  //scores
  textAlign(RIGHT);
  fill(255, 150);
  for (int i=0;i<scores.size();i++) {
    scoreKeep call =(scoreKeep)scores.get(i);
    if (i > scores.size()-2) fill(255, 255);
    else if (call.meName() == "you") fill(250, 250, 30, 255);
    else fill(200, 255, 90, 100);
    text(call.toString(), width-10, map(i, 0, scores.size(), scores.size(), 0)*20+5);
  }
  fill(255, 255);
  textAlign(LEFT);

  //call here
  for (int i = 0; i < arr.size(); i++) {
    Clickers boid = (Clickers)arr.get(i);
    boid.display();
  }

  //time and text
  textAlign(LEFT);
  if (scoreVal > finalScore) fill(90, 255, 90);
  if (scoreVal < finalScore) fill(255, 90, 90);
  text(Integer.toString(scoreVal)+" (beat "+finalScore+")", 10, 24);
  textAlign(CENTER);
  fill(255);
  //  text("Time: " + Integer.toString(delaytime-(millis() - starttime)/1000), width/2, 24);   //shows seconds of timer
  textSize(20);
  if (scores.size() >= 1) {
    scoreKeep skeep = (scoreKeep)scores.get(scores.size()-1);
    text(skeep.meName()+", you're winner!", width/2, height-10);
  }
  textSize(24);
  textAlign(LEFT);

  if (scoreVal >= 1) {
    //shows up on the mouse
    //if(game){
    //textAlign(CENTER);
    //text(Integer.toString(delaytime-(millis() - starttime)/1000), mouseX, mouseY+32);
    //if (scoreVal > finalScore) fill(0, 255, 0);
    //if (scoreVal < finalScore) fill(255, 0, 0);
    //text(scoreVal, mouseX, mouseY+52);
    //fill(255);
    //textAlign(LEFT);
    //}

    timer.delayIn(12000+delaytimeVal);
    if (runonceTimer) {
      //this goes into the timer code otherwise there's an endless loop which crashes
      //the audioUpdate function and the player...
      if (!fading) {
        audioUpdate(2, 100);
        fading = true;
      }    
      delaytimeVal = 0;
      timer.start();
      runonceTimer = false;
    }

    if (timer.isFinished()) {
      game = false;
      type = true;
    }
  } 
  else if (scoreVal == 0) {
    starttime = millis();
  }

  if (type) {
    if (fading) {
      audioUpdate(1, 1);
      arr.remove(2);
      arr.remove(1);
      arr.remove(0);
      fading = false;
      fading2 = false;
    }
    shapeMode(CENTER);
    shape(loadShape("both.svg"), width/2, height/2, 300,300);
    shapeMode(CORNER);
    starttime = millis();
    textAlign(CENTER);
    textSize(50);
    text("Your name?", width/2, height-height/7-50);
    fill(0xffDCC425);
    textFont(font2);
    text(input + "_", width/2, height-height/7);
    textFont(font);
    textAlign(LEFT);
  }

  //throw confetti and congratulate player
  if (scoreVal >= finalScore) {
  if(success) {
    player = minim.loadFile("success.mp3", 512);
    player.play();
    success = false;
  }
    //FIXME
    //also JIGGLE the objects on the screen
  }
}

public void audioUpdate(int fade, int milli) {
  if (fade == 1) {
    music2.rewind();
    music2.shiftGain(music.getGain(), 13, milli);
    music.shiftGain(music.getGain(), -80, milli);
    music3.shiftGain(music.getGain(), -80, milli);
  }
  if (fade == 2) {
    music.shiftGain(music.getGain(), 13, milli);
    music2.shiftGain(music.getGain(), -80, milli);
    music3.shiftGain(music.getGain(), -80, milli);
  }
  if (fade == 3) {
    music3.rewind();
    music.shiftGain(music.getGain(), -3, milli);
    music2.shiftGain(music.getGain(), -80, milli);
    music3.shiftGain(music.getGain(), 5, milli);
  }
}

class Clickers {
  PVector position;
  PShape shapeVal;
  int objVal;
  int sizeVal = (int)random(79, 100);
  int xVal = (int)random(sizeVal/2, width-sizeVal/2);
  int yVal=(int)random(sizeVal/2, height-sizeVal/2);
  int inputVal;

  Clickers(int _objVal, int _sizeVal, int _x, int _y) {
    objVal = _objVal;
    position = new PVector(_x, _y);
  }

  public void display() {
    shapeMode(CENTER);
    shape(loadShape("guy"+objVal+".svg"), xVal, yVal, sizeVal, sizeVal);
    shapeMode(CORNER);
  }

  public void kill() {
    if (game) {
      if (mouseX > xVal-sizeVal/2 && mouseY > yVal-sizeVal/2 && mouseX < xVal+sizeVal/2 && mouseY < yVal+sizeVal/2) {
        //FIXME cannot instantiate time properly
        TimerMe timer1 = (TimerMe)times.get(objVal);
        timer1.delayIn(1500);
        timer1.start();
        if (timer1.timeLeft() > 0) {
          shapeMode(CENTER);
          shape(loadShape("guy"+objVal+"a.svg"), xVal, yVal, sizeVal, sizeVal);
          shapeMode(CORNER);
        } 
        sizeVal = (int)random(79, 100);
        load();
        if(objVal != 3) scoreVal = (scoreVal + 10)+multiplier;
        if(objVal == 3) {
          TimerMe timer = (TimerMe)times.get(0);
          timer.startIn(5000);
          arr.remove(2);
          audioUpdate(2, 100);
          fading2 = false;
        }
        player = minim.loadFile("squeeze"+objVal+".mp3", 512);
        player.play();
        for (int i = 0; i < scores.size(); i++) {
          scoreKeep comp = (scoreKeep)scores.get(i);
          if (comp.meName() == "you") scores.remove(i);
        }
        scores.add(new scoreKeep("you", scoreVal));
      }
    }
  }



  public void load() {
    sizeVal = (int)random(79, 100);
    xVal=(int)random(sizeVal/2, width-sizeVal/2);
    yVal=(int)random(sizeVal/2, height-sizeVal/2);
  }

  public PVector getPosition() {
    return position;
  }
}

class FileAccess extends Thread {
  String[] justInput;
  String nameInput;
  int scoreInput;

  public void run() {
    return;  // exit Thread
  }

// use the following to activate
//  FileAccess fileme = new FileAccess();
//  fileme.start(); //run Thread!
//  fileme.readFile();
  public void readFile() {
    for(int i=scores.size()-1;i>=0;i--) {
      scores.remove(i);
    }
    justInput = loadStrings(sketchPath("bunk/hs.txt"));
    for (int i=0;i<justInput.length;i++) {
      String[] pieces = split(justInput[i], '\t');
      if (pieces.length == 2) {
        nameInput = pieces[0];
        scoreInput = PApplet.parseInt(pieces[1]);
        scores.add(new scoreKeep(nameInput, scoreInput));
      }
    }
  }

// use the following to activate
//  FileAccess fileme = new FileAccess();
//  fileme.start(); //run Thread!
//  fileme.writeFile();
  public void writeFile() {
    try {
      file = createWriter(sketchPath("bunk/hs.txt"));
      //file = new FileWriter(sketchPath("bunk/hs.txt"), true); //bool tells to append
      //file.write(commentsVal+"\n", 0, commentsVal.length()+1); //(string, start char, end char)
      for (int i=0;i<scores.size();i++) {
        scoreKeep call =(scoreKeep)scores.get(i);
        if(call.meName() != "you") file.println(call.meName()+"\t"+call.meScore());
      }
      file.close();
    } 
    catch(Exception e) {  
      println("Error: Can't open file!");
    }
  }

}

// beats kick in when game starts, and then extra sounds when winning

// before and after images for the current clickers

// enemy lives
// multipliers
// time additions
// APM




//// PVector for the position of these, and perhaps id number is needed

// do this with SQL
// load directly from Server in draw() to make it realtime
// yay, datasync issues!
// load from Server and check extra variable and compare before uploading to Server again

// add UI
// change/remove mouse hover stats

// spawn confetti > highscore

// remove the invasion and replace with endgame images

// fix hitbox (create classes or rounded hitboxes)
// add bad apple, extending time object

// predetermined game state for competitive round
// instead of having random values for x-yVal, have the game preload into an arraylist
// find a way to make a hash of the level for continued use

// extend feedback after hitting an object so that it shows a reaction as well with the sound,
// perhaps an animation where they explode as well

class TimerMe {
  int starttime;
  int delaytime;

  TimerMe() {
  }

  public void delayIn(int _delaytime) {
    delaytime = _delaytime;
  }

  public void start() {
    starttime = millis();
  }

  public void startIn(int _starttime) {
    starttime = starttime + _starttime;
  }

  // return true if finished
  public boolean isFinished() { 
    if (millis()- starttime >= delaytime) {  //check timepassed to delaytime in ms; 1s=1000ms
      return true;
    } 
    else {
      return false;
    }
  }

  public int timeLeft() {
    int timeLeft = delaytime-(millis() - starttime);
    return timeLeft;
  }

  public int startLeft() {
    return starttime;
  }

  public int delayLeft() {
    return delaytime;
  }
}

public void keyTyped() {
  if (type) {
    if (key == BACKSPACE && input.length() == 0) input = "";
    else if (key == BACKSPACE && input.length() > 0) { 
      input = input.substring(0, input.length()-1);
    }
    else if (keyCode == KeyEvent.VK_SPACE && input.length() == 0) input = "";
    else if (key == ENTER && input != "") {
      useinput = input;
      manipulate();
    }
    else if (key == ENTER && input == "") {
      useinput = "opponent";
      manipulate();
      //  type = (type == true) ? false : true;  //toggle bool for input text
    }
    else if (key != ENTER && input.length() < 11) input += key;
  }
  if (!type) {
    if (key == 'r') scoreVal = 0;
  }
}

public void manipulate() {
  for (int i = 0; i < scores.size(); i++) {
    scoreKeep comp = (scoreKeep)scores.get(i);
    if (comp.meName() == "you") scores.remove(i);
  }
  scores.add(new scoreKeep(useinput, scoreVal));

  if (scoreVal >= finalScore) {
    finalScore = scoreVal;
  }
  scoreVal = 0;

  //reset the game
  input = "";
  game = true;
  type = false;
  
  Collections.sort(scores);
  FileAccess fileme = new FileAccess();
  fileme.start(); //run Thread!
  fileme.writeFile();

  arr.add(new Clickers(1, 0, 0, 0));
  arr.add(new Clickers(2, 0, 0, 0));
  
  success = true;
  runonceTimer = true;
}

public void mouseReleased() {

  for (int i = 0; i < arr.size(); i++) {
    Clickers boid = (Clickers)arr.get(i);
    boid.kill();
  }


  //  if (game) {
  //    if (mouseX > xVal-aSize/2 && mouseY > yVal-aSize/2 && mouseX < xVal+aSize/2 && mouseY < yVal+aSize/2) {
  //      aSize = (int)random(79, 100);
  //      xVal=(int)random(aSize/2, width-aSize/2);  //boundaries for the spawn area are randomized with half the aSize
  //      yVal=(int)random(aSize/2, height-aSize/2);
  //      scoreVal++;
  //      player = minim.loadFile("squeeze1.mp3", 512);
  //      player.play();
  //    }

  //    if (mouseX > xVal1-bSize/2 && mouseY > yVal1-bSize/2 && mouseX < xVal1+bSize/2 && mouseY < yVal1+bSize/2) {
  //      bSize = (int)random(79, 100);
  //      xVal1=(int)random(bSize/2, width-bSize/2);
  //      yVal1=(int)random(bSize/2, height-bSize/2);
  //      scoreVal++;
  //      player = minim.loadFile("squeeze2.mp3", 512);
  //      player.play();
  //    }
  //  }
}

class scoreKeep implements Comparable {
  int score;
  String name;

  scoreKeep(String x, int y) {
    name = x;
    score = y;
  }

  public String toString() {
    return name+": "+score;
  }

  public String meName() {
    return name;
  }
  
  public int meScore() {
    return score;
  }

  public int compareTo(Object o) {
    scoreKeep sk = (scoreKeep)o;
    return score-sk.score;
  }
}

public void setupme() {
  size (600, 400);
  frame.setTitle("Aquavasion Smack");
  font = createFont("dnk.ttf", 24);  //blambot.com font, free for non-profit use
  font2 = createFont("RevseVy.ttf", 50);  //evn's
  lvlone = loadShape("lvlone.svg");
  hammer = loadImage("hammer.png");
  smooth(); //only needs to be called once
  cursor(hammer, 5, 15);
  scores = new ArrayList();
  arr = new ArrayList(); // initialisation arr
  times = new ArrayList();
  minim = new Minim(this);

  FileAccess fileme = new FileAccess();
  fileme.start(); //run Thread!
  fileme.readFile();

  finalScore = 130;

  arr.add(new Clickers(1, 0, 0, 0));
  arr.add(new Clickers(2, 0, 0, 0));

  a = width/12; // position x
  b = height/6; // position y
  music = minim.loadFile("evn_theyre_coming.mp3", 512);
  music.loop();
  music2 = minim.loadFile("evn_its_pretty_quiet.mp3", 512);
  music2.loop();
  music3 = minim.loadFile("evn_twitch.mp3", 512);
  music3.loop();
  audioUpdate(1, 1);
  times.add(new TimerMe());
  times.add(new TimerMe());
  times.add(new TimerMe());
  times.add(new TimerMe());
}

public void stop() {
  // always close Minim audio classes when you are done with them
  player.close();
  minim.stop();
  super.stop();
}

  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#F0F0F0", "AquavasionSmack" });
  }
}
