<!-- Svelte 4.2.11 -->

<script lang="ts">
  import {
    type AquaNetUser,
    type UserBox,
    type UserItem,
  } from "../../libs/generalTypes";
  import { DATA, USER, USERBOX } from "../../libs/sdk";
  import { t, ts } from "../../libs/i18n";
  import { DATA_HOST, FADE_IN, FADE_OUT, USERBOX_DEFAULT_URL } from "../../libs/config";
  import { fade, slide } from "svelte/transition";
  import StatusOverlays from "../StatusOverlays.svelte";
  import Icon from "@iconify/svelte";
  import GameSettingFields from "./GameSettingFields.svelte";
  import { filter } from "d3";
  import { coverNotFound } from "../../libs/ui";

  import { userboxFileProcess, ddsDB, initializeDb } from "../../libs/userbox/userbox"

  import ChuniPenguinComponent from "./userbox/ChuniPenguin.svelte"
  import ChuniUserplateComponent from "./userbox/ChuniUserplate.svelte";

  import useLocalStorage from "../../libs/hooks/useLocalStorage.svelte";
  import { DDS } from "../../libs/userbox/dds";

  let user: AquaNetUser
  let [loading, error, submitting, preview] = [true, "", "", ""]
  let changed: string[] = [];

  // Available (unlocked) options for each kind of item
  // In allItems: 'namePlate', 'frame', 'trophy', 'mapIcon', 'systemVoice', 'avatarAccessory'
  let allItems: Record<string, Record<string, { name: string }>> = {}
  let iKinds = { namePlate: 1, frame: 2, trophy: 3, mapIcon: 8, systemVoice: 9, avatarAccessory: 11 }
  // In userbox: 'nameplateId', 'frameId', 'trophyId', 'mapIconId', 'voiceId', 'avatar{Wear/Head/Face/Skin/Item/Front/Back}'
  let userbox: UserBox
  let avatarKinds = ['Wear', 'Head', 'Face', 'Skin', 'Item', 'Front', 'Back'] as const
  // iKey should match allItems keys, and ubKey should match userbox keys
  let userItems: { iKey: string, ubKey: keyof UserBox, items: UserItem[] }[] = []

  // Submit changes
  function submit(field: keyof UserBox) {
    let obj = { field, value: userbox[field] }
    if (submitting) return
    submitting = obj.field

    USERBOX.setUserBox(obj)
      .then(() => changed = changed.filter((c) => c !== obj.field))
      .catch(e => error = e.message)
      .finally(() => submitting = "")
  }

  // Fetch data from the server
  async function fetchData() {
    const profile = await USERBOX.getProfile().catch(_ => {
      loading = false
      error = t("userbox.error.nodata")
    })
    if (!profile) return
    userbox = profile.user
    userItems = Object.entries(iKinds).flatMap(([iKey, iKind]) => {
      if (iKey != 'avatarAccessory') {
        let ubKey = `${iKey}Id`
        if (ubKey == 'namePlateId') ubKey = 'nameplateId'
        if (ubKey == 'systemVoiceId') ubKey = 'voiceId'
        return [{ iKey, ubKey: ubKey as keyof UserBox,
          items: profile.items.filter(x => x.itemKind === iKind)
        }]
      }

      return avatarKinds.map((aKind, i) => {
        let items = profile.items.filter(x => x.itemKind === iKind && Math.floor(x.itemId / 100000) % 10 === i + 1)
        return { iKey, ubKey: `avatar${aKind}` as keyof UserBox, items }
      })
    })

    allItems = await DATA.allItems('chu3').catch(_ => {
      loading = false
      error = t("userbox.error.nodata")
    }) as typeof allItems

    console.log("User Items", userItems)
    console.log("All items", allItems)
    console.log("Userbox", userbox)

    loading = false
  }

  USER.me().then(u => {
    if (!u) throw new Error(t("userbox.error.nodata"))
    user = u
    return fetchData()
  }).catch((e) => { loading = false; error = e.message });
  
  let DDSreader: DDS | undefined;

  let USERBOX_PROGRESS = 0;
  let USERBOX_SETUP_RUN = false;
  let USERBOX_SETUP_MODE = false;
  let USERBOX_SETUP_TEXT = t("userbox.new.setup");

  let USERBOX_ENABLED = useLocalStorage("userboxNew", false);
  let USERBOX_PROFILE_ENABLED = useLocalStorage("userboxNewProfile", false);
  let USERBOX_INSTALLED = false;
  let USERBOX_SUPPORT = "webkitGetAsEntry" in DataTransferItem.prototype;

  type OnlyNumberPropsOf<T extends Record<string, any>> = {[Prop in keyof T as (T[Prop] extends number ? Prop : never)]: T[Prop]}
  let userboxSelected: keyof OnlyNumberPropsOf<UserBox> = "avatarWear";
  const userboxNewOptions = ["systemVoice", "frame", "trophy", "mapIcon"]

  async function userboxSafeDrop(event: Event & { currentTarget: EventTarget & HTMLInputElement; }) {
    if (!event.target) return null;
    let input = event.target as HTMLInputElement;
    let folder = input.webkitEntries[0];
    error = await userboxFileProcess(folder, (progress: number, progressString: string) => {
      USERBOX_SETUP_TEXT = progressString;
      USERBOX_PROGRESS = progress;
    }) ?? "";
  }

  let USERBOX_URL_STATE = useLocalStorage("userboxURL", USERBOX_DEFAULT_URL);
  function userboxHandleInput(e: KeyboardEvent) {
    if (e.key != "Enter")
      return;
    let baseURL = (e.target as HTMLInputElement).value;
    if (baseURL != "")
      try {
        // validate url
        new URL(baseURL);
      } catch(err) {
        return error = t("userbox.new.error.invalidUrl")
      }
    USERBOX_URL_STATE.value = baseURL;
    USERBOX_ENABLED.value = true;
    USERBOX_PROFILE_ENABLED.value = true;
    location.reload();
  }

  if (USERBOX_DEFAULT_URL)
    USERBOX_URL_STATE.value = USERBOX_DEFAULT_URL;

  indexedDB.databases().then(async (dbi) => {
    let databaseExists = dbi.some(db => db.name == "userboxChusanDDS");
    if (databaseExists) {
      await initializeDb();
      DDSreader = new DDS(ddsDB);
      USERBOX_INSTALLED = databaseExists;
    } else if (USERBOX_URL_STATE.value)
      USERBOX_INSTALLED = true;
  })

