/*****************************************************************************
 *
 * ElementLabel.js - This class realizes the object labels
 *
 * Copyright (c) 2004-2016 NagVis Project (Contact: info@nagvis.org)
 *
 * License:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/

const ElementLabel = Element.extend({
    label_text: null,

    update: function () {
        this.label_text = this.obj.conf.label_text || "";

        // Replace configuration based macros in label_text when needed
        if (this.label_text && this.label_text !== "") {
            let objName;
            // For maps use the alias as display string
            if (this.obj.conf.type == "map") {
                objName = this.obj.conf.alias;
            } else {
                objName = this.obj.conf.name;
            }

            this.label_text = this.label_text.replace(getRegEx("name", "\\[name\\]", "g"), objName);
            this.label_text = this.label_text.replace(getRegEx("alias", "\\[alias\\]", "g"), this.obj.conf.alias);

            if (this.obj.conf.type == "service") {
                this.label_text = this.label_text.replace(
                    getRegEx("service_description", "\\[service_description\\]", "g"),
                    this.obj.conf.service_description
                );
            }
        }
    },

    updateAttrs: function (only_state) {
        // update the label on every state update where at least the output or perfdata changed
        if (!only_state || (!this.obj.stateChanged() && this.obj.outputOrPerfdataChanged())) {
            this.erase();
            this.render();
            this.draw();

            // The x/y coords set above are only temporary coordinates. The right ones are
            // later set by the place() executed in:
            // a) onload of icon images
            // b) at end of lines render() methods
            //
            // Normally the following call of this.place() should not be neccessary but there seem
            // to be cases where the onload handler function of icons does not fire and
            // icons won't be moved to their expected position. To workaround this, we call
            // this.place() here which won't get the exact coordinates (in case of relative
            // coordinates to the icon like bottom/center).
            this.place();
        }
    },

    render: function () {
        this.dom_obj = renderNagVisTextbox(
            this.obj.conf.object_id + "-label",
            this.obj.conf.label_background,
            this.obj.conf.label_border,
            0,
            0,
            this.obj.conf.z,
            this.obj.conf.label_width,
            "",
            this.getText(),
            this.obj.conf.label_style
        );
    },

    unlock: function () {
        addEvent(this.dom_obj, "mouseover", function () {
            document.body.style.cursor = "move";
        });
        addEvent(this.dom_obj, "mouseout", function () {
            document.body.style.cursor = "auto";
        });

        makeDragable(this.dom_obj, this.obj, this.saveLabel, this.dragLabel);
    },

    lock: function () {
        this.dom_obj.onmouseover = null;
        this.dom_obj.onmouseout = null;

        makeUndragable(this.dom_obj);
    },

    /**
     * Calculates and applies the real positions of the objects label. It uses the configuration
     * variables label_x/label_y and repositions the labels based on the config. The label
     * must have been rendered and added to dom to have the dimensions of the object to be able
     * to realize the center/bottom coordinate definitions.
     */
    place: function () {
        this.dom_obj.style.left = this.parseLabelCoord("x") + "px";
        this.dom_obj.style.top = this.parseLabelCoord("y") + "px";
    },

    //
    // END OF PUBLIC METHODS
    //

    // Returns the label with even state based macros replaced
    getText: function () {
        let text = this.label_text;

        // Replace static macros in label_text when needed
        if (text && text !== "") {
            text = text.replace(getRegEx("output", "\\[output\\]", "g"), this.obj.conf.output);

            if (this.obj.conf.type == "service" || this.obj.conf.type == "host") {
                text = text.replace(getRegEx("perfdata", "\\[perfdata\\]", "g"), this.obj.conf.perfdata);
            }
        }

        if (this.obj.conf.label_maxlen > 0 && text.length > this.obj.conf.label_maxlen)
            text = text.substr(0, this.obj.conf.label_maxlen - 2) + "...";

        return text;
    },

    /**
     * This needs to calculate the offset of the current position to the first position,
     * then create a new coord (relative/absolue) and save them in label_x/y attributes
     */
    // Important: It is called from an event handler the 'this.' keyword can not be used here.
    dragLabel: function (trigger_obj, obj, event) {
        const isRelative = function (coord) {
            return coord.toString().match(/^(?:\+|-|center|bottom)/);
        };

        // Calculates relative/absolute coords depending on the current configured type
        const calcNewLabelCoord = function (labelCoord, coord, newCoord) {
            if (isRelative(labelCoord)) {
                const ret = newCoord - coord;
                if (ret >= 0) return "+" + ret;
                return ret;
            } else return newCoord;
        };

        obj.conf.label_x = calcNewLabelCoord(obj.conf.label_x, obj.parseCoord(obj.conf.x, "x", false), trigger_obj.x);
        obj.conf.label_y = calcNewLabelCoord(obj.conf.label_y, obj.parseCoord(obj.conf.y, "y", false), trigger_obj.y);

        if (isRelative(obj.conf.label_x) || isRelative(obj.conf.label_y)) obj.highlight(true);
    },

    // Important: It is called from an event handler the 'this.' keyword can not be used here.
    saveLabel: function (trigger_obj, obj, oParent) {
        saveObjectAttr(obj.conf.object_id, {
            label_x: obj.conf.label_x,
            label_y: obj.conf.label_y
        });
        obj.highlight(false);
    },

    parseLabelCoord: function (dir) {
        let coord, obj_coord;
        if (dir === "x") {
            coord = this.obj.conf.label_x;

            if (this.obj.conf.view_type && this.obj.conf.view_type == "line") {
                obj_coord = this.obj.getLineMid(this.obj.conf.x, "x");
            } else {
                obj_coord = addZoomFactor(this.obj.parseCoords(this.obj.conf.x, "x", false)[0], true);
            }
        } else {
            coord = this.obj.conf.label_y;
            if (this.obj.conf.view_type && this.obj.conf.view_type == "line") {
                obj_coord = this.obj.getLineMid(this.obj.conf.y, "y");
            } else {
                obj_coord = addZoomFactor(this.obj.parseCoords(this.obj.conf.y, "y", false)[0], true);
            }
        }

        if (dir == "x" && coord && coord.toString() == "center") {
            const diff = parseInt(parseInt(this.dom_obj.clientWidth) - rmZoomFactor(this.obj.getObjWidth())) / 2;
            coord = obj_coord - diff;
        } else if (dir == "y" && coord && coord.toString() == "bottom") {
            coord = obj_coord + rmZoomFactor(this.obj.getObjHeight());
        } else if (coord && coord.toString().match(/^(?:\+|-)/)) {
            // If there is a presign it should be relative to the objects x/y
            coord = obj_coord + addZoomFactor(parseFloat(coord));
        } else if (!coord || coord === "0") {
            // If no x/y coords set, fallback to object x/y
            coord = obj_coord;
        } else {
            // This must be absolute coordinates, apply zoom factor
            coord = addZoomFactor(coord, true);
        }

        return coord;
    }
});
