import { Juego, State as AllState } from "./main";
import { Sprite } from "./sprite";
import {
  Box,
  boxCollision,
  drawText,
  isMobile,
  isTouching,
  posInBox,
  randomFromArray,
  share,
} from "./utils";
import * as welcome from "./welcome";

const ENEMIES_NUM = 12;
const SEED_COOLDOWN = 150;
const MAP_MIN = 1000;
const MAP_MAX = 10000;
const MAP_SIZE = MAP_MAX - MAP_MIN;
const SIGNATURES_TO_WIN = 20000;
const CITIZENS = SIGNATURES_TO_WIN / 1000;
const TIME = 2 * 60 * 1000;
const TIME_LOST_WHEN_HIT = 20 * 1000;

type Citizen = { x: number; sprite: Sprite };

export type State = {
  current: "jugando";
  has: "lost" | "won" | null;
  playing: boolean;
  pos: { x: number };
  view: { x: number };
  side: "left" | "right";
  enemies: { x: number; sprite: Sprite }[];
  seeds: { x: number; velocity: { x: number } }[];
  trees: { x: number; sprite: Sprite }[];
  citizens: Citizen[];
  signatures: number;
  justSigned: number;
  time: number;
  seedCooldown: number;
};

export function createJugandoState(juego: Juego<any>): State {
  let citizens = [];
  for (let i = 0; i < CITIZENS; i++) {
    citizens.push({
      x: MAP_MIN + (i + 1) * (MAP_SIZE / CITIZENS),
      sprite: randomFromArray([
        juego.sprites.ciudadanx1,
        juego.sprites.ciudadanx2,
        juego.sprites.ciudadanx3,
      ]),
    });
  }
  return {
    current: "jugando",
    has: null,
    playing: false,
    pos: { x: MAP_MIN + 100 },
    view: { x: 0 },
    side: "right",
    enemies: [],
    seeds: [],
    trees: [],
    signatures: 0,
    citizens,
    justSigned: 0,
    time: TIME,
    seedCooldown: 0,
  };
}

function touchControls(juego: Juego<State>): {
  left: Box;
  right: Box;
  center: Box;
} {
  return {
    left: {
      x: 0,
      y: 0,
      width: juego.canvas.width / 7,
      height: juego.canvas.height,
    },
    right: {
      x: juego.canvas.width - juego.canvas.width / 7,
      y: 0,
      width: juego.canvas.width / 7,
      height: juego.canvas.height,
    },
    center: {
      x: juego.canvas.width / 7,
      y: 0,
      width: juego.canvas.width - (juego.canvas.width / 7) * 2,
      height: juego.canvas.height,
    },
  };
}

function shareButton(juego: Juego<State>): {
  box: Box;
  sprite: Sprite;
} {
  const sprite =
    juego.state.has === "won"
      ? juego.sprites.botonCompartirFelicitaciones
      : juego.sprites.botonCompartirPerdiste;

  const width = sprite.getWidth(juego);
  const height = sprite.getHeight(juego);
  return {
    box: {
      x: juego.canvas.width - width - 5,
      y: juego.canvas.height * 0.85 - height,
      width,
      height,
    },
    sprite,
  };
}

const socialUrls = {
  facebook: "https://www.facebook.com/Fpg.caba",
  instagram: "https://www.instagram.com/fpg.caba/",
  twitter: "https://twitter.com/FtePatriaGrande",
};
function socialButtons(juego: Juego<State>): {
  [key in "facebook" | "instagram" | "twitter"]: {
    box: Box;
    sprite: Sprite;
  };
} {
  let x = juego.canvas.width - 5;
  let s: any = {};
  for (const network of ["facebook", "instagram", "twitter"]) {
    const sprite = (juego.sprites as { [key in string]: Sprite })[network];
    const width = sprite.getWidth(juego);
    const height = sprite.getHeight(juego);
    x -= width + 20;
    s[network] = {
      box: {
        x,
        y: juego.canvas.height * 0.95 - height,
        width,
        height,
      },
      sprite,
    };
  }
  return s;
}

