import {
  BALL_INITIAL_ANGLE,
  BALL_RADIUS,
  BALL_SPEED,
  BALL_SPEED_LEVEL_STEP,
  BALL_SPEED_TIME_STEP,
  BRICK_HEIGHT,
  BRICK_WIDTH,
  CONTAINER_HEIGHT,
  CONTAINER_WIDTH,
  HEADER_HEIGHT,
  PADDLE_HEIGHT,
  PADDLE_WIDTH,
  PERK_BALL_SPEED_DECREASE,
} from './config';
import { Ball } from './entities/ball/ball';
import { layBricks } from './entities/brick/layBricks/layBricks';
import { Paddle } from './entities/paddle/paddle';
import { tick } from './lib/tick/tick';

/**
 * Класс игры c информацией о всех игровых сущностях
 */
export class Game {
  /**
   * @param {number[][][]} levels список уровней, каждый элемент которого - карта расположения блоков
   */
  constructor(levels) {
    this.livesAmount = 3;
    this.status = 'in_process';
    this.levels = levels;
    this.currentLevel = 0;
    this.maxLevel = levels.length - 1;

    this.paddle = new Paddle(0, CONTAINER_HEIGHT - PADDLE_HEIGHT, PADDLE_WIDTH, PADDLE_HEIGHT);
    this.balls = [new Ball(0, 0, BALL_RADIUS, BALL_SPEED, BALL_INITIAL_ANGLE)];
    this.bricks = layBricks(this.levels[this.currentLevel], BRICK_WIDTH, BRICK_HEIGHT, HEADER_HEIGHT);
    this.perks = [];

    this._placeBallOnPaddle();
  }

  /**
   * Основной (первый) мяч. Пока играет один мяч - это он.
   * @returns {Ball}
   */
  get ball() {
    return this.balls[0];
  }

  /**
   * Изменяет значения сущностей в зависимости от времени
   * @param {*} deltaTime изменение времени из Ticker
   */
  update(deltaTime) {
    if (this.status !== 'in_process') {
      return;
    }

    // Пока мяч не запущен - держим его на ракетке и не считаем физику
    if (this.ball.status === 'idle') {
      this._placeBallOnPaddle();
      return;
    }

    tick(this, CONTAINER_WIDTH, CONTAINER_HEIGHT, deltaTime);

    // Убираем улетевшие мячи. Жизнь теряется только когда не осталось ни одного
    const survivingBalls = this.balls.filter((ball) => ball.status !== 'out');

    if (survivingBalls.length === 0) {
      this.livesAmount -= 1;

      if (this.livesAmount === 0) {
        this.status = 'over';
      } else {
        this._placeBallOnPaddle();
      }

      return;
    }

    this.balls = survivingBalls;

    for (const ball of this.balls) {
      ball.increaseSpeed(BALL_SPEED_TIME_STEP * deltaTime);
    }

    const isLevelComplete = this._checkLevelCompletion();

    if (isLevelComplete) {
      this.currentLevel += 1;

      if (this.currentLevel <= this.maxLevel) {
        this._proceedToNextLevel();
      } else {
        this.status = 'completed';
      }
    }
  }

  /**
   * Проверяет завершен ли текущий уровень
   * @returns {boolean}
   */
  _checkLevelCompletion() {
    return this.bricks.every((brick) => brick.type === 3 || !brick.alive);
  }

  /**
   * Запускает переход на новый уровень, возвращает мяч в дефоотное положение и отрисовывает кирпичи по карте уровня.
   */
  _proceedToNextLevel() {
    this.ball.increaseSpeed(BALL_SPEED_LEVEL_STEP);
    this._placeBallOnPaddle();
    this.bricks = layBricks(this.levels[this.currentLevel], BRICK_WIDTH, BRICK_HEIGHT, HEADER_HEIGHT);
  }

  /**
   * Ставит мяч по центру ракетки, оставляя один мяч в игре
   */
  _placeBallOnPaddle() {
    this.balls = [this.ball];
    this.ball.reset(this.paddle.x + this.paddle.width / 2, this.paddle.y - this.ball.radius);
  }
}