</script>

<StatusOverlays {error} loading={loading || !!submitting} />
{#if !loading && !error}
<div out:fade={FADE_OUT} in:fade={FADE_IN}>
  <h2>{t("userbox.header.general")}</h2>
  <GameSettingFields game="chu3"/>
  <h2>{t("userbox.header.userbox")}</h2>
  {#if !USERBOX_ENABLED.value || !USERBOX_INSTALLED}
    <div class="fields">
      {#each userItems as { iKey, ubKey, items }, i}
        <div class="field">
          <label for={ubKey}>{ts(`userbox.${ubKey}`)}</label>
          <div>
            <select bind:value={userbox[ubKey]} id={ubKey} on:change={() => changed = [...changed, ubKey]}>
              {#each items as option}
                <option value={option.itemId}>{allItems[iKey][option.itemId]?.name || `(unknown ${option.itemId})`}</option>
              {/each}
            </select>
            {#if changed.includes(ubKey)}
              <button transition:slide={{axis: "x"}} on:click={() => submit(ubKey)} disabled={!!submitting}>
                {t("settings.profile.save")}
              </button>
            {/if}
          </div>
        </div>
      {/each}
    </div>
  {:else}
    <div class="chuni-userbox-container">
      <ChuniUserplateComponent on:click={() => userboxSelected = "nameplateId"} chuniCharacter={userbox.characterId} chuniLevel={userbox.level.toString()} chuniRating={userbox.playerRating / 100} 
        chuniNameplate={userbox.nameplateId} chuniName={userbox.userName} chuniTrophyName={allItems.trophy[userbox.trophyId].name}></ChuniUserplateComponent>
      <ChuniPenguinComponent chuniWear={userbox.avatarWear} chuniHead={userbox.avatarHead} chuniBack={userbox.avatarBack} 
        chuniFront={userbox.avatarFront} chuniFace={userbox.avatarFace} chuniItem={userbox.avatarItem} 
        chuniSkin={userbox.avatarSkin}></ChuniPenguinComponent>
    </div>
    <div class="chuni-userbox-row">
      {#each avatarKinds as avatarKind}
        {#await DDSreader?.getFile(`avatarAccessoryThumbnail:${userbox[`avatar${avatarKind}`].toString().padStart(8, "0")}`) then imageURL}
          <button on:click={() => userboxSelected = `avatar${avatarKind}`}>
            <img src={imageURL} class={userboxSelected == `avatar${avatarKind}` ? "focused" : ""} alt={allItems.avatarAccessory[userbox[`avatar${avatarKind}`]].name} title={allItems.avatarAccessory[userbox[`avatar${avatarKind}`]].name}>
          </button>
        {/await}
      {/each}
    </div>
    <div class="chuni-userbox">
      {#if userboxSelected == "nameplateId"}
        {#each userItems.find(f => f.ubKey == "nameplateId")?.items ?? [] as item}
          {#await DDSreader?.getFile(`nameplate:${item.itemId.toString().padStart(8, "0")}`) then imageURL}
            <button class="nameplate" on:click={() => {userbox[userboxSelected] = item.itemId; submit(userboxSelected)}}>
              <img src={imageURL} alt={allItems.namePlate[item.itemId].name} title={allItems.namePlate[item.itemId].name}>
            </button>
          {/await}
        {/each}
      {:else}
        {#each userItems.find(f => f.ubKey == userboxSelected)?.items ?? [] as item}
          {#await DDSreader?.getFile(`avatarAccessoryThumbnail:${item.itemId.toString().padStart(8, "0")}`) then imageURL}
            <button on:click={() => {userbox[userboxSelected] = item.itemId; submit(userboxSelected)}}>
              <img src={imageURL} alt={allItems.avatarAccessory[item.itemId].name} title={allItems.avatarAccessory[item.itemId].name}>
            </button>
          {/await}
        {/each}
      {/if}
    </div>
    <div class="fields">
      {#each userItems.filter(i => userboxNewOptions.includes(i.iKey)) as { iKey, ubKey, items }, i}
        <div class="field">
          <label for={ubKey}>{ts(`userbox.${ubKey}`)}</label>
          <div>
            <select bind:value={userbox[ubKey]} id={ubKey} on:change={() => changed = [...changed, ubKey]}>
              {#each items as option}
                <option value={option.itemId}>{allItems[iKey][option.itemId]?.name || `(unknown ${option.itemId})`}</option>
              {/each}
            </select>
            {#if changed.includes(ubKey)}
              <button transition:slide={{axis: "x"}} on:click={() => submit(ubKey)} disabled={!!submitting}>
                {t("settings.profile.save")}
              </button>
            {/if}
          </div>
        </div>
      {/each}
    </div>
  {/if}
  {#if USERBOX_INSTALLED}
    <!-- god this is a mess but idgaf atp -->
    <div class="field boolean" style:margin-top="1em">
      <input type="checkbox" bind:checked={USERBOX_ENABLED.value} id="newUserbox">
      <label for="newUserbox">
        <span class="name">{t("userbox.new.activate")}</span>
        <span class="desc">{t(`userbox.new.activate_desc`)}</span>
      </label>
    </div>
    <div class="field boolean" style:margin-top="1em">
      <input type="checkbox" bind:checked={USERBOX_PROFILE_ENABLED.value} id="newUserboxProfile">
      <label for="newUserboxProfile">
        <span class="name">{t("userbox.new.activate_profile")}</span>
        <span class="desc">{t(`userbox.new.activate_profile_desc`)}</span>
      </label>
    </div>
  {/if}
  {#if USERBOX_SUPPORT}
    <p>
      <button on:click={() => USERBOX_SETUP_RUN = !USERBOX_SETUP_RUN}>{t(!USERBOX_INSTALLED ? `userbox.new.activate_first` : `userbox.new.activate_update`)}</button>
    </p>
  {/if} 
  <!--{#if !USERBOX_SUPPORT || !USERBOX_INSTALLED || !USERBOX_ENABLED.value}
    <h2>{t("userbox.header.preview")}</h2>
    <p class="notice">{t("userbox.preview.notice")}</p>
    <input bind:value={preview} placeholder={t("userbox.preview.url")}/>
    {#if preview}
      <div class="preview">
        {#each userItems.filter(v => v.iKey != 'trophy' && v.iKey != 'systemVoice') as { iKey, ubKey, items }, i}
          <div>
            <span>{ts(`userbox.${ubKey}`)}</span>
            <img src={`${preview}/${iKey}/${userbox[ubKey].toString().padStart(8, '0')}.png`} alt="" on:error={coverNotFound} />
          </div>
        {/each}
      </div>
    {/if}
  {/if}-->
</div>
{/if}
 
{#if USERBOX_SETUP_RUN && !error}
  <div class="overlay" transition:fade>
    <div>
      <h2>{t('userbox.new.name')}</h2>
      <span>{USERBOX_SETUP_MODE ? t('userbox.new.url_warning') : USERBOX_SETUP_TEXT}</span>
      <div class="actions">
        {#if USERBOX_SETUP_MODE}
          <input type="text" on:keyup={userboxHandleInput} class="add-margin" placeholder="Base URL">
        {:else}
          <p class="notice add-margin">
            {t('userbox.new.setup.notice')}
          </p>
          {#if USERBOX_PROGRESS != 0}
            <div class="progress">
              <div class="progress-bar" style="width: {USERBOX_PROGRESS}%"></div>
            </div>
          {:else}
          <button class="drop-btn">
            <input type="file" on:input={userboxSafeDrop} on:click={e => e.preventDefault()}>
            {t('userbox.new.drop')}
          </button>
          {/if}
        {/if}
        <button on:click={() => USERBOX_SETUP_RUN = false}>
          {t('back')}
        </button>
        <button on:click={() => USERBOX_SETUP_MODE = !USERBOX_SETUP_MODE}>
          {t(USERBOX_SETUP_MODE ? 'userbox.new.switch.to_drop' : 'userbox.new.switch.to_url')}
        </button>
      </div>
    </div>
  </div>
{/if}

<style lang="sass">
@use "../../vars"

input
  width: 100%


h2
  margin-bottom: 0.5rem

p.notice
  opacity: 0.6
  margin-top: 0

.progress 
  width: 100%
  height: 10px
  box-shadow: 0 0 1px 1px vars.$ov-lighter
  border-radius: 25px
  margin-bottom: 15px
  overflow: hidden

  .progress-bar
    background: #b3c6ff
    height: 100%
    border-radius: 25px


.add-margin, .drop-btn
  margin-bottom: 1em

.drop-btn
  position: relative
  width: 100%
  aspect-ratio: 3
  background: transparent
  box-shadow: 0 0 1px 1px vars.$ov-lighter

  > input
    position: absolute
    top: 0
    left: 0
    width: 100%
    height: 100%
    opacity: 0

.preview
  margin-top: 32px
  display: flex
  flex-wrap: wrap
  justify-content: space-between
  gap: 32px

  > div
    position: relative
    width: 100px
    height: 100px
    overflow: hidden
    background: vars.$ov-lighter
    border-radius: vars.$border-radius

    span
      position: absolute
      bottom: 0
      width: 100%
      text-align: center
      z-index: 10
      background: rgba(0, 0, 0, 0.2)
      backdrop-filter: blur(2px)

    img
      position: absolute
      inset: 0
      width: 100%
      height: 100%
      object-fit: contain

.fields
  display: flex
  flex-direction: column
  gap: 12px
  width: 100%
  flex-grow: 0

  label
    display: flex
    flex-direction: column

  select
    width: 100%

.field
  display: flex
  flex-direction: column
  width: 100%

  label
    max-width: max-content

  > div:not(.bool)
    display: flex
    align-items: center
    gap: 1rem
    margin-top: 0.5rem

    > select
      flex: 1


.field.boolean
  display: flex
  flex-direction: row
  align-items: center
  gap: 1rem
  width: auto

  input
    width: auto
    aspect-ratio: 1 / 1

  label
    display: flex
    flex-direction: column
    max-width: max-content

    .desc
      opacity: 0.6

/* AquaBox */

.chuni-userbox-row
  width: 100%
  display: flex

  button
    padding: 0
    margin: 0
    width: 100%
    flex: 0 1 100%
    background: none
    aspect-ratio: 1

    img
      width: 100%
      filter: brightness(50%)

      &.focused
        filter: brightness(75%)

.chuni-userbox 
  width: calc(100% - 20px)
  height: 350px
  
  display: flex
  flex-direction: row
  flex-wrap: wrap
  padding: 10px
  background: vars.$c-bg
  border-radius: 16px
  overflow-y: auto
  margin-bottom: 15px
  justify-content: center

  button
    padding: 0
    margin: 0
    width: 20%
    align-self: flex-start
    background: none
    aspect-ratio: 1

    img
      width: 100%

    &.nameplate
      width: 50%
      aspect-ratio: unset
      border: none

.chuni-userbox-container
  display: flex
  align-items: center
  justify-content: center

@media (max-width: 1000px)
  .chuni-userbox-container
    flex-wrap: wrap
</style>
