import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
Minim minim;

int GRIDSIZE=40;
int SQUARESIZE = 20;

int GM_BEFORE=1;
int GM_DURING=2;
int GM_WAITING=3;
int GM_AFTER=4;

int gameMode = GM_BEFORE;
int gameLevel = -1;
int loopTimeCnt = 0;

Snake snake;
Timer timer;

int FASTSPEED = 1;
int REGULARSPEED = 2;

boolean loopTime = false;

boolean showFramerate = false;

boolean paused = false;

ArrayList<Wall> walls = new ArrayList<Wall>();
ArrayList<Rock> rocks = new ArrayList<Rock>();
ArrayList<Enemy>enemies = new ArrayList<Enemy>();
ArrayList<Treasure>treasures = new ArrayList<Treasure>();
ArrayList<Ladder>ladders = new ArrayList<Ladder>();
ArrayList<Enemy>happyRobbers = new ArrayList<Enemy>();


  HashMap<String,PImage> bodyimages_normal;
  HashMap<String,PImage> turnimages_normal;
    HashMap<String,PImage> bodyimages_gold;
  HashMap<String,PImage> turnimages_gold;
    HashMap<String,PImage> images_normal;
    HashMap<String,PImage> images_gold;

PImage bgArt;
PImage levelArt[] = new PImage[5];
PImage numberArt[] = new PImage[10];
PImage creditsArt;
PImage titleBeforeArt;
PImage titleAfterArt;
PImage nyet;



void setup() {
  size(800, 800);
  frameRate(30);
  // Get the background music to play.
  minim = new Minim(this);

  loadBgMusicFiles();
  loadFXfiles();

  bgArt = loadImage("background.jpg");
  
  timer = new Timer();
  
  creditsArt = loadImage("screens/credit-screen.png");
  titleBeforeArt = loadImage("screens/splash-screen.png");
  titleAfterArt = loadImage("screens/lose-screen.png");
  //titleArt = loadImage("SnakeBits-Logo.jpg");
    nyet =  loadImage("nyet.png");


    bodyimages_normal = load4DirImages("snake/body",true);
    turnimages_normal = load8DirImages("snake/turn",true);
    bodyimages_gold = load4DirImages("goldsnake/body",true);
    turnimages_gold = load8DirImages("goldsnake/turn",true);

       images_normal =  load4DirImages("snake/head",true);
       images_gold =  load4DirImages("goldsnake/head",true);


  for(int i = 1; i <= 5; i++){
    levelArt[i-1] = loadImage("screens/Splash-"+i+".png");
  }
  for(int i = 0; i < 10; i++){
    numberArt[i] = loadImage("Numbers/"+i+".png");
  }
}

//this is called when they hit space, start the game if the mode make sense to
void tryToAdvanceGame() {
  // boolean newStart = false;

  snake = new Snake(10, 10);
   directionKey = RIGHT;
  if(gameMode == GM_BEFORE && gameLevel == -1){
     gameLevel++; 
     playStartScreen();
  } 
  else if (gameMode == GM_BEFORE) {
    gameMode = GM_WAITING;
    preLevel();
  }  else if (gameMode == GM_WAITING){
     nextLevel(); 
  } else if(gameMode == GM_AFTER){
     gameMode = GM_BEFORE; 
     pauseGameOver();
     playStartScreen();
  }
}

void preLevel(){
   gameMode = GM_WAITING;
   gameLevel++;
   seedLevel(gameLevel);   
  // println("DO "+gameLevel);
}

void nextLevel(){
    gameMode = GM_DURING;
    timer.setLevel(gameLevel);
      addEnemy();
}

void tryToEndGame() {
  //println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  if (gameMode == GM_DURING ) {
    pauseBg();
    playGameOver();
    gameMode = GM_AFTER;
    gameLevel = 0; //Reset the level to start over.
    ditchEverything();   
    //this is a cheat, but i want to avoid a potential null snake error
    snake = new Snake(25, 25);
  }
}

void seedLevel(int whatLevel){
   addWalls();
   
   enemies = new ArrayList<Enemy>();
   if(whatLevel != 1){
      treasures = new ArrayList<Treasure>();
      for(int i = 0; i < 5; i++){
        treasures.add(new Treasure(  round(random(GRIDSIZE/4, GRIDSIZE*3/4)), round(random(GRIDSIZE/4, GRIDSIZE*3/4))));
    };
   }

  addLadders();
  
  // Stop title music, start bg music.
  pauseStartScreen();
  pauseGameOver();
  playBg();
}

// Clear everything after the snake dies but before loading next level.
void ditchEverything() {
   enemies = new ArrayList<Enemy>();
   treasures = new ArrayList<Treasure>();
   timer = new Timer();
   walls = new ArrayList<Wall>();
   rocks = new ArrayList<Rock>();
   addLadders();
}

boolean debug = false;
int howOften = REGULARSPEED;

