
class GameWorld {

  java.util.LinkedList<GameObjects> gameObjects;
  IceBerg iceberg1;
  IceBerg iceberg2;
  IcePick icepick;
  float xShift; //Variable für die Verschiebung der kompletten Gameworld
  float speed = 140; //Geschwindigkeit der Bewegung der Spielfläche 
  //inrormationen die zum spawnen der IcePick benötigt werden (ist schon eine da, wann wurde sie gespawnt, zeit bis die nächste gespawnt wird)
  //boolean pickNeeded = false;
  float pickSpawnTime= 0.0;
  float pickRespawnTime;
  float timeElapsed;
  //Timer Zeit (100 Sekunden) Läuft noch Falsch wegen vorausgegangener Zeit, und rest Time die vom Timer ausgegeben wird
  int gesamtTime = 100;
  int restTime; 
  int objectsMaxY;
  int objectsMinY;
  int randColliderMin;
  int randColliderMax;
  // X-Wert des Ziels
  int xZiel;
  // Zeit für das Spawnen von neuen Icebergen und Zeit ab der es keine Iceberge mehr gibt
  float spawnTime;
  float noIceFloeTime;
  //Variablen für die Steuerung: wann werden welche Knöpfe gedrückt
  boolean pressingUP;
  boolean pressingDOWN;
  boolean pressingLEFT;
  boolean pressingRIGHT;
  boolean pressingW;
  boolean pressingA;
  boolean pressingS;
  boolean pressingD;
  boolean goalUpdated = false;
  int minimalSize = 25;
  boolean goal = false;
  Rand zielRand;
  boolean lockKeys = false;
  PImage ch1;
  PImage ch2;




