import {useEffect, useRef, useState} from 'react';

import {useClient} from '@/react/context/client.js';
import {useEcs} from '@/react/context/ecs.js';
import useRect from '@/react/hooks/use-rect.js';

import styles from './tiles.module.css';

export default function Tiles({eventsChannel}) {
  const client = useClient();
  const wrapperRef = useRef();
  const imageRef = useRef();
  const imageRect = useRect(imageRef);
  const [indices, setIndices] = useState([]);
  const [selection, setSelection] = useState({x: 0, y: 0, w: 1, h: 1});
  const [moveStart, setMoveStart] = useState();
  const [layer, setLayer] = useState(0);
  const [brush, setBrush] = useState(0);
  const [stamp, setStamp] = useState([]);
  const [ecs] = useEcs();
  useEffect(() => {
    if (!ecs) {
      return;
    }
    const master = ecs.get(1);
    if (!master) {
      return;
    }
    const {TileLayers} = master;
    const {area, tileSize} = TileLayers.layer(0);
    function onClick({x, y}) {
      const at = {
        x: Math.floor(x / tileSize.x),
        y: Math.floor(y / tileSize.y),
      };
      if (at.x < 0 || at.y < 0 || at.x >= area.x || at.y >= area.y) {
        return;
      }
      client.send({
        type: 'AdminAction',
        payload: {
          type: 'paint',
          value: {
            brush,
            layer,
            stamp: {
              at,
              data: stamp,
            },
          },
        },
      });
    }
    eventsChannel.addListener('click', onClick);
    return () => {
      eventsChannel.removeListener('click', onClick);
    };
  });
  if (!ecs) {
    return false;
  }
  const master = ecs.get(1);
  if (!master) {
    return false;
  }
  const {TileLayers} = master;
  const {sourceJson, tileSize} = TileLayers.layer(0);
  const {w, h} = sourceJson.meta.size;
  const factor = (imageRect?.width ?? 1) / w;
  const wt = w / tileSize.x;
  return (
    <div className={styles.tiles}>
      <form>
        <div className={styles.paintBar}>
          <div className={styles.layer}>
            <label>
              <span>Layer</span>
              <select
                onChange={(event) => {
                  setLayer(event.target.value)
                }}
                value={layer}
              >
                {
                  Object.keys(TileLayers.layers)
                    .map((layer, i) => (
                      <option
                        key={i}
                        value={i}
                      >
                        {i}
                      </option>
                    ))
                }
              </select>
            </label>
          </div>
          <div className={styles.brush}>
            <label>
              <span>Brush</span>
              <select
                onChange={(event) => {
                  setBrush(event.target.value)
                }}
                value={brush}
              >
                <option>Paint</option>
              </select>
            </label>
          </div>
        </div>
      </form>
      {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
      <div
        onMouseDown={(event) => {
          const wrapperRect = wrapperRef.current.getBoundingClientRect();
          const {left, top} = wrapperRect;
          const c = {
            x: event.clientX,
            y: event.clientY + wrapperRef.current.scrollTop,
          };
          if (
            c.x - left >= (w * factor)
            || c.y - top >= (h * factor)
          ) {
            return;
          }
          const x = Math.floor((c.x - left) / (tileSize.x * factor));
          const y = Math.floor((c.y - top) / (tileSize.y * factor));
          setMoveStart({x, y});
          setSelection({x, y, w: 1, h: 1});
          setStamp([[y * wt + x]]);
          setIndices([y * wt + x]);
        }}
        onMouseMove={(event) => {
          if (0 === event.buttons) {
            setMoveStart();
            return;
          }
          if (!moveStart) {
            return;
          }
          const {x: sx, y: sy} = moveStart;
          const wrapperRect = wrapperRef.current.getBoundingClientRect();
          const {left, top} = wrapperRect;
          const c = {
            x: event.clientX,
            y: event.clientY + wrapperRef.current.scrollTop,
          };
          const x = Math.floor(
            Math.max(0, Math.min((w * factor) - 1, (c.x - left)) / (tileSize.x * factor)),
          );
          const y = Math.floor(
            Math.max(0, Math.min((h * factor) - 1, (c.y - top)) / (tileSize.y * factor)),
          );
          const mx = Math.min(sx, x);
          const my = Math.min(sy, y);
          const sw = Math.abs(sx - x) + 1;
          const sh = Math.abs(sy - y) + 1;
          const newSelection = {
            x: mx,
            y: my,
            w: sw,
            h: sh,
          };
          if (
            selection.x === newSelection.x
            && selection.y === newSelection.y
            && selection.w === newSelection.w
            && selection.h === newSelection.h
          ) {
            return;
          }
          setSelection(newSelection);
          const stamp = [];
          for (let iy = 0; iy < sh; ++iy) {
            const row = [];
            for (let ix = 0; ix < sw; ++ix) {
              row.push((my + iy) * wt + mx + ix);
            }
            stamp.push(row);
          }
          setStamp(stamp);
          const indices = [];
          for (let sy = 0; sy < newSelection.h; ++sy) {
            for (let sx = 0; sx < newSelection.w; ++sx) {
              indices.push(((newSelection.y + sy) * wt) + (newSelection.x + sx));
            }
          }
          setIndices(indices);
        }}
        className={styles.selectionWrapper}
        ref={wrapperRef}
      >
        <div
          className={styles.selection}
          style={{
            top: selection.y * tileSize.y * factor,
            left: selection.x * tileSize.x * factor,
            height: selection.h * tileSize.x * factor,
            width: selection.w * tileSize.y * factor,
          }}
        />
        <img
          alt="tileset"
          ref={imageRef}
          src={TileLayers.layer(0).source.replace('.json', '.png')}
        />
      </div>
      <div
        className={styles.status}
      >
        <code className={styles.selectionStatus}>
          Sel:
          {' {'}
          {selection.x}{', '}
          {selection.y}{', '}
          {selection.w}{', '}
          {selection.h}
          {'}'}
        </code>
        <code>
          Idx:
          {' ['}
          {indices.join(', ')}
          {']'}
        </code>
      </div>
    </div>
  );
}