void draw() {
  image(bgArt, 0, 0);
  for(Rock r:rocks){
  r.draw();
  }

if(showFramerate)  text(round(frameRate),760,20);

  for(Ladder l : ladders){
     l.draw(); 
  }

  if (gameMode == GM_DURING) {
    if (frameCount % howOften == 0) { //was 5
      if(!loopTime) snake.setDir(directionKey);
      else {
        snake.setDirFollowTail();
        howOften = FASTSPEED;
        ditchEverything();
        loopTimeCnt ++;
        if (loopTimeCnt == 60) {
          // reset the level, reset the goldloop counter, advance to next level.
          // reset the snake speed to be normal 
          snake = new Snake(10,10);
          //fxPassLevel();
          preLevel();
          loopTimeCnt = 0;
          loopTime = false;
          howOften = REGULARSPEED;
        }
      }
      if(!paused)snake.move();
    }
    
    maybeAddEnemy();
  }

  if (gameMode == GM_DURING || gameMode == GM_WAITING || gameMode == GM_AFTER) {

    timer.draw();
    
    happyRobbers = new ArrayList<Enemy>();   
    for(Enemy e : enemies){
       if(!paused)e.tryToMove();
       e.draw(); 
    }
    for(Enemy e : happyRobbers){
     treasures.remove(e.wantedTreasure);
      enemies.remove(e);     
    }

    for(Treasure t : treasures){
       t.draw(); 
    }
    snake.draw(); //drawing snakes las is nice

  }
  
    if(timer.goodToGo && !loopTime ) image(nyet,110,600);


  if (gameMode == GM_AFTER) {
    image(titleAfterArt, 215, 274);
  }

  if (debug) {
    for (int x = 0; x < GRIDSIZE; x++) {
      for (int y = 0; y < GRIDSIZE; y++) {
         noFill();stroke(200);rect(x*SQUARESIZE,y*SQUARESIZE,SQUARESIZE,SQUARESIZE);
      }
    }
  }
  
  if(gameMode == GM_BEFORE){

    if(gameLevel == -1){
          background(255);
        image(creditsArt,215,200);
     } 
    if(gameLevel == 0){
        image(titleBeforeArt,215,200);
    } 
  }

  if(gameMode == GM_WAITING){
       int levelToShow = gameLevel-1;
        
       if(levelToShow > 4) {
           levelToShow = 4;
       } 
        image(levelArt[levelToShow],200,200);
        
        if(gameLevel >= 5){
           //gameLevel = 15;
           String display = ""+gameLevel;
           
           float startPos = ((800-(display.length()*94))/2  );//-15;
           
           for(int d = 0; d < display.length(); d++){
              char c = display.charAt(d);
              int offset = c-'0';
              PImage img  = numberArt[offset];
              image(img,startPos,380);
              startPos += 85;
           }
        }   
  }
}

String dirToGfxDir(int dir) {
  switch(dir) {
  case UP:
    return "up";
  case DOWN:
    return "down";
  case LEFT:
    return "left";
  case RIGHT:
    return "right";
  } 
  return "";
}

xy dirToXY(int dir) {
  xy p = new xy(0,0);
  switch(dir) {
  case UP:
    p.y--;
   break;
  case DOWN:
    p.y++;
   break;
  case LEFT:
p.x--;
   break;
  case RIGHT:
p.x++;
   break;
  } 
  return p;
}


void addWalls(){
  walls = new ArrayList<Wall>(); 
  for(int x = 0; x < GRIDSIZE; x++) {
      walls.add(new Wall(x,0)); 
      walls.add(new Wall(x,1)); 
      walls.add(new Wall(x,GRIDSIZE-1)); 
      walls.add(new Wall(x,GRIDSIZE-2)); 
   }
  for(int y = 2; y < GRIDSIZE-2; y++) {
      walls.add(new Wall(0,y)); 
      walls.add(new Wall(GRIDSIZE-1,y)); 
      walls.add(new Wall(1,y)); 
      walls.add(new Wall(GRIDSIZE-2,y)); 
   }

  walls.add(new Wall(32, 2));
  walls.add(new Wall(32, 3));
  walls.add(new Wall(33, 4));
  walls.add(new Wall(34, 5));
  walls.add(new Wall(35, 6));
  walls.add(new Wall(36, 7));
  walls.add(new Wall(37, 7));
  
  addRocks();
}

void addRocks() {
  if ( gameLevel > 2) {
    rocks = new ArrayList<Rock>();
    int upperR, upperL, lowerR, lowerL;
    upperL = int(random(5,19));
    lowerL = int(random(5,19));
    lowerR = int(random(24,36));
    
    rocks.add(new Rock(upperL, 5)); 
    rocks.add(new Rock(upperL, 6)); 
    rocks.add(new Rock(upperL, 7)); 
    rocks.add(new Rock(upperL, 8)); 

    rocks.add(new Rock(lowerL, 33)); 
    rocks.add(new Rock(lowerL+1, 33)); 
    rocks.add(new Rock(lowerL+2, 33));
    
    rocks.add(new Rock(lowerR, 31)); 
    rocks.add(new Rock(lowerR+1, 31)); 
    rocks.add(new Rock(lowerR+2, 31));
    
    if (gameLevel > 10){
      rocks.add(new Rock(upperL+1, 8));
      rocks.add(new Rock(lowerR+3, 31));
      rocks.add(new Rock(lowerL+3, 33)); 
    }
    
    if (gameLevel > 15) {
      upperR = int(random(7,22));
      rocks.add(new Rock(24, upperR)); 
      rocks.add(new Rock(25, upperR)); 
      rocks.add(new Rock(26, upperR)); 
    }
  }
}  

class xy{
  int x,y;
  xy(int px, int py){
    x = px;
    y = py; 
  }
}  

void stop(){
  // always close Minim audio classes when you are done with them :-)
  shutdownFX();
  shutdownBgMusic();
  minim.stop();

  super.stop();
}

void maybeAddEnemy(){
  
  int oneChanceInX = 50;
  if(gameLevel == 1) oneChanceInX = 25;   
  else {
    oneChanceInX -= gameLevel * 4;
  }
  if(oneChanceInX < 10) oneChanceInX = 10;
  
   if(!paused && random(oneChanceInX) < 1){
      addEnemy();
   } 
}

void addEnemy(){
  if(enemies.size() > 30) return; // toomany
  //pick a random latter
  if(ladders.size() == 0) return;
  int r = int(random(ladders.size()));
  Ladder home = ladders.get(r);
  enemies.add(new Enemy(home.x,home.y,home,gameLevel));

}