  GameWorld() {
    //Hier wird die Welt erstellt
    gameObjects = new java.util.LinkedList<GameObjects>();
    iceberg1 = new IceBerg(this, width/4, height/2, 1);
    iceberg2 = new IceBerg(this, 3* width/4, height/2, 2);
    gameObjects.add(iceberg1);
    gameObjects.add(iceberg2);
    timeElapsed = 0;
    spawnTime = 0;
    noIceFloeTime = 0;
    speed += schwierigkeit * 20;
    pickRespawnTime = random(5.0, 8.0);




    if (currentCharacter1 ==1) {
      ch1 = character1full;
    } else if (currentCharacter1 ==2) {
      ch1 = character2full;
    } else if (currentCharacter1 ==3) {
      ch1 = character3full;
    } else {
      ch1 = character4full;
    }
    if (currentCharacter2 ==1) {
      ch2 = character1full;
    } else if (currentCharacter2 ==2) {
      ch2 = character2full;
    } else if (currentCharacter2 ==3) {
      ch2 = character3full;
    } else {
      ch2 = character4full;
    }

    int randCount =0;
    Rand randO;
    Rand randU;
    //8 Ränder im einfachen Modus entspricht ca 100 s spielzeit, false heißt kein Endrand, ist für Collider notwendig
    for ( int i = 0; i<(8+schwierigkeit*2); i++) {//(8+schwierigkeit*2)
      float randOID = random(1);
      if (randOID > 0.5) {
        randO = new Rand(this, i*ScreenWidth, 30, randO1, false);
      } else {
        randO = new Rand(this, i*ScreenWidth, 30, randO2, false);
      }
      gameObjects.add(randO);
      float randUID = random(1);
      if (randUID > 0.5) {
        randU = new Rand(this, i*ScreenWidth, ScreenHeight, randU1, false);
      } else {
      randU = new Rand(this, i*ScreenWidth, ScreenHeight, randU2, false);
      }
      gameObjects.add(randU);
      randCount++;
      if (i == 0) {     
        // von der Logik her sollte der Wert eigentlich auf sizeY/2 gesetzt werden, da das Bild aber an den Rändern noch transparent ist und die RandomFunktion
        // selten Werte direkt am Rand spawnt ist die SpielWelt mit sizeY/4 besser gebalanced.
        objectsMinY = (int)(randO.s.y+randO.sizeY/4);
        objectsMaxY = (int)(randU.s.y-randU.sizeY/4);
        // Grenze bis wo sich die Eisberge bewegen dürfen (nicht sizeY/2, wegen Transparenz der Ränder von den Spielfeldrändern).
        randColliderMin = (int)(randO.s.y+randO.sizeY/2.5);
        randColliderMax = (int)(randU.s.y-randU.sizeY/2.5);
      }
    }

    xZiel = (int)gameObjects.get(gameObjects.size()-1).globalX;
    println(xZiel);



    for (int i = 0; i < 80; i++) {
      float random1= random(1);
      if (random1 > 0.5 - (schwierigkeit*0.1)) {
        int randomSizeX = (int)random(300, 500+schwierigkeit*150);
        int randomSizeY = (int)random(300, 500+schwierigkeit*150);
        WarmWater warmWater = new WarmWater(this, ScreenWidth+i*280, int(random(objectsMinY + randomSizeY/2, objectsMaxY-randomSizeY/2)), randomSizeX, randomSizeY);
        gameObjects.add(warmWater);
      }
    }

    for (int i = 0; i < 200; i++) {
      float random1= random(0.1, 1.0);
      if (random1 > 0.6 + schwierigkeit*0.1) {
        // Hier wird randCollider genommen um sicher zu gehen das der IceBerg an diese Position kann
        IceFloe iceFloe = new IceFloe(this, ScreenWidth+i*70, random(randColliderMin, randColliderMax));
        gameObjects.add(iceFloe);
      }
    }

    /*
     
     IceFloe iceFloe = new IceFloe(this, 1000, 350);
     gameObjects.add(iceFloe);
     */
    int abstufung = xZiel/6;
    for (int i = 0; i < 6; i++) {
      float random1= random(1);
      Booster booster = new Booster(this, i*abstufung+random(-600, 600), int(random(randColliderMin, randColliderMax)));
      gameObjects.add(booster);
    }

    int start = xZiel/3;
    int schritteDampfer = xZiel*2/3 / (4 + schwierigkeit*2);
    for (int i = 0; i < (4 + schwierigkeit*2); i++) {
      {
        // hard gecoded die 100 dazu um Abstand einzuhalten zu den Rändern
        Dampfer dampfer = new Dampfer(this, start+schritteDampfer*i /*+ random(-400, 1000)*/, int(random(randColliderMin+ScreenHeight/10, randColliderMax-ScreenHeight/10)));
        gameObjects.add(dampfer);
      }
    }

    zielRand = new Rand(this, xZiel, ScreenHeight/2, ziel, true);
    gameObjects.add(zielRand);

    Rand schild1000m = new Rand(this, (randCount/4*0*1920)+800, ScreenHeight*0.9, schild1000, false);
    gameObjects.add(schild1000m);
    Rand schild750m = new Rand(this, (randCount/4*1*1920)+400, ScreenHeight*0.9, schild750, false);
    gameObjects.add(schild750m);
    Rand schild500m = new Rand(this, (randCount/4*2*1920)+400, ScreenHeight*0.9, schild500, false);
    gameObjects.add(schild500m);
    Rand schild250m = new Rand(this, (randCount/4*3*1920)+400, ScreenHeight*0.9, schild250, false);
    gameObjects.add(schild250m);
  }
  void updateGoal() {

    if (verlierer==1) {
      zielRand.s.x = iceberg2.s.x  +(1920-iceberg2.s.x);
      println(zielRand.s.x);
      println("goal uptodate");
      //Rand neuerZielRand = new Rand(this, iceberg2.s.x-xShift+(1920-iceberg1.s.x), ziel.height/2, ziel, true);
      //println(neuerZielRand.s.x);
      //gameObjects.add(neuerZielRand);
    }
    if (verlierer==2) {
      //Rand neuerZielRand = new Rand(this, iceberg1.s.x-xShift+(1920-iceberg1.s.x), ziel.height/2, ziel, true);//+(1920-iceberg1.s.x)
      // println(neuerZielRand.s.x);
      //gameObjects.add(neuerZielRand);
      zielRand.s.x = iceberg1.s.x  +(1920-iceberg1.s.x);
      println(zielRand.s.x);
      println("goal uptodate");
    }

    goalUpdated=true;
  }

