import BaseNode from '../core/base-node';
import renderAllAttr from './utils/render-all-attributes';
import renderAttr from './utils/render-attribute';
import renderStyle from './utils/render-style';
import NODE_MAP from './node-map';
import renderSVG from './utils/render-svg';
import { SVG_NS, NONE } from './constants';
import { support } from '../common';
import { defined } from '../util';

var TRANSFORM = "transform";
var DefinitionMap = {
    clip: "clip-path",
    fill: "fill"
};

function isDefinition(type, value) {
    return type === "clip" || (type === "fill" && (!value || value.nodeType === "Gradient"));
}

function baseUrl() {
    var base = document.getElementsByTagName("base")[0];
    var href = document.location.href;
    var url = "";

    if (base && !support.browser.msie) {
        var hashIndex = href.indexOf("#");
        if (hashIndex !== -1) {
            href = href.substring(0, hashIndex);
        }

        url = href;
    }

    return url;
}

var Node = (function (BaseNode) {
    function Node(srcElement, options) {
        BaseNode.call(this, srcElement);
        this.definitions = {};

        this.options = options;
    }

    if ( BaseNode ) Node.__proto__ = BaseNode;
    Node.prototype = Object.create( BaseNode && BaseNode.prototype );
    Node.prototype.constructor = Node;

    Node.prototype.destroy = function destroy () {
        if (this.element) {
            this.element._kendoNode = null;
            this.element = null;
        }

        this.clearDefinitions();
        BaseNode.prototype.destroy.call(this);
    };

    Node.prototype.load = function load (elements, pos) {
        var this$1 = this;

        for (var i = 0; i < elements.length; i++) {
            var srcElement = elements[i];
            var children = srcElement.children;

            var childNode = new NODE_MAP[srcElement.nodeType](srcElement, this$1.options);

            if (defined(pos)) {
                this$1.insertAt(childNode, pos);
            } else {
                this$1.append(childNode);
            }

            childNode.createDefinitions();

            if (children && children.length > 0) {
                childNode.load(children);
            }

            var element = this$1.element;
            if (element) {
                childNode.attachTo(element, pos);
            }
        }
    };

    Node.prototype.root = function root () {
        var root = this;

        while (root.parent) {
            root = root.parent;
        }

        return root;
    };

    Node.prototype.attachTo = function attachTo (domElement, pos) {
        var container = document.createElement("div");
        renderSVG(container,
            "<svg xmlns='" + SVG_NS + "' version='1.1'>" +
                this.render() +
            "</svg>"
        );

        var element = container.firstChild.firstChild;
        if (element) {
            if (defined(pos)) {
                domElement.insertBefore(element, domElement.childNodes[pos] || null);
            } else {
                domElement.appendChild(element);
            }
            this.setElement(element);
        }
    };

    Node.prototype.setElement = function setElement (element) {
        if (this.element) {
            this.element._kendoNode = null;
        }

        this.element = element;
        this.element._kendoNode = this;

        var nodes = this.childNodes;
        for (var i = 0; i < nodes.length; i++) {
            var childElement = element.childNodes[i];
            nodes[i].setElement(childElement);
        }
    };

    Node.prototype.clear = function clear () {
        this.clearDefinitions();

        if (this.element) {
            this.element.innerHTML = "";
        }

        var children = this.childNodes;
        for (var i = 0; i < children.length; i++) {
            children[i].destroy();
        }

        this.childNodes = [];
    };

    Node.prototype.removeSelf = function removeSelf () {
        if (this.element) {
            var parentNode = this.element.parentNode;
            if (parentNode) {
                parentNode.removeChild(this.element);
            }
            this.element = null;
        }

        BaseNode.prototype.removeSelf.call(this);
    };

    Node.prototype.template = function template () {
        return this.renderChildren();
    };

    Node.prototype.render = function render () {
        return this.template();
    };

    Node.prototype.renderChildren = function renderChildren () {
        var nodes = this.childNodes;
        var output = "";

        for (var i = 0; i < nodes.length; i++) {
            output += nodes[i].render();
        }

        return output;
    };

    Node.prototype.optionsChange = function optionsChange (e) {
        var field = e.field;
        var value = e.value;

        if (field === "visible") {
            this.css("display", value ? "" : NONE);
        } else if (DefinitionMap[field] && isDefinition(field, value)) {
            this.updateDefinition(field, value);
        } else if (field === "opacity") {
            this.attr("opacity", value);
        } else if (field === "cursor") {
            this.css("cursor", value);
        } else if (field === "id") {
            if (value) {
                this.attr("id", value);
            } else {
                this.removeAttr("id");
            }
        }

        BaseNode.prototype.optionsChange.call(this, e);
    };

    Node.prototype.attr = function attr (name, value) {
        if (this.element) {
            this.element.setAttribute(name, value);
        }
    };

    Node.prototype.allAttr = function allAttr (attrs) {
        var this$1 = this;

        for (var i = 0; i < attrs.length; i++) {
            this$1.attr(attrs[i][0], attrs[i][1]);
        }
    };

    Node.prototype.css = function css (name, value) {
        if (this.element) {
            this.element.style[name] = value;
        }
    };

    Node.prototype.allCss = function allCss (styles) {
        var this$1 = this;

        for (var i = 0; i < styles.length; i++) {
            this$1.css(styles[i][0], styles[i][1]);
        }
    };

    Node.prototype.removeAttr = function removeAttr (name) {
        if (this.element) {
            this.element.removeAttribute(name);
        }
    };

    Node.prototype.mapTransform = function mapTransform (transform) {
        var attrs = [];
        if (transform) {
            attrs.push([
                TRANSFORM,
                "matrix(" + transform.matrix().toString(6) + ")"
            ]);
        }

        return attrs;
    };

    Node.prototype.renderTransform = function renderTransform () {
        return renderAllAttr(
            this.mapTransform(this.srcElement.transform())
        );
    };

    Node.prototype.transformChange = function transformChange (value) {
        if (value) {
            this.allAttr(this.mapTransform(value));
        } else {
            this.removeAttr(TRANSFORM);
        }
    };

    Node.prototype.mapStyle = function mapStyle () {
        var options = this.srcElement.options;
        var style = [ [ "cursor", options.cursor ] ];

        if (options.visible === false) {
            style.push([ "display", NONE ]);
        }

        return style;
    };

    Node.prototype.renderStyle = function renderStyle$1 () {
        return renderAttr("style", renderStyle(this.mapStyle(true)));
    };

    Node.prototype.renderOpacity = function renderOpacity () {
        return renderAttr("opacity", this.srcElement.options.opacity);
    };

    Node.prototype.renderId = function renderId () {
        return renderAttr("id", this.srcElement.options.id);
    };

    Node.prototype.createDefinitions = function createDefinitions () {
        var srcElement = this.srcElement;
        var definitions = this.definitions;
        if (srcElement) {
            var options = srcElement.options;
            var hasDefinitions;

            for (var field in DefinitionMap) {
                var definition = options.get(field);
                if (definition && isDefinition(field, definition)) {
                    definitions[field] = definition;
                    hasDefinitions = true;
                }
            }
            if (hasDefinitions) {
                this.definitionChange({
                    action: "add",
                    definitions: definitions
                });
            }
        }
    };

    Node.prototype.definitionChange = function definitionChange (e) {
        if (this.parent) {
            this.parent.definitionChange(e);
        }
    };

    Node.prototype.updateDefinition = function updateDefinition (type, value) {
        var definitions = this.definitions;
        var current = definitions[type];
        var attr = DefinitionMap[type];
        var definition = {};
        if (current) {
            definition[type] = current;
            this.definitionChange({
                action: "remove",
                definitions: definition
            });
            delete definitions[type];
        }

        if (!value) {
            if (current) {
                this.removeAttr(attr);
            }
        } else {
            definition[type] = value;
            this.definitionChange({
                action: "add",
                definitions: definition
            });
            definitions[type] = value;
            this.attr(attr, this.refUrl(value.id));
        }
    };

    Node.prototype.clearDefinitions = function clearDefinitions () {
        var definitions = this.definitions;

        this.definitionChange({
            action: "remove",
            definitions: definitions
        });
        this.definitions = {};
    };

    Node.prototype.renderDefinitions = function renderDefinitions () {
        return renderAllAttr(this.mapDefinitions());
    };

    Node.prototype.mapDefinitions = function mapDefinitions () {
        var this$1 = this;

        var definitions = this.definitions;
        var attrs = [];

        for (var field in definitions) {
            attrs.push([ DefinitionMap[field], this$1.refUrl(definitions[field].id) ]);
        }

        return attrs;
    };

    Node.prototype.refUrl = function refUrl (id) {
        var skipBaseHref = (this.options || {}).skipBaseHref;
        var baseHref = this.baseUrl().replace(/'/g, "\\'");
        var base = skipBaseHref ? '' : baseHref;
        return ("url(" + base + "#" + id + ")");
    };

    Node.prototype.baseUrl = function baseUrl$1 () {
        return baseUrl();
    };

    return Node;
}(BaseNode));

export default Node;
