<script setup lang="ts">
import gsap from "gsap";
import { useWindowSize } from "@vueuse/core";

const app = useAppStore();
const windowSize = useWindowSize();
const hintsContainer = useTemplateRef("hintsContainer");

const buttonsDown = reactive(new Set<string>());
let mousePressedButton: string | null = null;

const dPadStyle = computed(() => {
  const rx =
    (buttonsDown.has("UP") ? -1 : 0) + (buttonsDown.has("DOWN") ? 1 : 0);
  const ry =
    (buttonsDown.has("LEFT") ? -1 : 0) + (buttonsDown.has("RIGHT") ? 1 : 0);
  if (!rx && !ry) return {};
  return { transform: `rotateX(${rx * 12}deg) rotateY(${ry * 12}deg)` };
});

const pressButton = (button: string) => {
  if (mousePressedButton) {
    buttonsDown.delete(mousePressedButton);
    window.dispatchEvent(
      new KeyboardEvent("keyup", { key: `NDS_${mousePressedButton}` }),
    );
  }
  buttonsDown.add(button);
  mousePressedButton = button;
  window.dispatchEvent(new KeyboardEvent("keydown", { key: `NDS_${button}` }));
};

const releaseButton = () => {
  if (!mousePressedButton) return;
  buttonsDown.delete(mousePressedButton);
  window.dispatchEvent(
    new KeyboardEvent("keyup", { key: `NDS_${mousePressedButton}` }),
  );
  mousePressedButton = null;
};

useKeyDown(({ ndsButton }) => {
  if (ndsButton) buttonsDown.add(ndsButton);
});

useKeyUp(({ ndsButton }) => {
  if (ndsButton) buttonsDown.delete(ndsButton);
});

useMouseUp(releaseButton);

const ndsScale = computed(() => {
  const scaleX = (windowSize.width.value - 40) / 235;
  const scaleY = (windowSize.height.value - 40) / 431;
  return Math.min(scaleX, scaleY);
});

const gallery = useGalleryStore();
const TOP_SCREEN_OFFSET = 170;

const zoomStyle = computed(() => {
  const scale = ndsScale.value * gallery.zoom.scale;
  if (scale === 1) return { scale: ndsScale.value };

  const y = TOP_SCREEN_OFFSET * ndsScale.value * (gallery.zoom.scale - 1);
  return { transform: `translateY(${y}px) scale(${scale})` };
});

watch(
  () => app.hintsVisible,
  async (show) => {
    await nextTick();
    if (!hintsContainer.value) return;
    if (show) {
      gsap.fromTo(
        hintsContainer.value,
        { opacity: 0 },
        { opacity: 1, duration: 0.2, ease: "power1.out" },
      );
    } else {
      gsap.to(hintsContainer.value, {
        opacity: 0,
        duration: 0.2,
        ease: "power1.in",
      });
    }
  },
);
</script>

