import { getFileName, isImage, isVideo, SEARCH_TYPE, keyPressed } from '../scripts/utils.js';
import { TVA_CONFIG } from '../scripts/settings.js';
import FlagsConfig from './flagsConfig.js';
import { doImageSearch } from '../scripts/search.js';
import UserList from './userList.js';

export async function renderTileHUD(hud, html, tileData, searchText = '', fp_files = null) {
  const tile = hud.object;
  const hudSettings = TVA_CONFIG.hud;

  if (!hudSettings.enableSideMenu || !TVA_CONFIG.tilesEnabled) return;

  const button = $(`
  <div class="control-icon" data-action="token-variants-side-selector">
    <img
      id="token-variants-side-button"
      src="modules/token-variants/img/token-images.svg"
      width="36"
      height="36"
      title="${game.i18n.localize('token-variants.windows.art-select.select-variant')}"
    />
  </div>
`);

  html.find('div.right').last().append(button);
  html.find('div.right').click(_deactivateTokenVariantsSideSelector);

  button.click((event) => _onButtonClick(event, tile));
  button.contextmenu((event) => _onButtonRightClick(event, tile));
}

async function _onButtonClick(event, tile) {
  if (keyPressed('config')) {
    setNameDialog(tile);
    return;
  }

  const button = $(event.target).closest('.control-icon');

  // De-activate 'Status Effects'
  button.closest('div.right').find('div.control-icon.effects').removeClass('active');
  button.closest('div.right').find('.status-effects').removeClass('active');

  // Remove contextmenu
  button.find('.contextmenu').remove();

  // Toggle variants side menu
  button.toggleClass('active');
  let variantsWrap = button.find('.token-variants-wrap');
  if (button.hasClass('active')) {
    if (!variantsWrap.length) {
      variantsWrap = await renderSideSelect(tile);
      if (variantsWrap) button.find('img').after(variantsWrap);
      else return;
    }
    variantsWrap.addClass('active');
  } else {
    variantsWrap.removeClass('active');
  }
}

function _onButtonRightClick(event, tile) {
  // Display side menu if button is not active yet
  const button = $(event.target).closest('.control-icon');
  if (!button.hasClass('active')) {
    // button.trigger('click');
    button.addClass('active');
  }

  if (button.find('.contextmenu').length) {
    // Contextmenu already displayed. Remove and activate images
    button.find('.contextmenu').remove();
    button.removeClass('active').trigger('click');
    //button.find('.token-variants-wrap.images').addClass('active');
  } else {
    // Contextmenu is not displayed. Hide images, create it and add it
    button.find('.token-variants-wrap.images').removeClass('active');
    const contextMenu = $(`
    <div class="token-variants-wrap contextmenu active">
      <div class="token-variants-context-menu active">
        <input class="token-variants-side-search" type="text" />
        <button class="flags" type="button"><i class="fab fa-font-awesome-flag"></i><label>Flags</label></button>
        <button class="file-picker" type="button"><i class="fas fa-file-import fa-fw"></i><label>Browse Folders</label></button>
      </div>
    </div>
      `);
    button.append(contextMenu);

    // Register contextmenu listeners
    contextMenu
      .find('.token-variants-side-search')
      .on('keydown', (event) => _onImageSearchKeyUp(event, tile))
      .on('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
      });
    contextMenu.find('.flags').click((event) => {
      event.preventDefault();
      event.stopPropagation();
      new FlagsConfig(tile).render(true);
    });
    contextMenu.find('.file-picker').click(async (event) => {
      event.preventDefault();
      event.stopPropagation();
      new FilePicker({
        type: 'folder',
        callback: async (path, fp) => {
          const content = await FilePicker.browse(fp.activeSource, fp.result.target);
          let files = content.files.filter((f) => isImage(f) || isVideo(f));
          if (files.length) {
            button.find('.token-variants-wrap').remove();
            const sideSelect = await renderSideSelect(tile, null, files);
            if (sideSelect) {
              sideSelect.addClass('active');
              button.append(sideSelect);
            }
          }
        },
      }).render(true);
    });
  }
}

function _deactivateTokenVariantsSideSelector(event) {
  const controlIcon = $(event.target).closest('.control-icon');
  const dataAction = controlIcon.attr('data-action');

  switch (dataAction) {
    case 'effects':
      break; // Effects button
    case 'thwildcard-selector':
      break; // Token HUD Wildcard module button
    default:
      return;
  }

  $(event.target)
    .closest('div.right')
    .find('.control-icon[data-action="token-variants-side-selector"]')
    .removeClass('active');
  $(event.target).closest('div.right').find('.token-variants-wrap').removeClass('active');
}

