<!--
    Generates an inline SVG arrow.
-->
<template>
    <svg
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        overflow="visible"
        :viewBox="svgData.viewBox"
    >
        <g
            :stroke-width="svgData.strokeWidth"
            stroke="currentColor"
            fill="none"
            fill-rule="evenodd"
            stroke-linecap="round"
        >
            <polyline :points="svgData.pointer"></polyline>
            <polyline v-if="stemVisible" :points="svgData.stem"></polyline>
        </g>
    </svg>
</template>

<script>
import PxBase from '../bases/PxBase.vue';
import PxBaseResize from '../bases/PxBaseResize.vue';
import { getBoundingBox, rotatePoints } from '../utils/points.js';
import { contain } from '../utils/fit.js';

export default {
    name: 'px-arrow',
    mixins: [PxBase, PxBaseResize],
    props: {
        /**
         * The aspect of the arrow (think of a rectangle that the arrow will fill)
         */
        pointerAspect: { type: [Number, String], default: 2 },

        /**
         * The size of the arrow relative to the outer bounds.
         */
        pointerSize: { type: Number, default: 0.4 },

        /**
         * Whether a stem will be generated
         */
        stemVisible: { type: Boolean, default: true },

        /**
         * The angle in which the arrow is pointing. If a number is provide it will be interpreted
         * in degrees (0 is up, 90 is pointing right, etc.). Alternatively one of the following
         * strings can be provided as the direction in which it points: up, right, down, left.
         */
        angle: { type: [Number, String], default: 0 },

        /**
         * The stroke thickness of the arrow in pixels.
         */
        strokeWidth: { type: Number, default: 1 },

        /**
         * When true, we scale the strokeWidth in the internal SVG to keep it at the specified value
         * regardless of render size
         */
        scaleStrokeWidth: { type: Boolean, default: true },
    },
    data() {
        return {
            elementSize: { width: 100, height: 100 },
        };
    },
    computed: {
        calcAngle() {
            const stringToAngle = {
                up: 0,
                right: 90,
                down: 180,
                left: 270,
            };

            if (typeof this.angle == 'string' && this.angle in stringToAngle) {
                return stringToAngle[this.angle];
            }

            return this.angle;
        },
        svgData() {
            const pointerHeight = this.pointerSize;
            const pointerWidth = this.pointerAspect * pointerHeight;

            // basic arrow (bottom left corner anchored to origin)
            let points = [
                // pointer
                { x: 0, y: pointerHeight },
                { x: pointerWidth / 2, y: 0 },
                { x: pointerWidth, y: pointerHeight },

                // stem
                { x: pointerWidth / 2, y: 0 },
                { x: pointerWidth / 2, y: 1 },
            ];

            // rotate
            points = rotatePoints(points, this.calcAngle);

            // scale by 100 so the viewbox isn't ridiculous
            points = points.map(p => {
                return {
                    x: p.x * 100,
                    y: p.y * 100,
                };
            });

            // get viewbox bounds
            const bounds = getBoundingBox(points);

            // get stroke scale
            const strokeScale = this.scaleStrokeWidth ? contain(this.elementSize, bounds).scale : 1;

            // convert points to a string
            points = points.map(p => {
                return `${p.x} ${p.y}`;
            });

            const svgData = {
                viewBox: [bounds.x, bounds.y, bounds.width, bounds.height]
                    .map(n => Math.round(n))
                    .join(' '),
                pointer: [points[0], points[1], points[2]].join(' '),
                stem: [points[3], points[4]].join(' '),
                strokeWidth: this.strokeWidth * strokeScale,
            };

            return svgData;
        },
    },
    methods: {
        resize() {
            if (this.$el) {
                const r = this.$el.getBoundingClientRect();
                this.elementSize = {
                    width: r.width,
                    height: r.height,
                };
            }
        },
    },
};
</script>

<style lang="scss">
.px-arrow {
    position: relative;
}
</style>
