import { assert } from "../../../core/assert.js";
import { isValueBetween } from "../../../core/math/interval/isValueBetween.js";
import { PI2 } from "../../../core/math/PI2.js";
import SVG, { svgCircularPath } from "../../SVG.js";
import View from "../../View.js";

let idCount = 0;

function generateCurveId() {
    const r = `radial-text-curve${idCount}`;

    idCount++;

    return r;
}

let idShadowFilter = 0;

/**
 * NOTE: adapted from https://www.w3.org/People/Dean/svg/texteffects/shadow.svg
 * @param {number} dx
 * @param {number} dy
 * @param {number} blurX
 * @param {number} blurY
 * @returns {{el: Element, id: string}}
 */
function createShadowFilter(dx, dy, blurX, blurY) {
    const filter = SVG.createElement('filter');

    const id = `drop-shadow-filter-${idShadowFilter++}`;
    filter.setAttribute('id', id);
    filter.setAttribute('x', '-20%');
    filter.setAttribute('y', '-20%');
    filter.setAttribute('width', '140%');
    filter.setAttribute('height', '140%');

    const feGaussianBlur = SVG.createElement('feGaussianBlur');

    feGaussianBlur.setAttribute('stdDeviation', `${blurX} ${blurY}`);
    feGaussianBlur.setAttribute('result', 'shadow');

    filter.appendChild(feGaussianBlur);

    if (dx !== 0 || dy !== 0) {
        const feOffset = SVG.createElement('feOffset');

        filter.appendChild(feOffset);
    }

    return {
        el: filter,
        id
    };
}


export class RadialText extends View {
    /**
     *
     * @param {number} share
     * @param {number} offset
     * @param {number} radius
     * @param {String|number} [fill] CSS color for the text
     * @param {boolean} [useShadow=true]
     * @param {boolean} [crop=true] If enabled - will crop the text to make sure it fits in the given section
     */
    constructor({
                    share = 1,
                    offset = 0,
                    radius = 100,
                    fill = 'black',
                    useShadow = true,
                    crop = true
                } = {}) {

        super();

        assert.isNumber(share, "share");
        assert.isNumber(offset, "offset");
        assert.isNumber(radius, "radius");

        this.share = share;
        this.offset = offset;
        this.radius = radius;

        /**
         * When text is flipped, this is added to the radius to move text further away from origin
         * NOTE: this is a fudge factor and need to be adjusted in tandem with font size
         * @type {number}
         */
        this.flipAlignmentOffset = 20;

        /**
         * Setting this to true will automatically toggle flipY on and off based on the angle at which the text appears
         * @type {boolean}
         */
        this.autoFlipY = true;

        /**
         * Text will be flipped up-side-down if set to true. If {@link #autoFlipY} is set to true, this parameter will be ignored and overridden
         * @type {boolean}
         */
        this.flipY = false;

        /**
         * @readonly
         * @type {boolean}
         */
        this.useShadow = useShadow;

        /**
         *
         * @type {boolean}
         */
        this.crop = crop;

        const elSVG = SVG.createElement('svg');
        elSVG.classList.add('radial-text-label');

        const elPath = SVG.createElement('path');

        const elText = SVG.createElement('text');

        const elTextPath = SVG.createElement('textPath');

        /**
         * NOTE: A unique curve ID is required as Chrome seems to pick up curves from other elements if IDs match
         * @type {string}
         */
        const curveId = generateCurveId();

        //
        elPath.setAttribute("id", curveId);

        elTextPath.setAttribute("startOffset", "50%");
        elTextPath.setAttribute("text-anchor", "middle");
        elTextPath.setAttributeNS('http://www.w3.org/1999/xlink', 'href', "#" + curveId);


        //assemble
        const elements = {
            path: elPath,
            textPath: elTextPath
        };

        this.elements = elements;

        if (useShadow) {
            const shadowFilter = createShadowFilter(0, 0, 4, 4);
            const elDefs = SVG.createElement('defs');

            elSVG.appendChild(elDefs);
            elDefs.appendChild(shadowFilter.el);

            const elShadowText = SVG.createElement('textPath');
            elShadowText.setAttribute("startOffset", "50%");
            elShadowText.setAttribute("text-anchor", "middle");
            elShadowText.setAttributeNS('http://www.w3.org/1999/xlink', 'href', "#" + curveId);


            elShadowText.style.filter = `url(#${shadowFilter.id})`;

            elText.appendChild(elShadowText);

            elements.shadow = {
                textPath: elShadowText
            };
        }

        elText.appendChild(elTextPath);

        elSVG.appendChild(elPath);
        elSVG.appendChild(elText);

        this.el = elSVG;


        this.setFill(fill);
    }

    /**
     *
     * @param {String|number} color
     */
    setFill(color) {
        this.elements.textPath.setAttribute('fill', color);
    }

    /**
     *
     * @param {String} value
     */
    setText(value) {
        let text = value;

        if (this.crop) {
            //figure out how many letters will fit
            const arcLength = 2 * Math.PI * this.radius * this.share;

            const charLength = 14;

            const maxLetters = arcLength / charLength;

            if (maxLetters < value.length) {

                if (maxLetters <= 3) {
                    text = value.slice(0, maxLetters);
                } else {
                    text = value.slice(0, maxLetters - 3) + '...';
                }

            }
        }

        //set text value
        this.elements.textPath.textContent = text;

        if (this.useShadow) {
            this.elements.shadow.textPath.textContent = text;
        }
    }

    render() {
        const offset = this.offset;
        const share = this.share;
        let radius = this.radius;

        if (this.autoFlipY) {
            //find mid point of the arch
            let midPoint = offset + share / 2;

            if (midPoint < 0) {
                midPoint++;
            }

            this.flipY = isValueBetween(midPoint % 1, 0, 0.5);
        }


        let a0 = offset * PI2;
        let a1 = (offset + share) * PI2;


        let angle0, angle1;

        if (this.flipY) {
            angle0 = a1;
            angle1 = a0;

            //push the text off-center along the radius
            radius = radius + this.flipAlignmentOffset;
        } else {
            angle0 = a0;
            angle1 = a1;
        }

        const d = svgCircularPath(
            radius,
            angle0,
            angle1
        );

        // const d = "M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145";
        this.elements.path.setAttribute("d", d);

    }
}