async function renderSideSelect(tile, searchText = null, fp_files = null) {
  const hudSettings = TVA_CONFIG.hud;
  const worldHudSettings = TVA_CONFIG.worldHud;
  const FULL_ACCESS = TVA_CONFIG.permissions.hudFullAccess[game.user.role];
  let images = [];
  let variants = [];
  let imageDuplicates = new Set();
  const pushImage = (img) => {
    if (imageDuplicates.has(img.path)) {
      if (!images.find((obj) => obj.path === img.path && obj.name === img.name)) {
        images.push(img);
      }
    } else {
      images.push(img);
      imageDuplicates.add(img.path);
    }
  };

  if (!fp_files) {
    if (searchText !== null && !searchText) return;

    if (!searchText) {
      variants = tile.document.getFlag('token-variants', 'variants') || [];
      variants.forEach((variant) => {
        for (const name of variant.names) {
          pushImage({ path: variant.imgSrc, name: name });
        }
      });

      // Parse directory flag and include the images
      const directoryFlag = tile.document.getFlag('token-variants', 'directory');
      if (directoryFlag) {
        let dirFlagImages;
        try {
          let path = directoryFlag.path;
          let source = directoryFlag.source;
          let bucket = '';
          if (source.startsWith('s3:')) {
            bucket = source.substring(3, source.length);
            source = 's3';
          }
          const content = await FilePicker.browse(source, path, {
            type: 'imagevideo',
            bucket,
          });
          dirFlagImages = content.files;
        } catch (err) {
          dirFlagImages = [];
        }
        dirFlagImages.forEach((f) => {
          if (isImage(f) || isVideo(f)) pushImage({ path: f, name: getFileName(f) });
        });
      }
    }

    // Perform the search if needed
    const search = searchText ?? tile.document.getFlag('token-variants', 'tileName');
    const noSearch = !search || (!searchText && worldHudSettings.displayOnlySharedImages);
    let artSearch = noSearch
      ? null
      : await doImageSearch(search, {
          searchType: SEARCH_TYPE.TILE,
          searchOptions: { keywordSearch: worldHudSettings.includeKeywords },
        });

    if (artSearch) {
      artSearch.forEach((results) => {
        images.push(...results);
      });
    }
  } else {
    images = fp_files.map((f) => {
      return { path: f, name: getFileName(f) };
    });
  }

  // Retrieving the possibly custom name attached as a flag to the token
  let tileImageName = tile.document.getFlag('token-variants', 'name');
  if (!tileImageName) {
    tileImageName = getFileName(tile.document.texture.src);
  }

  let imagesParsed = [];

  for (const imageObj of images) {
    const img = isImage(imageObj.path);
    const vid = isVideo(imageObj.path);

    let shared = false;
    if (game.user.isGM) {
      variants.forEach((variant) => {
        if (variant.imgSrc === imageObj.path && variant.names.includes(imageObj.name)) {
          shared = true;
        }
      });
    }

    const userMappings = tile.document.getFlag('token-variants', 'userMappings') || {};
    const [title, style] = genTitleAndStyle(userMappings, imageObj.path, imageObj.name);

    imagesParsed.push({
      route: imageObj.path,
      name: imageObj.name,
      used: imageObj.path === tile.document.texture.src && imageObj.name === tileImageName,
      img,
      vid,
      unknownType: !img && !vid,
      shared: shared,
      hasConfig: false, //hasConfig,
      title: title,
      style: game.user.isGM && style ? 'box-shadow: ' + style + ';' : null,
    });
  }

  //
  // Render
  //
  const imageDisplay = hudSettings.displayAsImage;
  const imageOpacity = hudSettings.imageOpacity / 100;

  const sideSelect = $(
    await renderTemplate('modules/token-variants/templates/sideSelect.html', {
      imagesParsed,
      imageDisplay,
      imageOpacity,
      autoplay: !TVA_CONFIG.playVideoOnHover,
    })
  );

  // Activate listeners
  sideSelect.find('video').hover(
    function () {
      if (TVA_CONFIG.playVideoOnHover) {
        this.play();
        $(this).siblings('.fa-play').hide();
      }
    },
    function () {
      if (TVA_CONFIG.pauseVideoOnHoverOut) {
        this.pause();
        this.currentTime = 0;
        $(this).siblings('.fa-play').show();
      }
    }
  );
  sideSelect.find('.token-variants-button-select').click((event) => _onImageClick(event, tile));

  if (FULL_ACCESS) {
    sideSelect
      .find('.token-variants-button-select')
      .on('contextmenu', (event) => _onImageRightClick(event, tile));
  }

  return sideSelect;
}

