/*!
 * @package    Prime Rating - Web Component
 * @author     Abhishek Das <info@virtueplanet.com>
 * @copyright  Copyright (C) 2012-2024 VirtuePlanet Services LLP. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 * @link       https://www.virtueplanet.com
 */

class PrimeRating extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
    }

    get max() {
        return this._getPositiveNumberAttribute('max', 5);
    }

    set max(val) {
        this.setAttribute("max", val);
    }

    get name() {
        return this.getAttribute("name");
    }

    set name(val) {
        this.setAttribute("name", val);
    }

    get rating() {
        return this._getPositiveNumberAttribute("rating", 0);
    }

    set rating(val) {
        this.setAttribute("rating", val);
    }

    get half() {
        return this._getValidBooleanAttribute("half", false);
    }

    set half(val) {
        this.setAttribute("half", val);
    }

    get readonly() {
        return this._getValidBooleanAttribute("readonly", false);
    }

    set readonly(val) {
        this.setAttribute("readonly", val);
    }

    get unratedMsg() {
        return this.getAttribute("unrated-msg");
    }

    set unratedMsg(val) {
        this.setAttribute("unrated-msg", val);
    }

    get hint() {
        return this.getAttribute("hint");
    }

    set hint(val) {
        this.setAttribute("hint", val);
    }

    get color() {
        return this._getValidColorAttribute("color", "#000000");
    }

    set color(val) {
        this.setAttribute("color", val);
    }

    get size() {
        return this._getPositiveNumberAttribute("size", 0);
    }

    set size(val) {
        this.setAttribute("size", val);
    }

    static get observedAttributes() {
        return [
            "max",
            "name",
            "rating",
            "half",
            "readonly",
            "unrated-msg",
            "hint",
            "color",
            "size"
        ];
    }

    attributeChangedCallback(prop, oldValue, newValue) {
        this._render();
    }

    connectedCallback() {
        this._render();
    }

    disconnectedCallback() {
        this.innerHTML = "";
    }

    _render() {
        this.innerHTML = "";

        const container = document.createElement("div");
        let star;

        container.classList.add("container");

        if (this.readonly) {
            container.classList.add("readonly");
        }

        for (let i = 1; i <= this.max; i++) {
            star = document.createElement("i");

            star.classList.add("star");
            star.setAttribute("aria-hidden", "true");
            star.setAttribute("data-rating", i);

            if (!this.readonly && this.hint) {
                star.setAttribute("title", i + " " + this.hint);
            }

            container.append(star);
        }

        if (this.readonly) {
            if (this.rating > 0.5 && this.hint) {
                container.setAttribute("title", this.hint + " " + this.rating);
            } else if (this.rating < 0.5 && this.unratedMsg) {
                container.setAttribute("title", this.unratedMsg);
            }
        } else if (this.name) {
            const input = document.createElement('input');

            input.setAttribute("type", "hidden");
            input.setAttribute("name", this.name);
            input.setAttribute("value", this.rating);

            this.appendChild(input);
        }

        this.shadowRoot.innerHTML = this._getStyles();

        this.shadowRoot.append(container);

        this._setStarRating(this.rating);

        if (!this.readonly) {
            this._bindEvents();
        }
    }

    _setStarRating(rating) {
        const container = this.shadowRoot.querySelector(".container");
        let balance = rating, star;

        for (let index = 0; index < this.max; index++) {
            star = container.children[index];

            star.classList.remove("star-full");
            star.classList.remove("star-half");

            if (balance > 0) {
                if (this.half && balance < 1 && balance <= 0.5) {
                    star.classList.add("star-half");
                } else if (balance > 0.5) {
                    star.classList.add("star-full");
                }
            }

            balance--;
        }
    }

    _bindEvents() {
        this.shadowRoot.querySelector(".container").addEventListener("mouseover", this._bindMouseover.bind(this));
        this.shadowRoot.querySelector(".container").addEventListener("mouseleave", this._bindMouseleave.bind(this));
        this.shadowRoot.querySelector(".container").addEventListener("click", this._bindClick.bind(this));
    }

    _bindMouseover(event) {
        if (!event.target) {
            return false;
        }

        if (event.target.classList.contains("star")) {
            const { rating } = event.target.dataset;
            this._setStarRating(rating);
        }
    }

    _bindMouseleave() {
        this._setStarRating(this.rating);
    }

    _bindClick(event) {
        if (!event.target) {
            return false;
        }

        if (event.target.classList.contains("star")) {
            const { rating } = event.target.dataset;
            this.rating = rating;
            this._setStarRating(rating);

            const input = this.querySelector("input[type='hidden']");

            if (input) {
                input.setAttribute("value", this.rating);
            }
        }
    }

    _getPositiveNumberAttribute(prop, def = 0) {
        let val = this.getAttribute(prop);

        val = val ? parseFloat(val) : 0;
        val = !val || val < 0 ? def : val;

        return val;
    }

    _getValidBooleanAttribute(prop, def = false) {
        let val = this.getAttribute(prop);

        if (val === null) {
            val = def
        }

        val = val === "true" || val === "TRUE" || val === "1" ? true : false;

        return val;
    }

    _getValidColorAttribute(prop, def = "#000000") {
        let strColor = this.getAttribute(prop);

        const s = new Option().style;
        s.color = strColor;

        if (s.color != '') {
            return s.color.replace("#", "%23");
        }

        return def.replace("#", "%23");
    }

    _getStyles() {
        const style = `
        <style>
        .container {
            display: inline-block;
        }
        .star {
            display: inline-block;
            width: ${this.size}px;
            height: ${this.size}px;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='${this.color}' viewBox='0 0 16 16'%3E%3Cpath d='M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899zM8 11.773l-3.492 1.836 0.667-3.888-2.825-2.753 3.904-0.567 1.746-3.537 1.746 3.537 3.904 0.567-2.825 2.753 0.667 3.888-3.492-1.836z'%3E%3C/path%3E%3C/svg%3E%0A");
            background-repeat: no-repeat;
            background-color: transparent;
            background-position: 50% 50%;
            background-size: contain;
            margin-right: 2px;
        }
        .star-full {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'  fill='${this.color}' viewBox='0 0 16 16'%3E%3Cpath d='M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899z'%3E%3C/path%3E%3C/svg%3E");
        }
        .star-half {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'  fill='${this.color}' viewBox='0 0 16 16'%3E%3Cpath d='M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899zM8 11.773l-0.015 0.008 0.015-8.918 1.746 3.537 3.904 0.567-2.825 2.753 0.667 3.888-3.492-1.836z'%3E%3C/path%3E%3C/svg%3E%0A");
        }
        .container:not(.readonly) .star {
            cursor: pointer;
        }
        </style>
        `;

        return style;
    }
}

window.customElements.define("prime-rating", PrimeRating);
