/**
 * nInvaders - a space invaders clone for ncurses
 * Copyright (C) 2002-2003 Dettus
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * homepage: http://ninvaders.sourceforge.net
 * mailto: ninvaders-devel@lists.sourceforge.net
 *
 */

#include "aliens.h"
#include "nInvaders.h"
#include "player.h"

Aliens aliens;

int shipnum;

// todo: move to structure
int lowest_ship[ALIENS_MAX_NUMBER_X];
int alienshotx[ALIENS_MAX_MISSILES];
int alienshoty[ALIENS_MAX_MISSILES];
int alienshotnum;
int alienBlock[ALIENS_MAX_NUMBER_Y][ALIENS_MAX_NUMBER_X];
int bunker[BUNKERHEIGHT][BUNKERWIDTH + 1];

/**
 * initialize aliens attributes
 */
void aliensReset() {
  int i, j;

  // three different types of aliens [5], [10]
  int level[ALIENS_MAX_NUMBER_Y][ALIENS_MAX_NUMBER_X] = {
      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
      {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
      {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
      {3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
      {3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};

  aliensClear(aliens.posX, aliens.posY, aliens.right,
              aliens.bottom); // clear old position of aliens

  // reset alien position
  aliens.posX = 0;
  aliens.posY = 0;
  aliens.right = 0;
  aliens.bottom = 0;
  aliens.left = 0;
  aliens.speed = 1;

  // copy level-array to enemy-array
  for (i = 0; i < ALIENS_MAX_NUMBER_X; i++) {
    for (j = 0; j < ALIENS_MAX_NUMBER_Y; j++) {
      alienBlock[j][i] = level[j][i];
    }
  }

  // reset missiles
  for (i = 0; i < ALIENS_MAX_MISSILES; i++) {
    if (alienshotx[i] != 0) {
      aliensMissileClear(alienshotx[i], alienshoty[i]); // clear old position
    }
    alienshotx[i] = 0; // start with zero values
    alienshoty[i] = 0; // start with zero values
  }
  alienshotnum = 1;  // one missile at the same time
  alienshotx[0] = 5; // x position of first alienshot
  alienshoty[0] = 1; // y position of first alienshot
}

/**
 * initialize bunkers attributes
 */
void bunkersReset() {
  int a, b;

  // set position of bunker sprites. user graphical char bunkerd for better visual overview
  // and set according to this the bunker-array
  char bunkerd[BUNKERHEIGHT][BUNKERWIDTH+1] = {
      "        ###                 ###                 ###                 ###         ",
      "       #####               #####               #####               #####        ",
      "      #######             #######             #######             #######       ",
      "      ##   ##             ##   ##             ##   ##             ##   ##       "
  };
  //   12345678901234567890123456789012345678901234567890123456789012345678901234567890
  // 80 characters wide

  // copy graphical "bunkerd" to binary "bunker"
  for (a = 0; a < BUNKERWIDTH; a++) {
    for (b = 0; b < BUNKERHEIGHT; b++) {
      if (bunkerd[b][a] == '#')
        bunker[b][a] = 1;
      else
        bunker[b][a] = 0;
    }
  }

  // display bunkers sprite
  bunkersDisplay(&bunker[0][0]);
}

/**
 * move aliens and test if they've reached the
 * bottom of the windows or the bunkers.
 */
int aliensMove() {

  int cx, cy;
  int fReachedPlayer = 0; // return value

  render();
  aliensClear(aliens.posX, aliens.posY, aliens.right,
              aliens.bottom); // clear old position of aliens

  aliens.posX = aliens.posX + aliens.speed; // move aliens left/ right

  // when aliens reached left or right screen-border
  if (aliens.posX == (BUNKERWIDTH + BUNKERX - 5 - aliens.right) ||
      aliens.posX == (BUNKERX + aliens.left)) {

    aliens.posY++; // move aliens downwards

    // aliens reached player
    if (aliens.posY == SCREENHEIGHT - 2 - aliens.bottom) {
      fReachedPlayer = 1; // set return value
    }

    // aliens reached bunkers //funzt nicht ganz: todo
    if (aliens.posY == BUNKERY - aliens.bottom) {
      // clear bunkers
      for (cx = 0; cx < BUNKERWIDTH; cx++) {
        for (cy = 0; cy < BUNKERHEIGHT; cy++) {
          bunker[cy][cx] = 0;
        }
      }
      bunkersClear(); // clear bunkers sprites
    }

    aliens.speed = aliens.speed * (-1); // change direction of aliens' movements
  }

  aliensDisplay(aliens.posX, aliens.posY, aliens.right,
                aliens.bottom); // display aliens at new position

  return fReachedPlayer; // return if aliens reached player
}

/**
 * display alien animation, display remaining parts of aliens and bunker
 */
void render() {
  int k, row;
  int c = 0;

  // calculate left, right, bottom, lowest_ship
  aliens.left = 1;
  aliens.right = -1;
  aliens.bottom = -1;
  shipnum = 0;
  for (k = 0; k < ALIENS_MAX_NUMBER_X; k++) {
    lowest_ship[k] = -1;
  }

  for (row = 0; row < ALIENS_MAX_NUMBER_Y * 2; row++) {
    if ((row % 2) == 0) {
      for (k = 0; k < ALIENS_MAX_NUMBER_X; k++) {
        if (alienBlock[c][k] != 0) {
          lowest_ship[k] = row;
          shipnum++;
          if (aliens.left == 1 || -k > aliens.left) {
            aliens.left = -k;
          }
          if (aliens.right == -1 || k > aliens.right) {
            aliens.right = k;
          }
          if (aliens.bottom == -1 || c > aliens.bottom) {
            aliens.bottom = c;
          }
        }
      }
    } else {
      c++;
    }
  }
  aliens.bottom = aliens.bottom * 2; // every 2nd row is an empty row
  aliens.left = aliens.left * 3;     // alien sprite is 3 chars wide
  aliens.right = aliens.right * 3;   // alien sprite is 3 chars wide

  // display remaining aliens with animation
  aliensRefresh(level, &alienBlock[0][0]);
}

/**
 * move aliens' missiles and do player/bunker hit testing
 * a zero value in alienshotx indicates that the appropriate missile is loaded,
 * but not fired
 */
int aliensMissileMove() {
  int i, tmp;
  int fPlayerWasHit = 0;
  int shootThreshold;
  static int alienshot_counter = 0;

  // calculate threshold when next missile should be fired
  // it is done here to save calculation time in for-instruction
  shootThreshold = (skill_level * 8) * (shipnum + 2);
  alienshot_counter = alienshot_counter + 10;

  // loop all possible missiles
  for (i = 0; i < ALIENS_MAX_MISSILES; i++) {

    // if the current missile is flying we should do movements
    if (alienshotx[i] != 0) {

      aliensMissileClear(alienshotx[i], alienshoty[i]); // clear old position

      // if missile hit the bunkers
      if (bunkersHitCheck(alienshotx[i], alienshoty[i]) == 1) {
        alienshotx[i] = 0; // value of zero reloads missile
      }

      alienshoty[i]++; // move missile downwards

      // check if player was hit by an alien missile
      if (playerHitCheck(alienshotx[i], alienshoty[i]) == 1) {
        alienshotx[i] = 0; // value of zero reloads missile
        fPlayerWasHit = 1;
      }

    } else { // missile not launched yet

      // start new missile if counter says so
      if (alienshot_counter > shootThreshold &&
          shipnum > 0) {       // only shot if there's an alien left
        alienshot_counter = 0; // reset counter
        tmp = random() % ALIENS_MAX_NUMBER_X; // randomly select one of the ...
        while (lowest_ship[tmp] == -1) {      // ...aliens at the bottom of ...
          tmp = random() % ALIENS_MAX_NUMBER_X; // ...a column to launch missile
        }
        alienshoty[i] =
            aliens.posY + lowest_ship[tmp];    // set y position of missile
        alienshotx[i] = aliens.posX + tmp * 3; // set x position of missile
      }
    } // if

    // display missiles if still running or just launched; could have been
    // modified in the above code
    if (alienshotx[i] != 0) {
      // if missile is out of battlefield
      if (alienshoty[i] == SCREENHEIGHT - 1) {
        alienshotx[i] = 0; // reload missile
      } else {
        aliensMissileDisplay(alienshotx[i],
                             alienshoty[i]); // display Missile at new position
      }
    }

  } // for

  return fPlayerWasHit;
}

/**
 * check if missile hit an alien
 */
int aliensHitCheck(int shotx, int shoty) {
  int alienType = 0;
  int shipx, shipy;
  // if missile is within alien-rectangle
  if (shotx >= aliens.posX &&
      shotx <= aliens.posX + ALIENS_MAX_NUMBER_X * 3 - 1 &&
      shoty >= aliens.posY &&
      shoty <= aliens.posY + (ALIENS_MAX_NUMBER_Y - 1) * 2) {
    // calculate the ship that was hit
    shipx = (shotx - aliens.posX) / 3;
    shipy = (shoty - aliens.posY) / 2;
    // if there is still a ship at this position
    alienType = alienBlock[shipy][shipx];
    if (alienType != 0) {
      alienBlock[shipy][shipx] = 0; // delete alien ship
    }
  }
  return alienType; // returns 0 if no alien was hit, else returns type-code of
                    // alien
}

/**
 * check if missile hit an element of bunker
 */
int bunkersHitCheck(int shotx, int shoty) {
  int adjx, adjy;
  int fBunkerWasHit = 0;
  // if missile is within bunker-rectangle
  if (shotx >= BUNKERX && shotx < BUNKERX + BUNKERWIDTH && shoty >= BUNKERY &&
      shoty < BUNKERY + BUNKERHEIGHT) {
    // calculate the element of the bunker that was hit
    adjy = shoty - BUNKERY;
    adjx = shotx - BUNKERX;
    // if there is still an element
    if (bunker[adjy][adjx] == 1) {
      bunker[adjy][adjx] = 0; // delete element
      fBunkerWasHit = 1;      // bunker was hit!
    }
  }
  return fBunkerWasHit;
}