<!--
 Copyright (C) 2024 - 2025 3NSoft Inc.

 This program is free software: you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation, either version 3 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 this program. If not, see <http://www.gnu.org/licenses/>.
-->

<script lang="ts" setup>
import { ScreenShareOption, SharedStream, WindowShareOption } from '@video/components/types';
import SharePreview from '@video/components/share-preview.vue';
import { ref, onBeforeMount, onBeforeUnmount } from 'vue';
import { Deferred, defer } from '@v1nt1248/3nclient-lib/utils';

const { intiallyShared } = defineProps<{ intiallyShared: SharedStream[]; }>();
function isAlreadyShared(srcId: string): boolean {
  return !!intiallyShared.find(s => (s.srcId === srcId))
}

const emits = defineEmits([ 'select' ]);

type DisplaySourceInfo = web3n.media.DisplaySourceInfo;

const screenChoices = ref<ScreenShareOption[]>();
const windowChoices = ref<WindowShareOption[]>();

const deferredStreams = new Map<string, Deferred<MediaStream>>();
function makeDeferredStream(srcId: string): Promise<MediaStream> {
  const deferredStream = defer<MediaStream>();
  deferredStreams.set(srcId, deferredStream);
  return deferredStream.promise;
}

async function collectAvailableScreenShareOptions(): Promise<void> {
  await w3n.mediaDevices!.setSelectDisplayMediaForCaptureHandler!(
    displayChoicesCollectionCB
  );

  mediaIdToGet = undefined;
  await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })
  .catch(err => console.error(`Error on the start of collection`, err));

  async function setStreamIn(
    { srcId, initiallySelected }: ScreenShareOption|WindowShareOption
  ): Promise<void> {
    if (initiallySelected) {
      const { stream } = intiallyShared.find(s => (s.srcId === srcId))!;
      deferredStreams.get(srcId)?.resolve(stream);
    } else {
      mediaIdToGet = srcId;
      const deferred = deferredStreams.get(mediaIdToGet);
      if (deferred) {
        deferredStreams.delete(mediaIdToGet);
        await navigator.mediaDevices.getDisplayMedia({
          video: true, audio: true
        }).then(
          stream => {
            deferred.resolve(stream);
          },
          err => deferred.reject(err)
        );
      }
      mediaIdToGet = undefined;
    }
  }

  if (screenChoices.value) {
    for (const screen of screenChoices.value) {
      await setStreamIn(screen);
    }
  }

  if (windowChoices.value) {
    for (const frame of windowChoices.value) {
      await setStreamIn(frame);
    }
  }
}

let mediaIdToGet: string|undefined = undefined;

async function displayChoicesCollectionCB(
  choices: DisplaySourceInfo
): Promise<string|undefined> {
  if (mediaIdToGet === undefined) {
    screenChoices.value = choices.screens?.map(info => {
      const { id: srcId, name, display_id } = info;
      const thumbnailURL = toImgURL(info.thumbnail)!;
      return {
        srcId, name, display_id, thumbnailURL,
        initiallySelected: isAlreadyShared(srcId),
        stream: makeDeferredStream(srcId)
      };
    });
    windowChoices.value = choices.windows?.map(info => {
      const { id: srcId, name } = info;
      const thumbnailURL = toImgURL(info.thumbnail)!;
      const appIconURL = toImgURL(info.appIcon);
      return {
        srcId, name, thumbnailURL, appIconURL,
        initiallySelected: isAlreadyShared(srcId),
        stream: makeDeferredStream(srcId)
      };
    });
    return;
  } else {
    return mediaIdToGet;
  }
}

function toImgURL(arr: Uint8Array|undefined): string|undefined {
  if (!arr) {
    return;
  }
  const blob = new Blob([ arr ], { type: 'image/bmp' });
  return URL.createObjectURL(blob);
}

onBeforeMount(() => {
  collectAvailableScreenShareOptions();
});

onBeforeUnmount(() => {
});

const selected: SharedStream[] = intiallyShared.concat();

async function onOptionSelectionChange(
  opt: ScreenShareOption|WindowShareOption, v: boolean
): Promise<void> {
  if (v) {
    const stream = await opt.stream;
    selected.push({
      srcId: opt.srcId,
      stream,
      type: ((opt as ScreenShareOption).display_id ? 'screen' : 'window')
    });
  } else {
    const ind = selected.findIndex(({ stream: { id } }) => (id === opt.srcId));
    selected.splice(ind, 1);
  }
  emits('select', selected);
}

</script>

<template>
  <div :class=$style.dialog>
    <div v-for="screen in screenChoices">
      <share-preview
        :opts=screen
        @selected="v => onOptionSelectionChange(screen, v)"
      ></share-preview>
    </div>

    <div v-for="frame in windowChoices">
      <share-preview
        :opts=frame
        @selected="v => onOptionSelectionChange(frame, v)"
      ></share-preview>
    </div>

  </div>
</template>

<style lang="scss" module>
.dialog {
  font-size: var(--font-14);
  font-weight: 400;
  color: var(--color-text-block-primary-default);
  text-align: center;
  padding: var(--spacing-l) var(--spacing-m);
}
</style>