async function _onImageClick(event, tile) {
  event.preventDefault();
  event.stopPropagation();

  if (!tile) return;

  const imgButton = $(event.target).closest('.token-variants-button-select');
  const imgSrc = imgButton.attr('data-name');
  const name = imgButton.attr('data-filename');
  if (imgSrc) {
    canvas.tiles.hud.clear();
    await tile.document.update({ img: imgSrc });
    try {
      if (getFileName(imgSrc) !== name) await tile.document.setFlag('token-variants', 'name', name);
    } catch (e) {}
  }
}

async function _onImageRightClick(event, tile) {
  event.preventDefault();
  event.stopPropagation();

  if (!tile) return;

  const imgButton = $(event.target).closest('.token-variants-button-select');
  const imgSrc = imgButton.attr('data-name');
  const name = imgButton.attr('data-filename');

  if (!imgSrc || !name) return;

  if (keyPressed('config') && game.user.isGM) {
    const regenStyle = (tile, img) => {
      const mappings = tile.document.getFlag('token-variants', 'userMappings') || {};
      const name = imgButton.attr('data-filename');
      const [title, style] = genTitleAndStyle(mappings, img, name);
      imgButton
        .closest('.token-variants-wrap')
        .find(`.token-variants-button-select[data-name='${img}']`)
        .css('box-shadow', style)
        .prop('title', title);
    };
    new UserList(tile, imgSrc, regenStyle).render(true);
    return;
  }

  let variants = tile.document.getFlag('token-variants', 'variants') || [];

  // Remove selected variant if present in the flag, add otherwise
  let del = false;
  let updated = false;
  for (let variant of variants) {
    if (variant.imgSrc === imgSrc) {
      let fNames = variant.names.filter((name) => name !== name);
      if (fNames.length === 0) {
        del = true;
      } else if (fNames.length === variant.names.length) {
        fNames.push(name);
      }
      variant.names = fNames;
      updated = true;
      break;
    }
  }
  if (del) variants = variants.filter((variant) => variant.imgSrc !== imgSrc);
  else if (!updated) variants.push({ imgSrc: imgSrc, names: [name] });

  // Set shared variants as a flag
  tile.document.unsetFlag('token-variants', 'variants');
  if (variants.length > 0) {
    tile.document.setFlag('token-variants', 'variants', variants);
  }
  imgButton.find('.fa-share').toggleClass('active'); // Display green arrow
}

async function _onImageSearchKeyUp(event, tile) {
  if (event.key === 'Enter' || event.keyCode === 13) {
    event.preventDefault();
    if (event.target.value.length >= 3) {
      const button = $(event.target).closest('.control-icon');
      button.find('.token-variants-wrap').remove();
      const sideSelect = await renderSideSelect(tile, event.target.value);
      if (sideSelect) {
        sideSelect.addClass('active');
        button.append(sideSelect);
      }
    }
    return false;
  }
}

function genTitleAndStyle(mappings, imgSrc, name) {
  let title = TVA_CONFIG.worldHud.showFullPath ? imgSrc : name;
  let style = '';
  let offset = 2;
  for (const [userId, img] of Object.entries(mappings)) {
    if (img === imgSrc) {
      const user = game.users.get(userId);
      if (!user) continue;
      if (style.length === 0) {
        style = `inset 0 0 0 ${offset}px ${user.color}`;
      } else {
        style += `, inset 0 0 0 ${offset}px ${user.color}`;
      }
      offset += 2;
      title += `\nDisplayed to: ${user.name}`;
    }
  }
  return [title, style];
}

function setNameDialog(tile) {
  const tileName = tile.document.getFlag('token-variants', 'tileName') || tile.id;
  new Dialog({
    title: `Assign a name to the Tile (3+ chars)`,
    content: `<table style="width:100%"><tr><th style="width:50%"><label>Tile Name</label></th><td style="width:50%"><input type="text" name="input" value="${tileName}"/></td></tr></table>`,
    buttons: {
      Ok: {
        label: `Save`,
        callback: (html) => {
          const name = html.find('input').val();
          if (name) {
            canvas.tiles.hud.clear();
            tile.document.setFlag('token-variants', 'tileName', name);
          }
        },
      },
    },
  }).render(true);
}