export function update(juego: Juego<State>, dt: number) {
  if (juego.state.has) {
    const button = shareButton(juego);
    if (
      isTouching(juego, button.box) ||
      (juego.mouse.down && posInBox(button.box, juego.mouse))
    ) {
      share();
      (juego as Juego<AllState>).state = welcome.createState();
    }
    for (const [network, { box }] of Object.entries(socialButtons(juego))) {
      if (
        isTouching(juego, box) ||
        (juego.mouse.down && posInBox(box, juego.mouse))
      ) {
        location.href = (socialUrls as any)[network];
      }
    }
    return;
  }

  if (!juego.state.playing) {
    juego.assets.fondo.play();
    juego.state.playing = true;
  }

  juego.state.time -= dt;
  if (juego.state.time < 0) {
    juego.state.has = "lost";
    return;
  }

  const playerSpeed = juego.canvas.width * 0.15;
  const enemySpeed = juego.canvas.width * 0.05;
  const { left, right, center } = touchControls(juego);
  if (
    juego.keyboard.keys.d ||
    juego.keyboard.keys.ArrowRight ||
    isTouching(juego, right)
  ) {
    juego.state.side = "right";
    juego.state.pos.x += (dt / 1000) * playerSpeed;
  }
  if (
    juego.keyboard.keys.a ||
    juego.keyboard.keys.ArrowLeft ||
    isTouching(juego, left)
  ) {
    juego.state.side = "left";
    juego.state.pos.x -= (dt / 1000) * playerSpeed;
  }

  if (juego.state.pos.x < MAP_MIN) juego.state.pos.x = MAP_MIN;
  if (juego.state.pos.x > MAP_MAX) juego.state.pos.x = MAP_MAX;

  juego.state.seedCooldown -= dt;
  juego.state.justSigned -= dt;

  const citizen = detectCitizen(juego);

  if (juego.keyboard.keys[" "] || isTouching(juego, center)) {
    if (citizen) {
      juego.state.signatures += 1000;
      juego.state.citizens = juego.state.citizens.filter((c) => c != citizen);
      juego.state.justSigned = 1000;
    } else {
      if (juego.state.seedCooldown < 0) {
        const seedSpeed = juego.canvas.width * 0.7;
        juego.state.seeds.push({
          x: juego.state.pos.x,
          velocity: { x: juego.state.side === "left" ? -seedSpeed : seedSpeed },
        });
      }
    }
    juego.state.seedCooldown = SEED_COOLDOWN;
  }

  for (const seed of juego.state.seeds) {
    seed.x += (dt / 1000) * seed.velocity.x;
    seed.velocity.x *= 0.97;
    for (const enemy of juego.state.enemies) {
      if (
        boxCollision(
          getFlooredBox(juego, juego.sprites.semilla, seed.x),
          getFlooredBox(juego, enemy.sprite, enemy.x)
        )
      ) {
        juego.state.seeds = juego.state.seeds.filter((s) => s.x !== seed.x);
        juego.state.enemies = juego.state.enemies.filter(
          (e) => e.x !== enemy.x
        );
        juego.state.trees.push({
          x: enemy.x,
          sprite: randomFromArray([juego.sprites.arbol1, juego.sprites.arbol2]),
        });
      }
    }
    if (Math.abs(seed.velocity.x) < 100)
      juego.state.seeds = juego.state.seeds.filter(
        (s) => s.velocity.x !== seed.velocity.x
      );
  }

  if (juego.state.signatures >= SIGNATURES_TO_WIN) {
    juego.state.has = "won";
    return;
  }

  while (juego.state.enemies.length < ENEMIES_NUM) {
    const x = Math.random() * 6000 + juego.state.pos.x;
    // Don't spawn enemies too close
    if (Math.abs(juego.state.pos.x - x) < 300) continue;
    juego.state.enemies.push({
      x,
      sprite: randomFromArray([
        juego.sprites.larreta,
        juego.sprites.millonarioMalo,
      ]),
    });
  }

  for (const enemy of juego.state.enemies) {
    const distance = enemy.x - juego.state.pos.x;
    if (distance < 0) {
      enemy.x += (dt / 1000) * enemySpeed;
    } else {
      enemy.x -= (dt / 1000) * enemySpeed;
    }

    if (
      boxCollision(
        getFlooredBox(juego, juego.sprites.jugadorx, juego.state.pos.x),
        getFlooredBox(juego, enemy.sprite, enemy.x)
      )
    ) {
      juego.state.enemies = juego.state.enemies.filter((e) => e.x !== enemy.x);
      juego.state.time -= TIME_LOST_WHEN_HIT;
    }
  }

  juego.state.view.x =
    -juego.state.pos.x +
    juego.canvas.width / 2 -
    juego.sprites.jugadorx.getWidth(juego) / 2;
}

