import {useEffect, useState} from 'react';

import addKeyListener from '@/add-key-listener.js';
import {RESOLUTION} from '@/constants.js';
import {useClient, usePacket} from '@/context/client.js';
import {useDebug} from '@/context/debug.js';
import {useEcs, useEcsTick} from '@/context/ecs.js';
import {useMainEntity} from '@/context/main-entity.js';

import Disconnected from './disconnected.jsx';
import Dom from './dom.jsx';
import HotBar from './hotbar.jsx';
import Pixi from './pixi.jsx';
import styles from './ui.module.css';

const ratio = RESOLUTION.x / RESOLUTION.y;

function emptySlots() {
  return Array(10).fill(undefined);
}

export default function Ui({disconnected}) {
  // Key input.
  const client = useClient();
  const [mainEntity, setMainEntity] = useMainEntity();
  const [debug, setDebug] = useDebug();
  const [ecs] = useEcs();
  const [showDisconnected, setShowDisconnected] = useState(false);
  const [bufferSlot, setBufferSlot] = useState();
  const [hotbarSlots, setHotbarSlots] = useState(emptySlots());
  const [activeSlot, setActiveSlot] = useState(0);
  const [scale, setScale] = useState(2);
  useEffect(() => {
    let handle;
    if (disconnected) {
      handle = setTimeout(() => {
        setShowDisconnected(true);
      }, 1000);
    }
    else {
      setShowDisconnected(false)
    }
    return () => {
      clearTimeout(handle);
    };
  }, [disconnected]);
  useEffect(() => {
    return addKeyListener(document.body, ({event, type, payload}) => {
      const KEY_MAP = {
        keyDown: 1,
        keyUp: 0,
      };
      let actionPayload;
      switch (payload) {
        case '-':
          if ('keyDown' === type) {
            setScale((scale) => scale > 1 ? scale - 1 : 0.666);
          }
          break;
        case '=':
        case '+':
          if ('keyDown' === type) {
            setScale((scale) => scale < 4 ? Math.floor(scale + 1) : 4);
          }
          break;
        case 'F3': {
          if (event) {
            event.preventDefault();
          }
          if ('keyDown' === type) {
            setDebug(!debug);
          }
          break;
        }
        case 'w': {
          actionPayload = {type: 'moveUp', value: KEY_MAP[type]};
          break;
        }
        case 'a': {
          actionPayload = {type: 'moveLeft', value: KEY_MAP[type]};
          break;
        }
        case 's': {
          actionPayload = {type: 'moveDown', value: KEY_MAP[type]};
          break;
        }
        case 'd': {
          actionPayload = {type: 'moveRight', value: KEY_MAP[type]};
          break;
        }
        case ' ': {
          actionPayload = {type: 'use', value: KEY_MAP[type]};
          break;
        }
        case 'e': {
          actionPayload = {type: 'interact', value: KEY_MAP[type]};
          break;
        }
        case '1': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 1};
          }
          break;
        }
        case '2': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 2};
          }
          break;
        }
        case '3': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 3};
          }
          break;
        }
        case '4': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 4};
          }
          break;
        }
        case '5': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 5};
          }
          break;
        }
        case '6': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 6};
          }
          break;
        }
        case '7': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 7};
          }
          break;
        }
        case '8': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 8};
          }
          break;
        }
        case '9': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 9};
          }
          break;
        }
        case '0': {
          if ('keyDown' === type) {
            actionPayload = {type: 'changeSlot', value: 10};
          }
          break;
        }
      }
      if (actionPayload) {
        client.send({
          type: 'Action',
          payload: actionPayload,
        });
      }
    });
  }, [client, debug, setDebug, setScale]);
  usePacket('Tick', async (payload, client) => {
    if (0 === Object.keys(payload.ecs).length) {
      return;
    }
    await ecs.apply(payload.ecs);
    for (const listener of client.listeners[':Ecs'] ?? []) {
      listener(payload.ecs);
    }
  }, [ecs]);
  useEcsTick((payload) => {
    let localMainEntity = mainEntity;
    for (const id in payload) {
      const entity = ecs.get(id);
      const update = payload[id];
      if (update.Sound?.play) {
        for (const sound of update.Sound.play) {
          (new Audio(sound)).play();
        }
      }
      if (update?.MainEntity) {
        setMainEntity(localMainEntity = id);
      }
      if (localMainEntity === id) {
        if (update.Inventory) {
          setBufferSlot(entity.Inventory.item(0));
          const newHotbarSlots = emptySlots();
          for (let i = 1; i < 11; ++i) {
            newHotbarSlots[i - 1] = entity.Inventory.item(i);
          }
          setHotbarSlots(newHotbarSlots);
        }
        if (update.Wielder && 'activeSlot' in update.Wielder) {
          setActiveSlot(update.Wielder.activeSlot);
        }
      }
    }
  }, [ecs, mainEntity]);
  useEffect(() => {
    function onContextMenu(event) {
      event.preventDefault();
    }
    document.body.addEventListener('contextmenu', onContextMenu);
    return () => {
      document.body.removeEventListener('contextmenu', onContextMenu);
    };
  }, [])
  return (
    // eslint-disable-next-line jsx-a11y/no-static-element-interactions
    <div
      className={styles.ui}
      onMouseDown={(event) => {
        switch (event.button) {
          case 0:
            client.send({
              type: 'Action',
              payload: {type: 'use', value: 1},
            });
            break;
          case 2:
            client.send({
              type: 'Action',
              payload: {type: 'interact', value: 1},
            });
            break;
        }
        event.preventDefault();
      }}
      onMouseUp={(event) => {
        switch (event.button) {
          case 0:
            client.send({
              type: 'Action',
              payload: {type: 'use', value: 0},
            });
            break;
          case 2:
            client.send({
              type: 'Action',
              payload: {type: 'interact', value: 0},
            });
            break;
        }
        event.preventDefault();
      }}
      onWheel={(event) => {
        if (event.deltaY > 0) {
          client.send({
            type: 'Action',
            payload: {type: 'changeSlot', value: 1 + ((activeSlot + 1) % 10)},
          });
        }
        else {
          client.send({
            type: 'Action',
            payload: {type: 'changeSlot', value: 1 + ((activeSlot + 9) % 10)},
          });
        }
      }}
    >
      <style>
        {`
          @media (max-aspect-ratio: ${ratio}) { .${styles.ui} { width: 100%; } }
          @media (min-aspect-ratio: ${ratio}) { .${styles.ui} { height: 100%; } }
          .${styles.ui} {
            cursor: ${
              bufferSlot
                ? `url('${bufferSlot.icon}'), auto !important`
                : 'auto'
              };
          }
        `}
      </style>
      <Pixi scale={scale} />
      {mainEntity && (
        <Dom>
          <HotBar
            active={activeSlot}
            onActivate={(i) => {
              client.send({
                type: 'Action',
                payload: {type: 'swapSlots', value: [0, i + 1]},
              });
            }}
            slots={hotbarSlots}
          />
          {showDisconnected && (
            <Disconnected />
          )}
        </Dom>
      )}
    </div>
  );
}