  void animate(float deltaTSeconds) {

    timeElapsed = deltaTSeconds;


    iceberg1.collisionDetection(deltaTSeconds);
    iceberg2.collisionDetection(deltaTSeconds);

    restTime = gesamtTime - ((int)deltaTSeconds);


    // überprüft alle Objekte, wird eine IcePick benötigt, oder ist eine im Spiel?
    // zählt auch die IceFloes und wenn gerade 0 im Bild sind wird an einer bestimmten Stelle eine erzeugt

    int iceFloeCount = 0;
    int icePickCount = 0;

    for (int i = 0; i < gameObjects.size(); i++) {
      if (gameObjects.get(i).getClass().getName() == "IceBergGame$IcePick" || iceberg1.bewaffnet || iceberg2.bewaffnet) {
        icePickCount++;
      }
      if (gameObjects.get(i).getClass().getName() == "IceBergGame$IceFloe" && gameObjects.get(i).globalX > -xShift && gameObjects.get(i).globalX < -xShift + width) {
        iceFloeCount ++;
      }
    }

    // Nacheinander werden über bestimmte Zeiträume Eisschollen gespawnt
    if (iceFloeCount < 4) {
      if (noIceFloeTime == 0) {
        noIceFloeTime = deltaTSeconds;
      }
      spawnTime = deltaTSeconds - noIceFloeTime;
      if (spawnTime > (0.2 + schwierigkeit*1.5)) {
        spawnTime= 0;
        noIceFloeTime = 0;
        IceFloe iceFloe = new IceFloe(this, -xShift+width/2 + int(random(50, 150)), int(random(randColliderMin, randColliderMax)));
        gameObjects.add(iceFloe);
      }
      if (spawnTime > (0.4 + schwierigkeit*1.5)) {
        IceFloe iceFloe = new IceFloe(this, -xShift+width/2 + int(random(60, 400)), int(random(randColliderMin, randColliderMax)));
        gameObjects.add(iceFloe);
      }
      if (spawnTime > (0.6 + schwierigkeit*1.5)) {
        IceFloe iceFloe = new IceFloe(this, -xShift+width/2 + int(random(200, 500)), int(random(randColliderMin, randColliderMax)));
        gameObjects.add(iceFloe);
      }
    }

    //wird eine IcePick benötigt wird sie jetzt gespawnt
    if (icePickCount == 0) {
      if (pickSpawnTime == 0) {
        pickSpawnTime = deltaTSeconds;
      } 
      if (pickRespawnTime < deltaTSeconds-pickSpawnTime) {
        icepick = new IcePick(this, -xShift+random(ScreenWidth/2, ScreenWidth), random(randColliderMin, randColliderMax));
        //println(pickSpawnTime); //for debugging
        gameObjects.add(icepick);
        pickRespawnTime = random(2, 6);
        pickSpawnTime = 0;
      }
    }

    //println(icePickCount);

    //animate aller Objekte im Spiel aufrufen
    for (GameObjects o : gameObjects) {
      o.animate(deltaTSeconds);
    }




    //Ablauf für das Ende des Spiels, wenn die Spielzeit um ist/beide Spieler das Ziel erreichen

    if ((zielRand.s.x+zielRand.sizeX/2) >= ScreenWidth) { //welt stopptihre bewegung wenn das ziel im bild ist
      xShift = - deltaTSeconds*speed;
    }

    if ((zielRand.s.x-xShift)-(iceberg1.s.x -xShift) <= 500 ||(zielRand.s.x-xShift)-(iceberg2.s.x-xShift) <= 500) { 
      //wenn einer der Berge zu geringen Abstand zum Ziel hat, wird die steuerung gelockt und beide Berge schwimmen nach links
      lockKeys = true;
      pressingUP = false;
      pressingDOWN = false;
      pressingLEFT = true;
      pressingRIGHT = false;
      pressingD= false;
      pressingS = false;
      pressingA = true;
      pressingW = false;
      /* Items beim Sieg entfernen?...geschmackssache, ich finds nichso schön aber kann man wieder aktivieren
       for (int i = gameObjects.size()-1; i >=0; i--) {
       if(gameObjects.get(i) instanceof WarmWater || gameObjects.get(i) instanceof IceFloe || gameObjects.get(i) instanceof IcePick ||gameObjects.get(i) instanceof Booster){
       gameObjects.remove(i);
       }
       }
       */
    }
    if (verlierer==1) { //Animation Eisberg2 schwimmt ins ziel sobald der verlierer feststeht
      pressingLEFT = false;
      pressingRIGHT=true;
      if (iceberg2.s.y>ScreenHeight/2+20) {
        pressingUP=true;
        pressingDOWN = false;
      } else if (iceberg2.s.y<ScreenHeight/2-20) {
        pressingUP=false;
        pressingDOWN = true;
      } else {
        pressingUP=false;
        pressingDOWN = false;
      }
      if (iceberg2.s.x >=zielRand.s.x+50) {
        gameMode = new GameEndsMode();
      }
    } else if ( verlierer == 2) { //Animation Eisberg1 schwimmt ins ziel sobald der verlierer feststeht
      pressingA= false;
      pressingD = true;
      if (iceberg1.s.y>ScreenHeight/2+50) {
        pressingS= false;
        pressingW = true;
      } else if (iceberg1.s.y<ScreenHeight/2-20) {
        pressingW= false;
        pressingS = true;
      } else {
        pressingS= false;
        pressingW = false;
      }

      if (iceberg1.s.x >=zielRand.s.x+50) {
        gameMode = new GameEndsMode();
      }
    }
  }

