import {
  Application,
  Container,
  Graphics,
  Rectangle,
  Sprite,
  Text,
  Texture,
} from "pixi.js";
import { CompositeTilemap, Tilemap } from "@pixi/tilemap";

async function setup() {
  const app = new Application();
  await app.init({ background: "#111111", resizeTo: window, antialias: true });
  document.getElementById("pixi-container")!.appendChild(app.canvas);
  return app;
}

function makeBlockTextures(app: Application): [Texture, Texture, Texture] {
  const red_box = new Graphics();
  red_box.rect(0, 0, 50, 50);
  red_box.fill(0xff0000);

  const green_box = new Graphics();
  green_box.rect(0, 0, 50, 50);
  green_box.fill(0x00ff00);

  const blue_box = new Graphics();
  blue_box.rect(0, 0, 50, 50);
  blue_box.fill(0x0000ff);

  return [
    app.renderer.generateTexture(red_box),
    app.renderer.generateTexture(green_box),
    app.renderer.generateTexture(blue_box),
  ];
}

function drawBlocks(
  app: Application,
  red_texture: Texture,
  green_texture: Texture,
  blue_texture: Texture,
) {
  const red_sprite = new Sprite(red_texture);
  red_sprite.x = 100;
  red_sprite.y = 100;
  app.stage.addChild(red_sprite);

  const green_sprite = new Sprite(green_texture);
  green_sprite.x = 200;
  green_sprite.y = 100;
  app.stage.addChild(green_sprite);

  const blue_sprite = new Sprite(blue_texture);
  blue_sprite.x = 300;
  blue_sprite.y = 100;
  app.stage.addChild(blue_sprite);
}

function drawCompositeTilemap(
  app: Application,
  red_texture: Texture,
  green_texture: Texture,
  blue_texture: Texture,
) {
  const tilemap = new CompositeTilemap();
  tilemap.tile(red_texture, 0, 0);
  tilemap.tile(green_texture, 100, 0);
  tilemap.tile(blue_texture, 200, 0);
  tilemap.x = 100;
  tilemap.y = 200;
  app.stage.addChild(tilemap);
  return tilemap;
}

function makeAtlas(
  app: Application,
  red_texture: Texture,
  green_texture: Texture,
  blue_texture: Texture,
): [Texture, Texture, Texture, Texture] {
  const container = new Container();
  const red_sprite = new Sprite(red_texture);
  const green_sprite = new Sprite(green_texture);
  const blue_sprite = new Sprite(blue_texture);
  red_sprite.x = 0;
  red_sprite.y = 0;
  green_sprite.x = 50;
  green_sprite.y = 0;
  blue_sprite.x = 100;
  blue_sprite.y = 0;
  container.addChild(red_sprite);
  container.addChild(green_sprite);
  container.addChild(blue_sprite);
  const atlas = app.renderer.generateTexture(container);
  const redTex = new Texture({
    source: atlas.source,
    frame: new Rectangle(0, 0, 50, 50),
  });
  const greenTex = new Texture({
    source: atlas.source,
    frame: new Rectangle(50, 0, 50, 50),
  });
  const blueTex = new Texture({
    source: atlas.source,
    frame: new Rectangle(100, 0, 50, 50),
  });
  return [atlas, redTex, greenTex, blueTex];
}

function drawPlainTilemap(
  app: Application,
  atlas: Texture,
  red_texture2: Texture,
  green_texture2: Texture,
  blue_texture2: Texture,
) {
  const tilemap = new Tilemap(atlas.source);
  tilemap.tile(red_texture2, 0, 0);
  tilemap.tile(green_texture2, 100, 0);
  tilemap.tile(blue_texture2, 200, 0);
  tilemap.x = 100;
  tilemap.y = 300;
  app.stage.addChild(tilemap);
  return tilemap;
}

function animateCounter(label: Text, frame: number) {
  label.text = `frame: ${frame}`;
}

function animateCompositeTilemap(
  tilemap: CompositeTilemap,
  textures: Texture[],
  frame: number,
) {
  tilemap.clear();
  const offset = Math.floor(frame / 50);
  const idx0 = (offset + 0) % 3;
  const idx1 = (offset + 1) % 3;
  const idx2 = (offset + 2) % 3;
  tilemap.tile(textures[idx0], 0, 0);
  tilemap.tile(textures[idx1], 100, 0);
  tilemap.tile(textures[idx2], 200, 0);
}

function animatePlainTilemap(
  tilemap: Tilemap,
  textures: Texture[],
  frame: number,
) {
  tilemap.clear();
  const offset = Math.floor(frame / 50);
  const idx0 = (offset + 0) % 3;
  const idx1 = (offset + 1) % 3;
  const idx2 = (offset + 2) % 3;
  tilemap.tile(textures[idx0], 0, 0);
  tilemap.tile(textures[idx1], 100, 0);
  tilemap.tile(textures[idx2], 200, 0);
}

(async () => {
  const app = await setup();
  const [red_texture1, green_texture1, blue_texture1] = makeBlockTextures(app);
  const [atlas, red_texture2, green_texture2, blue_texture2] = makeAtlas(
    app,
    red_texture1,
    green_texture1,
    blue_texture1,
  );
  drawBlocks(app, red_texture1, green_texture1, blue_texture1);
  const compositeTilemap = drawCompositeTilemap(
    app,
    red_texture1,
    green_texture1,
    blue_texture1,
  );
  const tilemap = drawPlainTilemap(
    app,
    atlas,
    red_texture2,
    green_texture2,
    blue_texture2,
  );

  const frameLabel = new Text({
    text: "frame: 0",
    style: { fill: 0xffffff, fontSize: 14 },
  });
  frameLabel.x = 8;
  frameLabel.y = 8;
  app.stage.addChild(frameLabel);

  const texlist1 = [red_texture1, green_texture1, blue_texture1];
  const texlist2 = [red_texture2, green_texture2, blue_texture2];
  let frame = 0;
  app.ticker.add(() => {
    frame++;
    animateCounter(frameLabel, frame);
    animateCompositeTilemap(compositeTilemap, texlist1, frame);
    animatePlainTilemap(tilemap, texlist2, frame);
  });
})();