<template>
  <div class="nds2d-container">
    <div class="nds2d" :style="zoomStyle">
      <div class="nds2d-top-screen">
        <div class="nds2d-speaker-hole nds2d-sh1"></div>
        <div class="nds2d-speaker-hole nds2d-sh2"></div>
        <div class="nds2d-screen nds2d-screen-top">
          <slot name="topScreen" />
        </div>
        <div class="nds2d-speaker-hole nds2d-sh3"></div>
        <div class="nds2d-speaker-hole nds2d-sh4"></div>
      </div>
      <div class="nds2d-hinge">
        <div class="nds2d-mic"></div>
        <div class="nds2d-light"></div>
      </div>
      <div class="nds2d-bottom-screen">
        <div class="nds2d-screen nds2d-screen-bottom">
          <slot name="bottomScreen" />
        </div>

        <div class="nds2d-d-pad-shadow"></div>
        <div class="nds2d-d-pad">
          <div class="nds2d-d-pad-inner" :style="dPadStyle">
            <div class="nds2d-d-pad-base"></div>
            <div class="nds2d-d-pad-light"></div>
            <div
              class="nds2d-d-pad-tip nds2d-tip-up"
              :class="{ pressed: buttonsDown.has('UP') }"
              @mousedown.prevent="pressButton('UP')"
            ></div>
            <div
              class="nds2d-d-pad-tip nds2d-tip-down"
              :class="{ pressed: buttonsDown.has('DOWN') }"
              @mousedown.prevent="pressButton('DOWN')"
            ></div>
            <div
              class="nds2d-d-pad-tip nds2d-tip-left"
              :class="{ pressed: buttonsDown.has('LEFT') }"
              @mousedown.prevent="pressButton('LEFT')"
            ></div>
            <div
              class="nds2d-d-pad-tip nds2d-tip-right"
              :class="{ pressed: buttonsDown.has('RIGHT') }"
              @mousedown.prevent="pressButton('RIGHT')"
            ></div>
            <div class="nds2d-d-pad-marker nds2d-dpm-up"></div>
            <div class="nds2d-d-pad-marker nds2d-dpm-down"></div>
            <div class="nds2d-d-pad-marker nds2d-dpm-left"></div>
            <div class="nds2d-d-pad-marker nds2d-dpm-right"></div>
          </div>
        </div>

        <div
          class="nds2d-button nds2d-btn-x"
          :class="{ pressed: buttonsDown.has('X') }"
          @mousedown.prevent="pressButton('X')"
        >
          X
        </div>
        <div
          class="nds2d-button nds2d-btn-a"
          :class="{ pressed: buttonsDown.has('A') }"
          @mousedown.prevent="pressButton('A')"
        >
          A
        </div>
        <div
          class="nds2d-button nds2d-btn-b"
          :class="{ pressed: buttonsDown.has('B') }"
          @mousedown.prevent="pressButton('B')"
        >
          B
        </div>
        <div
          class="nds2d-button nds2d-btn-y"
          :class="{ pressed: buttonsDown.has('Y') }"
          @mousedown.prevent="pressButton('Y')"
        >
          Y
        </div>

        <div
          class="nds2d-small-button nds2d-start"
          :class="{ pressed: buttonsDown.has('START') }"
          @mousedown.prevent="pressButton('START')"
        ></div>
        <div class="nds2d-small-button-label nds2d-start-label">START</div>
        <div
          class="nds2d-small-button nds2d-select"
          :class="{ pressed: buttonsDown.has('SELECT') }"
          @mousedown.prevent="pressButton('SELECT')"
        ></div>
        <div class="nds2d-small-button-label nds2d-select-label">SELECT</div>

        <div
          ref="hintsContainer"
          class="nds2d-hints-container"
          style="opacity: 0"
        >
          <div class="nds2d-hint nds2d-hint-dpad">Arrows</div>
          <div class="nds2d-hint nds2d-hint-x">{{ mapNDSToKey("X") }}</div>
          <div class="nds2d-hint nds2d-hint-a">{{ mapNDSToKey("A") }}</div>
          <div class="nds2d-hint nds2d-hint-b">{{ mapNDSToKey("B") }}</div>
          <div class="nds2d-hint nds2d-hint-y">{{ mapNDSToKey("Y") }}</div>
          <div class="nds2d-hint nds2d-hint-start">
            {{ mapNDSToKey("START") }}
          </div>
          <div class="nds2d-hint nds2d-hint-select">
            {{ mapNDSToKey("SELECT") }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped lang="css">
.nds2d-container {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: #181818;
  overflow: hidden;
}

.nds2d {
  --primary: #1a1a1a;
  --shadow: #111;
  --black: #050505;

  width: 414px;
  height: 431px;
  position: absolute;
}

.nds2d-top-screen {
  position: absolute;
  width: 100%;
  height: 46.5%;
  border-radius: 15px 15px 5px 5px;
  background: var(--primary);
  box-shadow:
    inset 10px 10px 12px -2px var(--shadow),
    inset -2px 5px 3px 1px var(--shadow),
    inset -5px 5px 5px 2px #444,
    0px -1px 2px 2px #111;
}

.nds2d-screen {
  position: absolute;
  width: 51.5%;
  height: 83.5%;
  border-radius: 3px;
  top: 9.5%;
  left: 24%;
  box-shadow: 0px 0px 3px 0px #111;
}

.nds2d-screen :deep(canvas) {
  position: absolute;
  width: 91%;
  height: 87%;
  left: 4%;
  top: 6%;
  box-shadow: 0px 0px 1px 2px rgba(0, 0, 0, 0.5);
  border: none !important;
}

.nds2d-speaker-hole {
  position: absolute;
  width: 5px;
  height: 5px;
  background: var(--black);
  border-radius: 2.5px;
}

.nds2d-speaker-hole::after {
  position: absolute;
  content: "";
  height: 5px;
  width: 5px;
  border-radius: 2.5px;
  background: var(--black);
  left: 16px;
}

.nds2d-speaker-hole::before {
  position: absolute;
  content: "";
  height: 5px;
  width: 5px;
  border-radius: 2.5px;
  background: var(--black);
  left: 31px;
}

.nds2d-sh1 {
  left: 30px;
  top: 94px;
}

.nds2d-sh2 {
  left: 30px;
  top: 110px;
}

.nds2d-sh3 {
  left: 346px;
  top: 95px;
}

.nds2d-sh4 {
  left: 346px;
  top: 110px;
}

.nds2d-hinge {
  position: absolute;
  width: 99.5%;
  top: 46.5%;
  height: 28px;
  border-radius: 25px;
  z-index: 3;
  background: linear-gradient(
    0deg,
    rgba(8, 8, 8) 0%,
    rgba(20, 20, 20, 1) 10%,
    rgba(32, 32, 32, 1) 65%,
    rgba(20, 20, 20, 1) 100%
  );
  box-shadow: 0px 5px 10px -1px #080808;
}

.nds2d-hinge::after {
  position: absolute;
  content: "";
  width: 2px;
  height: 100%;
  background: #222;
  left: 49px;
}

.nds2d-hinge::before {
  position: absolute;
  content: "";
  width: 2px;
  height: 100%;
  background: #222;
  left: 364px;
}

.nds2d-mic {
  position: absolute;
  width: 4px;
  height: 10px;
  border-radius: 2px;
  background: var(--black);
  left: 49.5%;
  top: 8px;
}

.nds2d-light {
  position: absolute;
  height: 67%;
  width: 4px;
  top: 12%;
  border-radius: 10px;
  right: 25px;
  background: linear-gradient(
    0deg,
    rgba(40, 40, 40, 1) 0%,
    rgba(80, 80, 80, 1) 65%,
    rgba(40, 40, 40, 1) 100%
  );
}

.nds2d-light::after {
  content: "";
  position: absolute;
  height: 100%;
  width: 4px;
  top: 0;
  border-radius: 10px;
  left: 8px;
  background: linear-gradient(
    0deg,
    rgba(92, 107, 73, 1) 0%,
    rgba(211, 212, 183, 1) 65%,
    rgba(123, 142, 98, 1) 100%
  );
}

.nds2d-bottom-screen {
  position: absolute;
  width: 99.5%;
  height: 50%;
  bottom: 0;
  border-radius: 3px 3px 14px 14px;
  background: var(--primary);
  box-shadow: 0px 1px 2px 2px #111;
}

.nds2d-bottom-screen .nds2d-screen {
  top: 24px;
  height: 168px;
  width: 52%;
  background: var(--primary);
  z-index: 1;
}

.nds2d-bottom-screen .nds2d-screen::before {
  content: "";
  position: absolute;
  width: 102%;
  height: 109%;
  left: -1%;
  top: -7%;
  z-index: -1;
  border-radius: 5px;
  box-shadow:
    10px 7px 10px 3px #111,
    -6px 4px 5px 2px var(--primary),
    -12px 4px 8px 3px #333;
}

.nds2d-d-pad-shadow {
  position: absolute;
  left: 19px;
  top: 61px;
  width: 57px;
  height: 57px;
  background: var(--shadow);
  filter: drop-shadow(0 3px 2px #111);
  clip-path: path(
    "M 22.5 0 Q 19 0 19 3.5 L 19 19 L 3.5 19 Q 0 19 0 22.5 L 0 34.5 Q 0 38 3.5 38 L 19 38 L 19 53.5 Q 19 57 22.5 57 L 34.5 57 Q 38 57 38 53.5 L 38 38 L 53.5 38 Q 57 38 57 34.5 L 57 22.5 Q 57 19 53.5 19 L 38 19 L 38 3.5 Q 38 0 34.5 0 Z"
  );
}

.nds2d-d-pad {
  position: absolute;
  left: 20px;
  top: 62px;
  width: 55px;
  height: 55px;
  clip-path: path(
    "M 22 0 Q 19 0 19 3 L 19 19 L 3 19 Q 0 19 0 22 L 0 33 Q 0 36 3 36 L 19 36 L 19 52 Q 19 55 22 55 L 33 55 Q 36 55 36 52 L 36 36 L 52 36 Q 55 36 55 33 L 55 22 Q 55 19 52 19 L 36 19 L 36 3 Q 36 0 33 0 Z"
  );
}

.nds2d-d-pad-inner {
  position: absolute;
  width: 100%;
  height: 100%;
  transition: transform 0.06s ease;
}

.nds2d-d-pad-base {
  position: absolute;
  width: 100%;
  height: 100%;
  background: #333;
}

.nds2d-d-pad-light {
  position: absolute;
  width: 100%;
  height: 100%;
  box-shadow: inset 2px 2px 3px -1px #444;
}

.nds2d-d-pad-tip {
  position: absolute;
  cursor: pointer;
  transition:
    filter 0.06s ease,
    box-shadow 0.06s ease;
}

.nds2d-tip-up {
  left: 19px;
  top: 0;
  width: 17px;
  height: 19px;
  box-shadow: inset 0 2px 3px -1px #444;
}

.nds2d-tip-up.pressed {
  filter: brightness(0.75);
  box-shadow: inset 0 6px 4px -6px #222;
}

.nds2d-tip-down {
  left: 19px;
  bottom: 0;
  width: 17px;
  height: 19px;
  box-shadow: inset 0 -2px 3px -1px #444;
}

.nds2d-tip-down.pressed {
  filter: brightness(0.75);
  box-shadow: inset 0 -6px 4px -6px #222;
}

.nds2d-tip-left {
  left: 0;
  top: 19px;
  width: 19px;
  height: 17px;
  box-shadow: inset 2px 0 3px -1px #444;
}

.nds2d-tip-left.pressed {
  filter: brightness(0.75);
  box-shadow: inset 6px 0 4px -6px #222;
}

.nds2d-tip-right {
  right: 0;
  top: 19px;
  width: 19px;
  height: 17px;
  box-shadow: inset -2px 0 3px -1px #444;
}

.nds2d-tip-right.pressed {
  filter: brightness(0.75);
  box-shadow: inset -6px 0 4px -6px #222;
}

.nds2d-d-pad-marker {
  position: absolute;
  background: #999;
  pointer-events: none;
}

.nds2d-dpm-up,
.nds2d-dpm-down {
  width: 2px;
  height: 11px;
  left: 26px;
}

.nds2d-dpm-up {
  top: 5px;
}

.nds2d-dpm-down {
  bottom: 5px;
}

.nds2d-dpm-left,
.nds2d-dpm-right {
  width: 11px;
  height: 2px;
  top: 26px;
}

.nds2d-dpm-left {
  left: 5px;
}

.nds2d-dpm-right {
  right: 5px;
}

/* Buttons */

.nds2d-button {
  user-select: none;
  cursor: pointer;
  position: absolute;
  width: 21px;
  height: 21px;
  padding-top: 1px;
  border: 1px solid var(--shadow);
  border-radius: 50%;
  z-index: 5;
  font-size: 0.7rem;
  font-weight: 400;
  background: #333;
  color: #999;
  box-shadow:
    3px 2px 3px -2px #111,
    inset 2px 2px 3px -1px #444;
  transition:
    box-shadow 0.06s ease,
    transform 0.06s ease;
}

.nds2d-button.pressed {
  box-shadow:
    1px 1px 1px -1px #111,
    inset 2px 2px 3px -1px #222;
  transform: scale(0.91) translateY(1px);
}

.nds2d-btn-x {
  right: 35px;
  top: 45px;
}

.nds2d-btn-a {
  right: 12px;
  top: 69px;
}

.nds2d-btn-y {
  right: 59px;
  top: 69px;
}

.nds2d-btn-b {
  right: 35px;
  top: 92px;
}

.nds2d-small-button {
  user-select: none;
  cursor: pointer;
  position: absolute;
  width: 11px;
  height: 11px;
  border: 1px solid var(--shadow);
  border-radius: 50%;
  bottom: 53px;
  right: 70px;
  background: #333;
  box-shadow:
    3px 2px 3px -2px #111,
    inset 2px 2px 3px -1px #444;
  transition:
    box-shadow 0.06s ease,
    transform 0.06s ease;
}

.nds2d-small-button.pressed {
  box-shadow:
    1px 1px 1px -1px #111,
    inset 1px 1px 2px -1px #222;
  transform: scale(0.88) translateY(1px);
}

.nds2d-select {
  bottom: 29px;
  right: 70px;
}

.nds2d-small-button-label {
  position: absolute;
  font-size: 7px;
  color: #999;
  pointer-events: none;
}

.nds2d-start-label {
  bottom: 53px;
  right: 44px;
}

.nds2d-select-label {
  bottom: 29px;
  right: 40px;
}

.nds2d-hints-container {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 10;
}

.nds2d-hint {
  position: absolute;
  font-size: 7px;
  color: #fff;
  background: rgba(0, 0, 0, 0.7);
  padding: 1px 3px;
  border-radius: 2px;
  white-space: nowrap;
  pointer-events: none;
}

.nds2d-hint-dpad {
  left: 47px;
  top: 46px;
  transform: translateX(-50%);
}

.nds2d-hint-x,
.nds2d-hint-a,
.nds2d-hint-b,
.nds2d-hint-y {
  width: 15px;
  text-align: center;
}

.nds2d-hint-x {
  right: 38px;
  top: 32px;
}

.nds2d-hint-a {
  right: 15px;
  top: 56px;
}

.nds2d-hint-b {
  right: 38px;
  top: 79px;
}

.nds2d-hint-y {
  right: 62px;
  top: 56px;
}

.nds2d-hint-start {
  left: 330px;
  bottom: 53px;
  transform: translateX(-100%);
}

.nds2d-hint-select {
  left: 330px;
  bottom: 28px;
  transform: translateX(-100%);
}
</style>
