'use strict';

const { traverse } = require('../lib/xast.js');
const JSAPI = require('../lib/svgo/jsAPI');

exports.name = 'reusePaths';

exports.type = 'full';

exports.active = false;

exports.description =
  'Finds <path> elements with the same d, fill, and ' +
  'stroke, and converts them to <use> elements ' +
  'referencing a single <path> def.';

/**
 * Finds <path> elements with the same d, fill, and stroke, and converts them to
 * <use> elements referencing a single <path> def.
 *
 * @author Jacob Howcroft
 */
exports.fn = function (root) {
  const seen = new Map();
  let count = 0;
  const defs = [];
  traverse(root, (node) => {
    if (
      node.type !== 'element' ||
      node.name !== 'path' ||
      node.attributes.d == null
    ) {
      return;
    }
    const d = node.attributes.d;
    const fill = node.attributes.fill || '';
    const stroke = node.attributes.stroke || '';
    const key = d + ';s:' + stroke + ';f:' + fill;
    const hasSeen = seen.get(key);
    if (!hasSeen) {
      seen.set(key, { elem: node, reused: false });
      return;
    }
    if (!hasSeen.reused) {
      hasSeen.reused = true;
      if (hasSeen.elem.attributes.id == null) {
        hasSeen.elem.attributes.id = 'reuse-' + count++;
      }
      defs.push(hasSeen.elem);
    }
    convertToUse(node, hasSeen.elem.attributes.id);
  });
  if (defs.length > 0) {
    const defsTag = new JSAPI(
      {
        type: 'element',
        name: 'defs',
        attributes: {},
        children: [],
      },
      root
    );
    root.children[0].spliceContent(0, 0, defsTag);
    for (let def of defs) {
      // Remove class and style before copying to avoid circular refs in
      // JSON.stringify. This is fine because we don't actually want class or
      // style information to be copied.
      const style = def.style;
      const defClass = def.class;
      delete def.style;
      delete def.class;
      const defClone = def.clone();
      def.style = style;
      def.class = defClass;
      delete defClone.attributes.transform;
      defsTag.spliceContent(0, 0, defClone);
      // Convert the original def to a use so the first usage isn't duplicated.
      def = convertToUse(def, defClone.attributes.id);
      delete def.attributes.id;
    }
  }
  return root;
};

/** */
function convertToUse(item, href) {
  item.renameElem('use');
  delete item.attributes.d;
  delete item.attributes.stroke;
  delete item.attributes.fill;
  item.attributes['xlink:href'] = '#' + href;
  delete item.pathJS;
  return item;
}
