// The goal of this script is to have a shape ready for use with the
// "Shape on Image" feature of Odoo.
// Therefor we need to rearrange the file a little.
// Marks which axis each parameter of a command belongs to, as well as whether
// It's a positional measurement (x/y), a distance (dx/dy) or none (angles, flags)
const commandAxes = {
    'M': ['x', 'y'],
    'm': ['dx', 'dy'],
    'L': ['x', 'y'],
    'l': ['dx', 'dy'],
    'H': ['x'],
    'h': ['dx'],
    'V': ['y'],
    'v': ['dy'],
    'Z': [],
    'z': [],
    'C': ['x', 'y', 'x', 'y', 'x', 'y'],
    'c': ['dx', 'dy', 'dx', 'dy', 'dx', 'dy'],
    'S': ['x', 'y', 'x', 'y'],
    's': ['dx', 'dy', 'dx', 'dy'],
    'Q': ['x', 'y', 'x', 'y'],
    'q': ['dx', 'dy', 'dx', 'dy'],
    'T': ['x', 'y'],
    't': ['dx', 'dy'],
    'A': ['dx', 'dy', 'none', 'none', 'none', 'x', 'y'],
    'a': ['dx', 'dy', 'none', 'none', 'none', 'dx', 'dy'],
};

const toUserSpace = (x, y, width, height, precision = 4) => ({
    x: val => +((parseFloat(val) - x) / width).toFixed(precision),
    dx: val => +(parseFloat(val) / width).toFixed(precision),
    y: val => +((parseFloat(val) - y) / height).toFixed(precision),
    dy: val => +(parseFloat(val) / height).toFixed(precision),
    none: val => val,
});

