import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import java.util.List; 
import java.util.List; 

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 LANTycoon extends PApplet {

Game game;
int tycoonState;
String superLongHowToString = "Welcome to LANTycoon\nThis is a very simple "
+ "tycoon game made for The Gathering 2013 NGA Fast Game Development compo. The theme for this years compo was \"Time is money\".\n"
+ "The game is developet by Emil Lie Hatlelid and Kyrre Havik Eriksen from University of Oslo (visit our UIO:Campus TG stand!)\n"
+ "In the following section we will try to explain how the game is played.\n\n"
+ "- The goal is to earn as much money as possible untill the end of 2013! This is done by making succesefull LAN partys.\n"
+ "- Of course, if you run out of money you will lose, by the end of that year :(\n"
+ "- You earn money by participants visitng your party, and bonus/multipliers made from having stands. The bigger LAN, the better!\n"
+ "- The thing is, you have only a set amount of time before a LAN party starts. So you need to build quick, "
+ "or buy the long tables with more seats. Since they are quicker to build\n"
+ "- Each LAN hall also cost money to rent (except the first one). So you need to add this to your price calculation." +
"- Each table square have room for two particpants, so don't make it to close or else you miss seats." +
"- There are some exception to this last rule (It's not a bug, it's a feature!)." +
"- You can sell seats or stand by pressing 'd' while hovering over.";

public void setup() {
  size(800, 500);
  background(0);
  game = new Game();
  tycoonState = 0;
}

public void draw() {
  background(0);
  switch(tycoonState) {
  case 0: // intro
    fill(random(255), random(255), random(255));
    textAlign(CENTER);
    textSize(56);
    text("LAN Tycoon", 250, 250);
    fill(random(255), random(255), random(255));
    textSize(21);
    text("Press the any-key", 250, 280);
    break;
  case 1: // howto
    fill(255);
    textAlign(LEFT);
    textSize(14);
    text(superLongHowToString, 10, 10, 790, 490);
    textAlign(RIGHT);
    textSize(21);
    text("Press the any-key again", 495, 495);
    break;
  case 2: // game
    game.draw();
    break;
  case 3: // score
    break;
  }
}

public void mousePressed() {
  switch(tycoonState) {
  case 0: // intro
    break;
  case 1: // howto
    break;
  case 2: // game
    game.mousePressed();
    break;
  case 3: // score
    break;
  }
}

public void mouseReleased() {
  switch(tycoonState) {
  case 0: // intro
    break;
  case 1: // howto
    break;
  case 2: // game
    game.mouseReleased();
    break;
  case 3: // score
    break;
  }
}

public void mouseDragged() {
  switch(tycoonState) {
  case 0: // intro
    break;
  case 1: // howto
    break;
  case 2: // game
    game.mouseDragged();
    break;
  case 3: // score
    break;
  }
}

public void mouseClicked() {
  switch(tycoonState) {
  case 0: // intro
    tycoonState = 1;
    break;
  case 1: // howto
    tycoonState = 2;
    break;
  case 2: // game
    game.mouseClicked();
    break;
  case 3: // score
    break;
  }
}

public void keyPressed() {
  switch(tycoonState) {
  case 0: // intro
    tycoonState = 1;
    break;
  case 1: // howto
    tycoonState = 2;
    break;
  case 2: // game
    break;
  case 3: // score
    break;
  }
}

public void keyReleased() {
  switch(tycoonState) {
  case 0: // intro
    break;
  case 1: // howto
    break;
  case 2: // game
  game.keyReleased();
    break;
  case 3: // score
    break;
  }
}



class Cube {

  int x, y, r, g, b, value, price, maxSeats, seats, tempSeats;
  int w, h;
  char type;
  boolean vertical;

  public Cube(int x, int y, char type) {
    this.x = x;
    this.y = y;
    this.type = type;

    switch (type) {
    case 'T': //L TABLE
      w = 40;
      h = 10;
      r = 134;
      g = 120;
      b = 39;
      price = 800;
      seats = 8;
      break;
    case 't': //M TABLE
      w = 20;
      h = 10;
      r = 134;
      g = 120;
      b = 39;
      price = 300;
      seats = 4;
      break;
    case 'B': //S TABLE
      w = 10;
      h = 10;
      r = 134;
      g = 120;
      b = 39;
      price = 100;
      seats = 2;
      break;
    case 'U': //UiO
      w = 80;
      h = 30;
      r = 255;
      g = 0;
      b = 0;
      price = 2000;
      break;
    case 'K': //KOMPLETT.NO
      w = 100;
      h = 80;
      r = 0;
      g = 128;
      b = 255;
      price = 50000;
      break;
    case 'R': //RNDSTAND
      w = 60;
      h = 20;
      r = 100;
      g = 100;
      b = 100;
      price = 1500;
      break;
    case 'E': //ENTRANCE
      w = 20;
      h = 10;
      r = 0;
      g = 250;
      b = 0;
      break;
    }
  }

  public void draw() {
    fill(r, g, b);
    rect(x, y, w, h);
    if (seats > 0) {
      fill(255);
      textAlign(CENTER);
      textSize(10);
      text(seats - tempSeats, x + (w / 2), y + h - 1);
    }
  }

  public void update() {
    x = (mouseX/10)*10;
    y = (mouseY/10)*10;
  }

  // Return true if avaiable
  public boolean available(Cube c) {
    if (c.x >= (x+w)) return true;
    if (c.y >= (y+h)) return true;
    if ((c.x+c.w) <= x) return true;
    if ((c.y+c.h) <= y) return true;
    return false;
  }

  boolean checked;

  public void clearSeats(List<Cube> cubes) {
    tempSeats = 0;
    for (Cube c : cubes) {
      c.tempSeats = 0;
      if (vertical && c.vertical) {
        for (int j = 0; j < h/10; j++) {
          for (int i = 0; i < c.h / 10; i++) {
            if (x == c.x + w || x + w == c.x) {
              if ((y + (j * 10)) == (c.y + (i * 10))) {
                tempSeats++;
                c.tempSeats++;
              }
            }
          }
        }
      }
      else if (vertical) {
        for (int i = 0; i < c.w / 10; i++) {
          if (x == c.x + (i * 10)) {
            if (y == c.y + c.h || y + h == c.y) {
              c.tempSeats++;
            }
          }
        }

        for (int i = 0; i < h/10; i++) {
          if (c.y == y + (i * 10)) {
            if (x == c.x + c.w || x + w == c.x) {
              tempSeats++;
            }
          }
        }
      } 
      else if (c.vertical) {
        for (int i = 0; i < w / 10; i++) {
          if (c.x == x + (i * 10)) {
            if (c.y == y + h || c.y + c.h == y) {
              tempSeats++;
            }
          }
        }

        for (int i = 0; i < c.h/10; i++) {
          if (y == c.y + (i * 10)) {
            if (c.x == x + w || c.x + c.w == x) {
              c.tempSeats++;
            }
          }
        }
      }
      else {
        for (int j = 0; j < w/10; j++) {
          for (int i = 0; i < c.w / 10; i++) {
            if (y == c.y + h || y + h == c.y) {
              if ((x + (j * 10)) == (c.x + (i * 10))) {
                tempSeats++;
                c.tempSeats++;
              }
            }
          }
        }
      }
    }

    for (int j = 0; j < w/10; j++) {
      if (y + h == LANTycoon.this.game.hmaxh || y == LANTycoon.this.game.hminh) {
        tempSeats++;
      }
    }
  }

  public void saveClearSeats(List<Cube> cubes) {
    tempSeats = 0;
    for (Cube c : cubes) {
      c.tempSeats = 0;
      if (vertical && c.vertical) {
        for (int j = 0; j < h/10; j++) {
          for (int i = 0; i < c.h / 10; i++) {
            if (x == c.x + w || x + w == c.x) {
              if ((y + (j * 10)) == (c.y + (i * 10))) {
                seats--;
                c.seats--;
              }
            }
          }
        }
      } 
      else {
        for (int j = 0; j < w/10; j++) {
          for (int i = 0; i < c.w / 10; i++) {
            if (y == c.y + h || y + h == c.y) {
              if ((x + (j * 10)) == (c.x + (i * 10))) {
                seats--;
                c.seats--;
              }
            }
          }
        }
      }
    }

    for (int j = 0; j < w/10; j++) {
      if (y + h == LANTycoon.this.game.hmaxh || y == LANTycoon.this.game.hminh) {
        seats--;
      }
    }
  }
}



class Game {
  List<Cube> cubes;
  Cube tempCube, ent;
  int gameState;
  int lanHall, hminw, hmaxw, hminh, hmaxh;
  long time, money, year, roundTime, lastMillis;
  boolean hallDrawn, gameCompleted;
  int[] results;

  public Game() {
    gameCompleted = true;
    cubes = new ArrayList<Cube>();

    gameState = 0;
    lanHall = -1;

    // temp
    roundTime = 120000;
    year = 1992;
    money = 1000;
  }

  public void draw() {
    switch (gameState) {
    case 0: // velg hall
      fill(255);
      textAlign(LEFT);
      textSize(21);
      text("Choose your LAN hall:", 30, 30);
      textSize(16);
      text("Money: " + money, 500, 60);
      stroke(255);
      fill(102, 51, 0);
      rect(100, 100, 50, 50);
      text("Menighetshus\n0,-", 160, 117);
      if (money >= 1000) fill(255, 0, 0);
      else fill(100);
      rect(320, 100, 50, 50);
      text("Gymsal\n1 000,-", 380, 117);
      if (money >= 5000) fill(0, 255, 0);
      else fill(100);
      rect(100, 300, 50, 50);
      text("Flerbrukshall\n5 000,-", 160, 317);
      if (money >= 10000) fill(0, 0, 255);
      else fill(100);
      rect(320, 300, 50, 50);
      text("Vikingskipet\n10 000,-", 380, 317);
      noStroke();
      break;
    case 1: // build it
      if (millis() > lastMillis + 1000) {
        time -= 1000;
        lastMillis = millis();
      }
      if (time <= 0) {
        gameState = 2;
        calculateStats();
        return;
      }
      grid();
      menu();
      stats();
      ent.draw();

      for (Cube c : cubes) {
        c.draw();
      }

      if (tempCube != null) {
        tempCube.update();
        tempCube.draw();
      }
      break;
    case 2: // stats
      grid();
      for (Cube c : cubes) {
        c.draw();
      }
      textAlign(LEFT);
      fill(0, 50);
      rect(-10.0f, -10.0f, (float) displayWidth, (float) displayHeight);
      fill(121, 121, 121, 199);
      rect(25, 25, 750, 450);
      fill(250);
      textSize(24);
      text("SCORE", 100, 110);
      line(90, 120, 190, 120);
      pushMatrix();
      textSize(21);
      fill(random(255), random(255), random(255));
      translate(200, 200);
      rotate(radians(PI/0.30f));
      text("TG-" + (year-1) + " FINISHED!", -100, -100);
      popMatrix();

      fill(150);
      textSize(18);
      text("Year:   " + (year-1) + "\nExpenses: " + results[2] + "\nTicketsales: " + results[1] + "\nStand attraction " + (results[0]) + "\n\n\n\nNet Income: " + results[4], 100, 150);
      textSize(22);
      fill(250);
      text("INCOME: " + results[3], 100, 300);

      textSize(21);
      textAlign(CENTER);
      if (year<2014 && money > 99) text("CLICK\nTO CONTINUE\n(or esc to quit)", 500, 250);
      else text("CLICK\nTO QUIT", 500, 250);
      break;
    }
  }

  public void menu() {
    pushMatrix();
    textAlign(LEFT);
    fill(200, 200, 200);
    translate(500, 0);
    textSize(12);
    //TEXT
    fill(0);
    rect(0, -1, 300, 500);
    fill(200);
    stroke(255);

    text("TABLE", 15, 20);
    text("800,-", 5, 40);
    text("300,-", 5, 60);
    text("100,-", 5, 80);

    text("UiO", 20, 130);
    text("2 000,-", 5, 150);

    text("KOMPLETT.NO", 20, 200);
    text("50 000,-", 5, 220);

    text("RND STAND", 20, 315);
    text("1 500,-", 5, 335);

    //RECTS TABLE
    fill(134, 120, 39);
    rect(60, 30, 40, 10);
    rect(60, 50, 20, 10);
    rect(60, 70, 10, 10);

    //RECT UiO
    fill(255, 0, 0);
    rect(60, 140, 100, 30);

    //RECT KOMPLETT
    fill(0, 128, 255);
    rect(60, 210, 100, 80);

    //RECT RNDSTAND
    fill(100, 100, 100);
    rect(60, 325, 60, 20);

    popMatrix();
  }

  public void grid() {
    if (!hallDrawn) {
      switch(lanHall) {
      case 0: // for small map
        hminw = 200;
        hmaxw = 300;
        hminh = 200;
        hmaxh = 250;
        ent = new Cube(hminw, hmaxh, 'E'); //Entrance
        break;
      case 1: // for medium map
        hminw = 150;
        hmaxw = 350;
        hminh = 200;
        hmaxh = 300;
        ent = new Cube(hminw, hmaxh, 'E'); //Entrance
        break;
      case 2: // for big map
        hminw = 50;
        hmaxw = 450;
        hminh = 200;
        hmaxh = 400;
        ent = new Cube(hminw, hmaxh, 'E'); //Entrance
        break;
      case 3: // for vikingskipet map
        hminw = 50;
        hmaxw = 450;
        hminh = 50;
        hmaxh = 450;
        ent = new Cube(hminw, hmaxh, 'E'); //Entrance
        break;
      }
      hallDrawn = true;
    }

    stroke(64);
    for (int x = hminw; x < hmaxw; x += 10) {
      line(x, hminh, x, hmaxh);
      for (int y = hminh; y < hmaxh; y += 10) {
        line(hminw, y, hmaxw, y);
      }
    }
    line(hmaxw, hminh, hmaxw, hmaxh);
    line(hminw, hmaxh, hmaxw, hmaxh);
    stroke(255);
  }

  public void stats() {
    int x = 520;
    fill(51, 51, 51);
    rect(x+170, 470, 109, 29);
    fill(255);
    line(500, 350, 800, 350);

    textAlign(LEFT);
    textSize(18);
    text("TIME: " + time/1000 + " seconds", x, 370);
    text("MONEY: " + money, x, 400);
    text("YEAR: " + year, x, 430);
    text("IM READY!", x+ 180, 491);
  }

  public void calculateStats() {
    time = roundTime;
    year++;
    results = calculateProfits();
    money += results[3];
  }

  public void mousePressed() {
    switch (gameState) {
    case 0: // velg hall
      break;
    case 1: // build it
      if (mouseX > 500 && mouseY < 344) { // clicked inside menu/panel area
        // the following if's are for the different cubes
        if (mouseY > 324 && mouseY < 324 + 20) tempCube = new Cube(mouseX, mouseY, 'R');
        else if (mouseY > 209 && mouseY < 209 + 80) tempCube = new Cube(mouseX, mouseY, 'K');
        else if (mouseY > 139 && mouseY < 139 + 30) tempCube = new Cube(mouseX, mouseY, 'U');
        else if (mouseY > 69 && mouseY < 69 + 10) tempCube = new Cube(mouseX, mouseY, 'B');
        else if (mouseY > 49 && mouseY < 49 + 10) tempCube = new Cube(mouseX, mouseY, 't');
        else if (mouseY > 29 && mouseY < 29 + 10) tempCube = new Cube(mouseX, mouseY, 'T');
      } 
      else if (mouseX > 500 && mouseY > 344) { // states goes here
      }
      break;
    case 2: // stats
      break;
    }
  }

  public void mouseReleased() {
    switch (gameState) {
    case 0: // velg hall
      break;
    case 1: // build it
      if (mouseButton == RIGHT && tempCube != null) {
        int tempValue = tempCube.w;
        tempCube.w = tempCube.h;
        tempCube.h = tempValue;
        tempCube.vertical = !tempCube.vertical;
      }
      else if (mouseButton == LEFT) {
        if (tempCube != null) {
          if (mouseX > hminw && mouseY > hminh && mouseX <= 10 + hmaxw-tempCube.w && mouseY <= 10 + hmaxh-tempCube.h) {
            for (Cube c : cubes) { // Look for busy area
              if (!c.available(tempCube)) {
                tempCube = null;
                return;
              }
            }
            if (money < tempCube.price) {
              // TODO inform about to expensive
              tempCube = null;
              return;
            }
            tempCube.saveClearSeats(cubes);
            money -= tempCube.price;
            cubes.add(tempCube);
            tempCube = null;
          } 
          else {
            tempCube = null;
          }
        }
      }
      break;
    case 2: // stats
      break;
    }
  }

  public void mouseClicked() {
    switch (gameState) {
    case 0: // velg hall
      if (mouseX > 100 && mouseX < 150 && mouseY > 100 && mouseY < 150) { // small hall
        lanHall = 0;
      } 
      else if (mouseX > 320 && mouseX < 370 && mouseY > 100 && mouseY < 150 && money >= 1000) { // medium
        money -=1000;
        lanHall = 1;
      } 
      else if (mouseX > 100 && mouseX < 150 && mouseY > 300 && mouseY < 350 && money >= 5000) { // large
        money -=5000;
        lanHall = 2;
      } 
      else if (mouseX > 320 && mouseX < 370 && mouseY > 300 && mouseY < 350 && money >= 10000) { // vikingskipet
        money -=10000;
        lanHall = 3;
      }
      else {
        return;
      }
      // The game starts now!
      time = roundTime;
      lastMillis = millis();
      gameState = 1;
      break;
    case 1: // build it
      if (mouseX > 689 && mouseY > 469) {
        this.gameState = 2;
        calculateStats();
      }
      break;
    case 2: // stats
      // time = millis()+120000;
      cubes = new ArrayList<Cube>();
      hallDrawn = false;
      if (year>2013 || money < 99) {
        gameState = 0;
        lanHall = -1;

        roundTime = 120000;
        year = 1992;
        money = 1000;
        LANTycoon.this.tycoonState = 0;
      }
      else 
        gameState = 0;
      break;
    }
  }

  public void mouseDragged() {
    switch (gameState) {
    case 0: // velg hall
      break;
    case 1: // build it
      if (tempCube != null) {
        tempCube.clearSeats(cubes);
      }
      break;
    case 2: // stats
      break;
    }
  }

  public int[] calculateProfits() {
    int expenses = 0;
    int income = 0;
    float multiplier = 1.0f;
    int ticketPrice = 100+(lanHall*100);
    for (Cube c : cubes) {
      switch (c.type) {
      case 'T': //L TABLE
        expenses += c.price;
        income += ticketPrice*c.seats;
        break;
      case 't': //M TABLE
        expenses += c.price;
        income += ticketPrice*c.seats;
        break;
      case 'B': //S TABLE
        expenses += c.price;
        income += ticketPrice*c.seats;
        break;
      case 'U': //UiO
        expenses += c.price;
        multiplier += 0.15f;
        break;
      case 'K': //KOMPLETT.NO
        expenses += c.price;
        multiplier += 0.75f;
        break;
      case 'R': //RNDSTAND
        expenses += c.price;
        multiplier += 0.1f;
        break;
      case 'E': //ENTRANCE
        break;
      }
    }
    //Remove previous variables and use this single int array after dev is finished.
    int [] result = {
      round(multiplier*100), income, expenses, round(income*multiplier), (round(income*multiplier) - expenses)
      };
      return result;
  }

  public void keyReleased() {
    switch (gameState) {
    case 0: // choose hall
      break;
    case 1: // build it
      if (key == 'd' && tempCube == null) {
        if (mouseX > hminw && mouseX < hmaxw && mouseY > hminh && mouseY < hmaxh) {
          Cube tmp = null;
          for (Cube c : cubes) {
            if (mouseX > c.x && mouseX < c.x + c.w && mouseY > c.y && mouseY < c.y + c.h) {
              tmp = c;
              break;
            }
          }
          if (tmp != null) {
            cubes.remove(tmp);
            money += tmp.price;// / 2;
          }
        }
      }
      break;
    case 2: // stats
      break;
    }
  }
}

  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "--full-screen", "--bgcolor=#666666", "--stop-color=#cccccc", "LANTycoon" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