function getFlooredBox(juego: Juego<State>, sprite: Sprite, x: number): Box {
  const height = sprite.getHeight(juego);
  return {
    x,
    y: getFloorY(juego) - height,
    width: sprite.getWidth(juego),
    height,
  };
}

function detectCitizen(juego: Juego<State>): Citizen | undefined {
  const jugadorxBox = getFlooredBox(
    juego,
    juego.sprites.jugadorx,
    juego.state.pos.x
  );
  return juego.state.citizens.find((c) =>
    boxCollision(jugadorxBox, getFlooredBox(juego, c.sprite, c.x))
  );
}

function drawJugadorx(juego: Juego<State>) {
  const floorY = getFloorY(juego);
  const width = juego.sprites.jugadorx.getWidth(juego);
  const height = juego.sprites.jugadorx.getHeight(juego);
  juego.sprites.jugadorx.draw(
    juego,
    juego.state.pos.x + juego.state.view.x,
    floorY - height,
    0,
    juego.state.side === "left"
  );
  if (juego.state.justSigned > 0) {
    juego.sprites.dialogoFirma.draw(
      juego,
      juego.state.pos.x + juego.state.view.x + width,
      floorY - height - juego.sprites.dialogoFirma.getHeight(juego)
    );
  }
}

function drawTrees(juego: Juego<State>) {
  const floorY = getFloorY(juego);
  for (const tree of juego.state.trees) {
    const height = tree.sprite.getHeight(juego);
    tree.sprite.draw(juego, tree.x + juego.state.view.x, floorY - height);
  }
}
function drawEnemies(juego: Juego<State>) {
  const floorY = getFloorY(juego);
  for (const enemy of juego.state.enemies) {
    const height = enemy.sprite.getHeight(juego);
    enemy.sprite.draw(juego, enemy.x + juego.state.view.x, floorY - height);
  }
}
function drawCitizens(juego: Juego<State>) {
  const floorY = getFloorY(juego);
  for (const citizen of juego.state.citizens) {
    const height = citizen.sprite.getHeight(juego);
    citizen.sprite.draw(juego, citizen.x + juego.state.view.x, floorY - height);
  }
}

function drawSeeds(juego: Juego<State>) {
  const height = juego.sprites.semilla.getHeight(juego);
  const floorY = getFloorY(juego);
  for (const seed of juego.state.seeds) {
    juego.sprites.semilla.draw(
      juego,
      seed.x + juego.state.view.x,
      floorY - height
    );
  }
}

function getFloorY(juego: Juego<State>): number {
  return (juego.canvas.height * 65) / 96;
}

export function drawBackground(
  juego: Juego<AllState>,
  y: number,
  height: number,
  img: HTMLImageElement,
  offset: number = 0,
  sourceBox?: Box,
  opacity?: number
) {
  const aspect = img.width / img.height;
  const width = height * aspect;
  if (opacity !== undefined) {
    juego.ctx.globalAlpha = opacity;
  }
  for (let i = 0; i < 50; i++) {
    if (sourceBox) {
      juego.ctx.drawImage(
        img,
        sourceBox.x,
        sourceBox.y,
        sourceBox.width,
        sourceBox.height,
        i * (width - (opacity === undefined ? 1 : 0)) + offset,
        y,
        width,
        height
      );
    } else {
      juego.ctx.drawImage(
        img,
        i * (width - (opacity === undefined ? 1 : 0)) + offset,
        y,
        width,
        height
      );
    }
  }
  if (opacity !== undefined) {
    juego.ctx.globalAlpha = 1;
  }
}