const filePicker = document.getElementById('svgPicker');
const submitButton = document.getElementById('submitButton');
submitButton.addEventListener('click', async (ev) => {
    if (!filePicker.files.length > 0) {
        alert('Please select files using the file picker first');
        return;
    }
    Array.from(filePicker.files).forEach(async file => {
        const fileReader = new FileReader();
        const readerPromise = new Promise((resolve, reject) => {
            fileReader.addEventListener('load', () => resolve(fileReader.result));
            fileReader.addEventListener('error', () => reject(fileReader.error));
        });
        fileReader.readAsText(file, 'utf-8');
        const svgString = await readerPromise;
        const parser = new DOMParser();
        const svg = parser.parseFromString(svgString, 'image/svg+xml');
        const path = svg.getElementById('filterPath');
        const svgDocumentElement = svg.documentElement;
        // Some SVGs come without xlink
        svgDocumentElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
        // We add the SVG to the body so we can take measurements of its
        // original size
        document.body.appendChild(svg.documentElement);
        const { x, y, width, height } = svgDocumentElement.getBBox();
        const scalers = toUserSpace(x, y, width, height);

        // Converts the clipPath in values between 0 and 1 so we can use
        // object bounding box as clip path units. It will make the clip path
        // always adapt to the size of the picture.
        const commands = path.getAttribute('d').match(/[a-z][^a-z]*/ig).map(c => {
            return c.split(/[, ]|(?=-)|(?<=[a-z])(?=[0-9])/i).filter(part => !!part.length);
        });
        const relSpaceCommands = commands.map(([command, ...nums]) => {
            const axes = commandAxes[command];
            const relSpaceNums = nums.map((n, i) => {
                const scaler = scalers[axes[i % axes.length]];
                return scaler(n);
            });
            return `${command}${relSpaceNums.join(',')}`.replace(/,-/g, '-');
        });
        path.setAttribute('d', relSpaceCommands.join(''));
        path.removeAttribute('fill');
        svgDocumentElement.removeAttribute('viewBox');

        let defsEl = svgDocumentElement.querySelector('defs');
        if (!defsEl) {
            defsEl = svg.createElementNS('http://www.w3.org/2000/svg', 'defs');
            svgDocumentElement.appendChild(defsEl);
        }

        let clipPathEl = svgDocumentElement.querySelector('clipPath');
        if (!clipPathEl) {
            clipPathEl = svg.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
            clipPathEl.setAttribute('id', 'clip-path');
            defsEl.appendChild(clipPathEl);
        }

        clipPathEl.setAttribute('clipPathUnits', 'objectBoundingBox');
        const backgroundEls = svgDocumentElement.getElementsByClassName('background');
        // We set the BG elements into their own svg so that when the total
        // space gets stretched out, so does the backgrounds elements
        Array.from(backgroundEls).forEach(el => {
            const bgBbox = el.getBBox();
            const svgBackground = document.createElement('svg');
            const strokeWidth = el.getAttribute('stroke-width');
            // If the background has a strokeWidth, the viewBox need to take it
            // into account
            if (strokeWidth) {
                const adj = parseFloat(strokeWidth) / 2;
                svgBackground.setAttributeNS('http://www.w3.org/2000/svg', 'viewBox',
                `${bgBbox.x - adj} ${bgBbox.y - adj} ${bgBbox.width + (adj * 2)} ${bgBbox.height + (adj * 2)}`);
            } else {
                svgBackground.setAttributeNS('http://www.w3.org/2000/svg', 'viewBox', `${bgBbox.x} ${bgBbox.y} ${bgBbox.width} ${bgBbox.height}`);
            }
            svgBackground.setAttributeNS('http://www.w3.org/2000/svg', 'preserveAspectRatio', 'none');
            svgBackground.appendChild(el);
            svgDocumentElement.appendChild(svgBackground);
        });

        defsEl.appendChild(path);
        // Setting the clip path for use and for preview
        const useClipPathEl = document.createElementNS('http://www.w3.org/2000/svg', 'use');
        useClipPathEl.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#filterPath');
        useClipPathEl.setAttribute('fill', 'none');
        clipPathEl.appendChild(useClipPathEl);

        const svgPreviewEl = svg.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svgPreviewEl.setAttributeNS('http://www.w3.org/2000/svg', 'viewBox', '0 0 1 1');
        svgPreviewEl.setAttribute('width', '800');
        svgPreviewEl.setAttribute('height', '600');
        svgPreviewEl.setAttribute('id', 'preview');
        svgPreviewEl.setAttributeNS('http://www.w3.org/2000/svg', 'preserveAspectRatio', 'none');
        const previewUseEl = useClipPathEl.cloneNode(true);
        previewUseEl.setAttribute('fill', 'darkgrey');
        svgPreviewEl.appendChild(previewUseEl);
        svgDocumentElement.appendChild(svgPreviewEl);

        const imageEl = document.createElement('image');
        imageEl.setAttribute('xlink:href', '');
        imageEl.setAttribute('clip-path', 'url(#clip-path)');
        svgDocumentElement.appendChild(imageEl);
        // Give a default size to the SVGs for an easier preview on disk
        svgDocumentElement.setAttribute('width', '800');
        svgDocumentElement.setAttribute('height', '600');

        const outFile = new File([svgDocumentElement.outerHTML], filePicker.files[0].name, { type: 'image/svg+xml' });
        const outFileReader = new FileReader();
        const outReaderPromise = new Promise((resolve, reject) => {
            outFileReader.addEventListener('load', () => resolve(outFileReader.result));
            outFileReader.addEventListener('error', () => reject(outFileReader.error));
        });
        outFileReader.readAsDataURL(outFile);
        const dataURL = await outReaderPromise;

        const downloadLinkEl = document.createElement('a');
        downloadLinkEl.href = dataURL;
        downloadLinkEl.innerText = 'Download';
        downloadLinkEl.setAttribute('download', file.name);
        downloadLinkEl.classList.add('dl_link');
        document.getElementById('downloadArea').appendChild(downloadLinkEl);
    });
});
