/*
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
'use strict';

import {
  configs
} from '/common/common.js';
import * as Color from './color.js';
import * as Constants from './constants.js';

export function generateThemeRules(theme) {
  const rules = [];
  const generateCustomRule = (theme, prefix = '') => {
    for (const key of Object.keys(theme)) {
      if (!theme[key])
        continue;
      const propertyKey = prefix ? `${prefix}-${key}` : key;
      let value = theme[key];
      switch (typeof theme[key]) {
        case 'object':
          generateCustomRule(value, propertyKey);
          break;
        case 'string':
          if (/^[^:]+:\/\//.test(value))
            value = `url(${JSON.stringify(value)})`;
          rules.push(`--theme-${propertyKey}: ${value};`);
          for (let alpha = 10; alpha < 100; alpha += 10) {
            rules.push(`--theme-${propertyKey}-${alpha}: ${Color.overrideCSSAlpha(value, alpha / 100)};`);
          }
          break;
      }
    }
  };
  generateCustomRule(theme);
  return rules.join('\n');
}

export async function generateThemeDeclarations(theme) {
  if (!theme ||
      !theme.colors) {
    return `
      :root {
        /* https://searchfox.org/mozilla-central/rev/0c7c41109902cb8967ec3ef2c0ddb326701cfbee/browser/themes/windows/browser.css#15 */
        /* https://searchfox.org/mozilla-central/rev/0c7c41109902cb8967ec3ef2c0ddb326701cfbee/browser/themes/linux/browser.css#20 */
        --non-lwt-selected-tab-background-color-proton: rgba(255, 255, 255, 0.15);
    }`;
  }

  const extraColors = [];
  const themeFrameColor   = theme.colors.frame || theme.colors.accentcolor /* old name */;
  const inactiveTextColor = theme.colors.tab_background_text || theme.colors.textcolor /* old name */;
  const activeTextColor   = theme.colors.tab_text || theme.colors.bookmark_text || theme.colors.toolbar_text /* old name */ || inactiveTextColor;
  let bgAlpha = 1;
  let hasImage = false;
  if (theme.images) {
    const isRightside = configs.sidebarPosition == Constants.kTABBAR_POSITION_RIGHT;
    const images    = [];
    const frameImage = theme.images.theme_frame || theme.images.headerURL /* old name */;
    if (frameImage) {
      hasImage = true;
      // https://searchfox.org/mozilla-central/rev/532e4b94b9e807d157ba8e55034aef05c1196dc9/browser/themes/shared/tabs.inc.css#537
      extraColors.push('--browser-bg-hover-for-header-image: rgba(0, 0, 0, 0.1);');
      // https://searchfox.org/mozilla-central/rev/532e4b94b9e807d157ba8e55034aef05c1196dc9/browser/base/content/browser.css#20
      extraColors.push('--browser-bg-active-for-header-image: rgba(255, 255, 255, 0.4)');
      // https://searchfox.org/mozilla-central/rev/532e4b94b9e807d157ba8e55034aef05c1196dc9/toolkit/themes/windows/global/global.css#138
      if (Color.isBrightColor(inactiveTextColor)) {
        // for bright text
        extraColors.push('--browser-textshadow-for-header-image: 1px 1px 1.5px black');
      }
      else {
        // for dark text
        extraColors.push('--browser-textshadow-for-header-image: 0 0 1.5px white');
      }
      images.push({
        url:      frameImage,
        position: isRightside ? 'top right' : 'top left',
        repeat:   'no-repeat',
      });
    }

    const positions = theme.properties?.additional_backgrounds_alignment || [];
    const repeats = theme.properties?.additional_backgrounds_tiling || [];
    if (Array.isArray(theme.images.additional_backgrounds) &&
        theme.images.additional_backgrounds.length > 0) {
      const leftImageCount = positions.filter(position => position.includes('left')).length;
      const rightImageCount = positions.filter(position => position.includes('right')).length;
      const repeatableImageCount = repeats.filter(repeat => repeat != 'no-repeat').length;
      for (let i = 0, maxi = theme.images.additional_backgrounds.length; i < maxi; i++) {
        const image = theme.images.additional_backgrounds[i];
        const position = positions.length > 0 && positions[Math.min(i, positions.length - 1)] || 'default';
        const repeat = repeats.length > 0 && repeats[Math.min(i, repeats.length - 1)] || 'default';
        if (repeatableImageCount > 0 &&
            repeat.includes('no-repeat'))
          continue;
        if (position &&
            position.includes('right') != isRightside &&
            repeat == 'no-repeat' &&
            leftImageCount > 0 &&
            rightImageCount > 0)
          continue;
        images.push({
          url: image,
          position,
          repeat,
          size: repeat == 'reepat-y' ? 'auto' : 'auto 100%',
        });
      }
      bgAlpha = 0.75;
      hasImage = true;
    }

    await Promise.all(images.map(async image => {
      if (image.size)
        return;

      const loader = new Image();
      try {
        const shouldRepeat = (
          theme.properties &&
          Array.isArray(theme.properties.additional_backgrounds_tiling) &&
          theme.properties.additional_backgrounds_tiling.some(value => value == 'repeat' || value == 'repeat-y')
        );
        const shouldNoRepeat = (
          !theme.properties ||
          !Array.isArray(theme.properties.additional_backgrounds_tiling) ||
          theme.properties.additional_backgrounds_tiling.some(value => value == 'no-repeat')
        );
        let maybeRepeatable = false;
        if (!shouldRepeat && !shouldNoRepeat) {
          await new Promise((resolve, reject) => {
            loader.addEventListener('load', resolve);
            loader.addEventListener('error', reject);
            loader.src = image;
          });
          maybeRepeatable = (loader.width / Math.max(1, loader.height)) <= configs.unrepeatableBGImageAspectRatio;
        }
        if (shouldNoRepeat)
          image.size = 'cover';
        else if (shouldRepeat || maybeRepeatable)
          image.size = 'auto';
      }
      catch(error) {
        console.error(error);
      }
    }));

    if (hasImage) {
      extraColors.push('--browser-bg-images: ' + images.map(image => `url(${JSON.stringify(image.url)})`).join(','));
      extraColors.push('--browser-bg-position: ' + images.map(image => image.position).join(','));
      extraColors.push('--browser-bg-repeat: ' + images.map(image => image.repeat).join(','));
      extraColors.push('--browser-bg-size: ' + images.map(image => image.size).join(','));
    }
  }
  const themeBaseColor    = Color.overrideCSSAlpha(themeFrameColor, bgAlpha);
  let toolbarColor = Color.mixCSSColors(themeBaseColor, 'rgba(255, 255, 255, 0.4)', bgAlpha);
  if (theme.colors.toolbar) {
    if (hasImage) {
      extraColors.push(`--browser-bg-for-header-image: ${theme.colors.toolbar};`);
      toolbarColor = theme.colors.toolbar;
    }
    else {
      toolbarColor = Color.mixCSSColors(themeBaseColor, theme.colors.toolbar);
    }
    extraColors.push(`--browser-toolbar: ${theme.colors.toolbar}`);
    if (Color.isParsable(theme.colors.toolbar) &&
        Color.isParsable(theme.colors.toolbar_text)) {
      const halfTransparentTextColor = Color.mixCSSColors(theme.colors.toolbar_text, theme.colors.toolbar_text, 0.5);
      extraColors.push(`--browser-toolbar_text-darker: ${Color.mixCSSColors(theme.colors.toolbar, halfTransparentTextColor)}`);
    }
  }
  else if (hasImage) {
    extraColors.push('--browser-bg-for-header-image: rgba(255, 255, 255, 0.25);');
  }
  if (theme.colors.tab_line)
    extraColors.push(`--browser-tab-highlighter: ${theme.colors.tab_line}`);
  if (theme.colors.tab_loading)
    extraColors.push(`--browser-loading-indicator: ${theme.colors.tab_loading}`);
  if (theme.colors.tab_selected)
    extraColors.push(`--browser-selected-tab-bg: ${theme.colors.tab_selected}`);
  extraColors.push(generateThemeRules(theme));
  return `
    :root {
      --browser-background:      ${themeFrameColor};
      --browser-bg-base:         ${toolbarColor};
      --browser-bg-less-lighter: ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.05)', bgAlpha)};
      --browser-bg-lighter:      ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.1)', bgAlpha)};
      --browser-bg-more-lighter: ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.25)', bgAlpha)};
      --browser-bg-lightest:     ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.4)', bgAlpha)};
      --browser-bg-less-darker:  ${Color.mixCSSColors(toolbarColor, 'rgba(0, 0, 0, 0.1)', bgAlpha)};
      --browser-bg-darker:       ${Color.mixCSSColors(toolbarColor, 'rgba(0, 0, 0, 0.25)', bgAlpha)};
      --browser-bg-more-darker:  ${Color.mixCSSColors(toolbarColor, 'rgba(0, 0, 0, 0.5)', bgAlpha)};
      --browser-fg:              ${inactiveTextColor};
      --browser-fg-active:       ${activeTextColor};
      --browser-border:          ${Color.overrideCSSAlpha(inactiveTextColor, 0.4)};
      ${extraColors.join(';\n')}
    }
  `;
}