export function draw(juego: Juego<State>, timestamp: number) {
  drawBackground(
    juego,
    0,
    juego.canvas.height,
    juego.assets.cieloRioCalle,
    -juego.state.pos.x
  );
  drawBackground(
    juego,
    0,
    getFloorY(juego),
    juego.assets.edificios,
    -juego.state.pos.x,
    {
      x: 0,
      y: 0,
      width: 89,
      height: 48,
    }
  );

  drawBackground(
    juego,
    0,
    juego.canvas.height,
    juego.assets.cielo,
    -juego.state.pos.x,
    undefined,
    juego.state.signatures / SIGNATURES_TO_WIN
  );
  drawBackground(
    juego,
    0,
    juego.canvas.height,
    juego.assets.parquePublicoRio,
    -juego.state.pos.x,
    undefined,
    juego.state.signatures / SIGNATURES_TO_WIN
  );

  drawTrees(juego);
  drawCitizens(juego);
  drawEnemies(juego);

  drawJugadorx(juego);
  drawSeeds(juego);

  if (isMobile(juego) && !juego.state.has) {
    const { left, right } = touchControls(juego);

    juego.ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
    juego.ctx.fillRect(left.x, left.y, left.width, left.height);
    juego.ctx.fillRect(right.x, right.y, right.width, right.height);

    const width = juego.sprites.flecha.getWidth(juego);
    const height = juego.sprites.flecha.getHeight(juego);
    juego.sprites.flecha.draw(
      juego,
      left.x + left.width - width,
      left.height / 2 - height / 2,
      0,
      false
    );
    juego.sprites.flecha.draw(
      juego,
      right.x,
      right.height / 2 - height / 2,
      0,
      true
    );
  }

  juego.ctx.fillStyle = "white";
  const firmasBox = drawText(
    juego,
    `Firmas: ${juego.state.signatures}/${SIGNATURES_TO_WIN}`,
    { x: juego.canvas.width - 10, y: 10 },
    { bold: true, align: "right" }
  );

  const tiempoBox = drawText(
    juego,
    `Tiempo restante`,
    { x: 10, y: 10 },
    { bold: false, align: "left" }
  );

  juego.ctx.fillRect(
    10,
    tiempoBox.y + tiempoBox.height,
    (juego.canvas.width - firmasBox.width - 20) * (juego.state.time / TIME),
    30
  );

  if (detectCitizen(juego)) {
    drawText(
      juego,
      `¡Apreta para recolectar firmas!`,
      { x: juego.canvas.width / 2, y: juego.canvas.height * 0.7 },
      { align: "center", maxWidth: juego.canvas.width }
    );
  }

  if (juego.state.has) {
    juego.sprites.logoFPGFDTBlanco.draw(
      juego,
      0,
      juego.canvas.height - juego.sprites.logoFPGFDTBlanco.getHeight(juego) - 10
    );
    if (juego.state.has === "lost") {
      juego.sprites.placaPerdiste.draw(juego, 10, juego.canvas.height * 0.1);
      juego.sprites.baldosa.draw(
        juego,
        juego.canvas.width - juego.sprites.baldosa.getWidth(juego) - 30,
        juego.canvas.height * 0.3
      );
    } else if (juego.state.has === "won") {
      const width = juego.sprites.placaFelicitaciones.getWidth(juego);
      juego.sprites.placaFelicitaciones.draw(
        juego,
        juego.canvas.width / 2 - width / 2,
        juego.canvas.height * 0.1
      );
    }
    const button = shareButton(juego);
    button.sprite.draw(juego, button.box.x, button.box.y);
    const socials = socialButtons(juego);
    for (const { sprite, box } of Object.values(socials)) {
      sprite.draw(juego, box.x, box.y);
    }
  }
}