  void draw() {
    //scale(2.0);
    imageMode(CORNER);
    image(background, 0, 0, 1920, 1200);
    imageMode(CENTER);
    //scale(0.5);
    //println(width+" "+height);
    textAlign(CENTER);
    textSize(60);
    fill(255);
    //text("playMode", width/2, height/2); 

    // hier werden alle Elemente gezeichnet die sich von rechts nach linkss bewegen sollen
    pushMatrix();
    translate(xShift, 0);
    fill(0);
    for (int i = 0; i < gameObjects.size(); i++) {

      if (gameObjects.get(i).getClass().getName() != "IceBergGame$IceBerg") {
        //Wertebereich ist hier ein bisschen größer 1920, da die größten Objekte die Ränder sind und diese erst dann ausgeblendet werden sollen wenn sie wirklich
        //aus dem Bild verschwunden sind
        if (gameObjects.get(i).s.x > -1950 && gameObjects.get(i).s.x < width+1950) {  
          gameObjects.get(i).draw();
        }

        //hier werden Objekte gesucht die links aus dem Bildschirm geflossen sind
        if (gameObjects.get(i).s.x < - 1950) {
          //alle Objekte die links aus dem Bildschirm geflossen sind werden entfernt (unnötiger rechenaufwand)
          gameObjects.remove(i);
        }
      }
    }
    popMatrix();


    // um die Koordinaten aufeinander zu matchen muss man das xShift zu den Koordinaten der Iceberge addieren
    iceberg1.draw();
    iceberg2.draw();


    if (zielRand.s.x+300 <= 1920) { //sobald das Ziel weit genug im Bild ist Endet das spiel und der verlierer wird bestimmt
      //println("Zeit abgelaufen");
      lossCondition = 2;

      float size1 = iceberg1.sizeX;
      float size2 = iceberg2.sizeX;
      if (size1 > size2) {
        verlierer = 2;
      } else if (size1 == size2) {
        verlierer = 0;
      } else {
        verlierer = 1;
      }
      //gameMode = new GameEndsMode();
    }

    //Prüft die Eisberge im Spiel auf ihre Größe, schrumft ein Eisberg zu Stark endet das Spiel
    if (lossCondition!=2) {
      for (int i = 0; i < gameObjects.size(); i++) {
        if (gameObjects.get(i).getClass().getName() == "IceBergGame$IceBerg") {
          if (gameObjects.get(i).sizeX <= minimalSize) {
            //println("Zu klein geschrumpft!");
            lossCondition = 1;
            if (i==0) {
              verlierer = 1;
            } else {
              verlierer = 2;
            }
            gameMode = new GameEndsMode();
          }
        }
      }
    }

    //Lebensanzeige Eisberg1
    fill(40, 40, 80);
    rect(70, height-55, iceberg1.health*2, 30);
    fill(90, 90, 200);
    rect(70, height-55, min(iceberg1.actualHealth, 100)*2, 30);
    imageMode(CENTER);
    image(ch1, 35, height-40, 60, 60); 

    //Lebensanzeige Eisberg2
    fill(40, 40, 80);
    rect(width-270, 25, iceberg2.health*2, 30);
    fill(90, 90, 200);
    rect(width-270, 25, min(iceberg2.actualHealth, 100)*2, 30);
    image(ch2, width-35, 40, 65, 60);
    fill(0);
  }

