import { invoke } from '@tauri-apps/api';
import { listen } from '@tauri-apps/api/event';
import { convertFileSrc } from '@tauri-apps/api/tauri';
import {
  Component,
  createEffect,
  createSignal,
  JSX,
  onCleanup,
  onMount,
  splitProps,
} from 'solid-js';

type ScreenViewProps = {
  displayId: number;
} & JSX.HTMLAttributes<HTMLDivElement>;

async function subscribeScreenshotUpdate(displayId: number) {
  await invoke('subscribe_encoded_screenshot_updated', {
    displayId,
  });
}

export const ScreenView: Component<ScreenViewProps> = (props) => {
  const [localProps, rootProps] = splitProps(props, ['displayId']);
  let canvas: HTMLCanvasElement;
  let root: HTMLDivElement;
  const [ctx, setCtx] = createSignal<CanvasRenderingContext2D | null>(null);
  const [drawInfo, setDrawInfo] = createSignal({
    drawX: 0,
    drawY: 0,
    drawWidth: 0,
    drawHeight: 0,
  });
  const [imageData, setImageData] = createSignal<{
    buffer: Uint8ClampedArray;
    width: number;
    height: number;
  } | null>(null);

  const resetSize = () => {
    const aspectRatio = canvas.width / canvas.height;

    const drawWidth = Math.round(
      Math.min(root.clientWidth, root.clientHeight * aspectRatio),
    );
    const drawHeight = Math.round(
      Math.min(root.clientHeight, root.clientWidth / aspectRatio),
    );

    const drawX = Math.round((root.clientWidth - drawWidth) / 2);
    const drawY = Math.round((root.clientHeight - drawHeight) / 2);

    setDrawInfo({
      drawX,
      drawY,
      drawWidth,
      drawHeight,
    });

    canvas.width = root.clientWidth;
    canvas.height = root.clientHeight;

    draw(true);
  };

  const draw = (cached: boolean = false) => {
    const { drawX, drawY, drawWidth, drawHeight } = drawInfo();

    let _ctx = ctx();
    let raw = imageData();
    if (_ctx && raw) {
      _ctx.clearRect(0, 0, canvas.width, canvas.height);
      if (cached) {
        for (let i = 3; i < raw.buffer.length; i += 8) {
          raw.buffer[i] = Math.floor(raw.buffer[i] * 0.7);
        }
      }
      const img = new ImageData(raw.buffer, raw.width, raw.height);
      _ctx.putImageData(img, drawX, drawY);
    }
  };

  createEffect(() => {
    const unlisten = listen<{
      base64_image: string;
      display_id: number;
      height: number;
      width: number;
    }>('encoded-screenshot-updated', (event) => {
      const { drawWidth, drawHeight } = drawInfo();
      if (event.payload.display_id === localProps.displayId) {
        const url = convertFileSrc(
          `displays/${localProps.displayId}?width=${drawWidth}&height=${drawHeight}`,
          'ambient-light',
        );
        fetch(url, {
          mode: 'cors',
        })
          .then((res) => res.body?.getReader().read())
          .then((buffer) => {
            // console.log(buffer?.value?.length);
            if (buffer?.value) {
              setImageData({
                buffer: new Uint8ClampedArray(buffer?.value),
                width: drawWidth,
                height: drawHeight,
              });
            } else {
              setImageData(null);
            }
            draw();
          });
      }

      // console.log(event.payload.display_id, localProps.displayId);
    });
    subscribeScreenshotUpdate(localProps.displayId);

    onCleanup(() => {
      unlisten.then((unlisten) => {
        unlisten();
      });
    });
  });

  createEffect(() => {
    let resizeObserver: ResizeObserver;

    onMount(() => {
      setCtx(canvas.getContext('2d'));
      new ResizeObserver(() => {
        resetSize();
      }).observe(root);
    });

    onCleanup(() => {
      resizeObserver?.unobserve(root);
    });
  });

  return (
    <div
      ref={root!}
      {...rootProps}
      class={'overflow-hidden h-full w-full ' + rootProps.class}
    >
      <canvas ref={canvas!} />
    </div>
  );
};
