"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.parseImage = exports.default = void 0;
var _plugin = _interopRequireDefault(require("../../plugin"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** @module */

const escape = target => target.replace(/[\\;:()]/g, matched => `\\${matched[0].codePointAt(0).toString(16)} `);
const optionMatchers = new Map();

// The scale percentage for resize background
optionMatchers.set(/^(\d*\.)?\d+%$/, matches => ({
  size: matches[0]
}));

// width and height
const normalizeLength = v => `${v}${/^(\d*\.)?\d+$/.test(v) ? 'px' : ''}`;
optionMatchers.set(/^w(?:idth)?:((?:\d*\.)?\d+(?:%|ch|cm|em|ex|in|mm|pc|pt|px)?|auto)$/, matches => ({
  width: normalizeLength(matches[1])
}));
optionMatchers.set(/^h(?:eight)?:((?:\d*\.)?\d+(?:%|ch|cm|em|ex|in|mm|pc|pt|px)?|auto)$/, matches => ({
  height: normalizeLength(matches[1])
}));

// CSS filters
optionMatchers.set(/^blur(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['blur', escape(matches[1] || '10px')]]
}));
optionMatchers.set(/^brightness(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['brightness', escape(matches[1] || '1.5')]]
}));
optionMatchers.set(/^contrast(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['contrast', escape(matches[1] || '2')]]
}));
optionMatchers.set(/^drop-shadow(?::(.+?),(.+?)(?:,(.+?))?(?:,(.+?))?)?$/, (matches, meta) => {
  const args = [];
  for (const arg of matches.slice(1)) {
    if (arg) {
      const colorFunc = arg.match(/^(rgba?|hsla?|hwb|(?:ok)?(?:lab|lch)|color)\((.*)\)$/);
      args.push(colorFunc ? `${colorFunc[1]}(${escape(colorFunc[2])})` : escape(arg));
    }
  }
  return {
    filters: [...meta.filters, ['drop-shadow', args.join(' ') || '0 5px 10px rgba(0,0,0,.4)']]
  };
});
optionMatchers.set(/^grayscale(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['grayscale', escape(matches[1] || '1')]]
}));
optionMatchers.set(/^hue-rotate(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['hue-rotate', escape(matches[1] || '180deg')]]
}));
optionMatchers.set(/^invert(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['invert', escape(matches[1] || '1')]]
}));
optionMatchers.set(/^opacity(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['opacity', escape(matches[1] || '.5')]]
}));
optionMatchers.set(/^saturate(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['saturate', escape(matches[1] || '2')]]
}));
optionMatchers.set(/^sepia(?::(.+))?$/, (matches, meta) => ({
  filters: [...meta.filters, ['sepia', escape(matches[1] || '1')]]
}));

/**
 * Marpit image parse plugin.
 *
 * Parse image tokens and store the result into `marpitImage` meta. It has an
 * image url and options. The alternative text is regarded as space-separated
 * options.
 *
 * @function parseImage
 * @param {MarkdownIt} md markdown-it instance.
 */
function _parseImage(md) {
  const {
    process
  } = md.core;
  let refCount = 0;
  const finalizeTokenAttr = (token, state) => {
    // Apply finalization recursively to inline tokens
    if (token.type === 'inline') {
      for (const t of token.children) finalizeTokenAttr(t, state);
    }

    // Re-generate the alt text of image token to remove Marpit specific options
    if (token.type === 'image' && token.meta && token.meta.marpitImage) {
      let updatedAlt = '';
      let hasConsumed = false;
      for (const opt of token.meta.marpitImage.options) {
        if (opt.consumed) {
          hasConsumed = true;
        } else {
          updatedAlt += opt.leading + opt.content;
        }
      }
      if (hasConsumed) {
        let newTokens = [];
        md.inline.parse(updatedAlt.trimStart(), state.md, state.env, newTokens);
        token.children = newTokens;
      }
    }
  };
  md.core.process = state => {
    try {
      refCount += 1;
      return process.call(md.core, state);
    } finally {
      refCount -= 1;
      if (refCount === 0) {
        // Apply finalization for every tokens
        for (const token of state.tokens) finalizeTokenAttr(token, state);
      }
    }
  };
  md.inline.ruler2.push('marpit_parse_image', ({
    tokens
  }) => {
    for (const token of tokens) {
      if (token.type === 'image') {
        // Parse alt text as options
        const optsBase = token.content.split(/(\s+)/);
        let currentIdx = 0;
        let leading = '';
        const options = optsBase.reduce((acc, opt, i) => {
          if (i % 2 === 0 && opt.length > 0) {
            currentIdx += leading.length;
            acc.push({
              content: opt,
              index: currentIdx,
              leading,
              consumed: false
            });
            leading = '';
            currentIdx += opt.length;
          } else {
            leading += opt;
          }
          return acc;
        }, []);
        const url = token.attrGet('src');
        token.meta = token.meta || {};
        token.meta.marpitImage = {
          ...(token.meta.marpitImage || {}),
          url: url.toString(),
          options
        };

        // Parse keyword through matchers
        for (const opt of options) {
          for (const [regexp, mergeFunc] of optionMatchers) {
            if (opt.consumed) continue;
            const matched = opt.content.match(regexp);
            if (matched) {
              opt.consumed = true;
              token.meta.marpitImage = {
                ...token.meta.marpitImage,
                ...mergeFunc(matched, {
                  filters: [],
                  ...token.meta.marpitImage
                })
              };
            }
          }
        }
      }
    }
  });
}
const parseImage = exports.parseImage = (0, _plugin.default)(_parseImage);
var _default = exports.default = parseImage;