  void keyReleased() {
    if (lockKeys == false) {
      //if (key==CODED) {
      if (keyCode==UP) {
        pressingUP=false;
        //println("up losgelassen");
      }
      if (keyCode==DOWN) {
        pressingDOWN=false;
      }
      if (keyCode==LEFT) {
        pressingLEFT=false;
      }
      if (keyCode==RIGHT) {
        pressingRIGHT=false;
      }
      //}

      switch (key) {
      case 'w' : 
        pressingW=false;
        //println("w losgelassen");

        break;
      case 'a' : 
        pressingA=false;

        break;
      case 's' : 
        pressingS=false;

        break;
      case 'd' : 
        pressingD=false;

        break;
      }
    }
  }

  void keyPressed() {

    //Steuerung der Eisberge
    //zuerst wird geprüft ob es sich um einen key mit keycode handelt (Pfeiltasten werden über keycode angesprochen)
    //Falls ja werden die Pfeiltasten geprüft, falls nicht wasd
    //es wird je die Beschleunigung für den jeweiligen Character geupdated
    if (lockKeys == false) {
      if (key==CODED) {
        if (keyCode==UP) {
          // iceberg2.updateA( 1);
          pressingUP=true;
          //println("up gedrückt");
        }
        if (keyCode==DOWN) {
          //iceberg2.updateA( 3);
          pressingDOWN=true;
        }
        if (keyCode==LEFT) {
          //iceberg2.updateA( 2);
          pressingLEFT=true;
        }
        if (keyCode==RIGHT) {
          //iceberg2.updateA( 4);
          pressingRIGHT=true;
        }
      }

      switch (key) {
      case 'w' : 
        //iceberg1.updateA( 1);
        pressingW=true;
        //println("w gedrückt"); 

        break;
      case 'a' : 
        //iceberg1.updateA( 2);
        pressingA=true;

        break;
      case 's' : 
        //iceberg1.updateA( 3);
        pressingS=true;

        break;
      case 'd' : 
        //iceberg1.updateA( 4);
        pressingD=true;

        break;
      case 'g' : 


        break;
      }
    }
  }


  void createIceFlow(float startX, float endX, float startY, float endY) {
    IceFloe iceFloe = new IceFloe(this, startX, startY, endX, endY, true);
    gameObjects.add(iceFloe);
  }

  //s.x - gameWorld.xShift, s.x - gameWorld.xShift + random(-100, 100), s.y, s.y + random(-100, 100)
  void createRandomIceFlow(float globalX, float localX, float y) {

    float startX = globalX;
    float endX;
    float startY = y;
    float endY;

    if (localX < 120) {
      endX = startX + random(50, 90);
    } else if (localX > ScreenWidth-120) {
      endX = startX - random(50, 90);
    } else {
      if (int(random(0, 1)) == 0) {
        endX = startX + random(50, 90);
      } else {
        endX = startX - random(50, 90);
      }
    }

    if (y < 120) {
      endY = startY + random(50, 90);
    } else if (y > ScreenHeight-120) {
      endY = startY - random(50, 90);
    } else {
      if (int(random(0, 1)) == 0) {
        endY = startY + random(50, 90);
      } else
      {
        endY = startY - random(50, 90);
      }
    }

    IceFloe iceFloe = new IceFloe(this, startX, startY, endX, endY, true);
    gameObjects.add(iceFloe);
  }
}
