text: //
  // Created by Wouter Groeneveld on 09/08/18.
  //
  
  #include <libgba-sprite-engine/sprites/sprite_builder.h>
  #include <libgba-sprite-engine/gba_engine.h>
  #include <algorithm>
  #include <libgba-sprite-engine/background/text_stream.h>
  #include <sstream>
  #include "food_scene.h"
  #include "avatar.h"
  #include "bullet.h"
  #include "bullet_data.h"
  
  #define AVATAR_ROTATION_DIFF (128 * 6)
  #define MAX_AMOUNT_OF_BULLETS 40
  #define BULLET_COOLDOWN_START 20
  
  FoodScene::FoodScene(const std::shared_ptr<GBAEngine> &engine) : Scene(engine) {}
  
  std::vector<Background *> FoodScene::backgrounds() {
      return {};
  }
  
  std::vector<Sprite *> FoodScene::sprites() {
      std::vector<Sprite*> sprites;
      // TODO order is important for affine transformations because of affinity - those always last!
      // some kind of affine bug depending on the order of sprites added in OAM
      for(auto& b : bullets) {
          sprites.push_back(b->getSprite());
      }
      sprites.push_back(someBulletSprite.get());
      sprites.push_back(avatar.get());
  
      return sprites;
  }
  
  void FoodScene::removeBulletsOffScreen() {
      // TODO this is VERY ineffective, but it's a nice example of how C++11 remove_if works.
      bullets.erase(
              std::remove_if(bullets.begin(), bullets.end(), [](std::unique_ptr<Bullet> &s) { return s->isOffScreen(); }),
              bullets.end());
  }
  
  VECTOR FoodScene::rotateAround(VECTOR center, VECTOR point) {
      return GBAVector(center).rotateAsCenter(point, avatarRotation);
  }
  
  void FoodScene::tick(u16 keys) {
      if(engine->getTimer()->getTotalMsecs() < 5000) {
          counter++;
      } else {
          engine->getTimer()->stop();
      }
  
      TextStream::instance().setText(std::to_string(counter) + std::string(" frames/5sec"), 5, 1);
      TextStream::instance().setText(std::string(engine->getTimer()->to_string()), 6, 1);
  
  
      avatar->animateToFrame(0);
      bool allowedToShoot = false;
      int oldBulletSize = bullets.size();
  
      if(bulletCooldown > 0) {
          bulletCooldown--;
      } else if(bulletCooldown == 0) {
          allowedToShoot = true;
      }
  
      removeBulletsOffScreen();
  
      if(keys & KEY_LEFT) {
          avatarRotation -= AVATAR_ROTATION_DIFF;
      } else if(keys & KEY_RIGHT) {
          avatarRotation += AVATAR_ROTATION_DIFF;
      }
      if((keys & KEY_A)) {
          avatar->animateToFrame(1);
  
          if(allowedToShoot && bullets.size() < MAX_AMOUNT_OF_BULLETS) {
              bulletCooldown = BULLET_COOLDOWN_START;
              bullets.push_back(createBullet());
  
              auto &b = bullets.at(bullets.size() - 1);
              auto destination = rotateAround(avatar->getCenter(), defaultBulletTarget);
              TextStream::instance().setText(std::string("shooting dest: ") + std::to_string(destination.x) + std::string(",") + std::to_string(destination.y), 16, 1);
              b->setDestination(destination);
          }
      }
  
      avatar->rotate(avatarRotation);
  
      if(oldBulletSize != bullets.size()) {
          engine.get()->updateSpritesInScene();
      }
  
      for(auto &b : bullets) {
          b->tick();
      }
   }
  
   std::unique_ptr<Bullet> FoodScene::createBullet() {
      return std::unique_ptr<Bullet>(new Bullet(spriteBuilder
              ->withLocation(avatar->getX() + avatar->getWidth() / 2, avatar->getY() + avatar->getHeight() / 2)
              .buildWithDataOf(*someBulletSprite.get())));
  }
  
  void FoodScene::load() {
      foregroundPalette = std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager(avatar_palette, sizeof(avatar_palette)));
      backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager());
  
      spriteBuilder = std::unique_ptr<SpriteBuilder<Sprite>>(new SpriteBuilder<Sprite>);
      SpriteBuilder<AffineSprite> affineBuilder;
  
      // Doom avatar copyright iD Software
      avatar = affineBuilder.withData(avatar_data, sizeof(avatar_data))
              .withSize(SIZE_64_64)
              .withAnimated(2, 3)
              .withLocation(GBA_SCREEN_WIDTH / 2 - 32, GBA_SCREEN_HEIGHT / 2 - 32)
              .buildPtr();
      avatar->stopAnimating();
      avatarRotation = 0;
  
      someBulletSprite = spriteBuilder->withData(bullet_data, sizeof(bullet_data))
              .withSize(SIZE_16_16)
              .withLocation(GBA_SCREEN_WIDTH + 20, GBA_SCREEN_HEIGHT + 20)
              .buildPtr();
      bulletCooldown = BULLET_COOLDOWN_START;
  
      // rotation of a point on a circle within the resolution means our radius should be big enough
      defaultBulletTarget = { GBA_SCREEN_WIDTH / 2, GBA_SCREEN_HEIGHT + (GBA_SCREEN_WIDTH / 2) - avatar->getCenter().y + 40};
  
      engine->getTimer()->start();
  }