/**
 * SVG Generator mit SVG.js
 * Erzeugt Text-Symbole mit Rahmen und Pfeilen
 */

var SvgGenerator = {

    getDashArray: function(style, weight) {
        switch(style) {
            case 'dashed': return (weight * 4) + ',' + (weight * 2);
            case 'dotted': return weight + ',' + (weight * 1.5);
            default: return null;
        }
    },

    measureText: function(text, fontSize) {
        var lines = text.split('\n');
        var charWidth = fontSize * 0.6;
        var lineHeight = fontSize * 1.3;

        var maxLength = 0;
        for (var i = 0; i < lines.length; i++) {
            if (lines[i].length > maxLength) maxLength = lines[i].length;
        }
        if (maxLength === 0) maxLength = 4;

        return {
            lines: lines,
            width: maxLength * charWidth,
            height: lines.length * lineHeight,
            lineHeight: lineHeight,
            charWidth: charWidth
        };
    },

    createShape: function(draw, shape, x, y, width, height, style) {
        var shapeEl;
        var halfW = width / 2;
        var halfH = height / 2;
        var cx = x + halfW;
        var cy = y + halfH;

        switch(shape) {
            case 'rect':
                shapeEl = draw.rect(width, height).move(x, y).radius(4);
                break;
            case 'square':
                var squareSize = Math.max(width, height);
                var squareX = x - (squareSize - width) / 2;
                var squareY = y - (squareSize - height) / 2;
                shapeEl = draw.rect(squareSize, squareSize).move(squareX, squareY).radius(4);
                break;
            case 'rounded':
                shapeEl = draw.rect(width, height).move(x, y).radius(Math.min(halfW, halfH) * 0.5);
                break;
            case 'circle':
                var circleRadius = Math.max(halfW, halfH);
                shapeEl = draw.circle(circleRadius * 2).center(cx, cy);
                break;
            case 'oval':
                shapeEl = draw.ellipse(width, height).center(cx, cy);
                break;
            case 'diamond':
                shapeEl = draw.polygon([
                    [cx, y],
                    [x + width, cy],
                    [cx, y + height],
                    [x, cy]
                ]);
                break;
            default:
                return null;
        }

        if (shapeEl) {
            shapeEl.fill('none').stroke({ color: style.color, width: style.weight });
            if (style.dashArray) {
                shapeEl.attr('stroke-dasharray', style.dashArray);
            }
        }
        return shapeEl;
    },

    calculateArrowHead: function(endX, endY, prevX, prevY, size, tipLength) {
        // Berechne den Winkel der Pfeilrichtung
        var angle = Math.atan2(endY - prevY, endX - prevX);

        // Die zwei hinteren Punkte der Pfeilspitze:
        // - tipLength zurueck in Pfeilrichtung
        // - size/2 seitlich (links und rechts)
        var baseX = endX - tipLength * Math.cos(angle);
        var baseY = endY - tipLength * Math.sin(angle);

        // Senkrecht zur Pfeilrichtung (90 Grad = PI/2)
        var perpAngle = angle + Math.PI / 2;
        var halfWidth = size / 2;

        return [
            [endX, endY],  // Spitze
            [baseX + halfWidth * Math.cos(perpAngle), baseY + halfWidth * Math.sin(perpAngle)],  // Links
            [baseX - halfWidth * Math.cos(perpAngle), baseY - halfWidth * Math.sin(perpAngle)]   // Rechts
        ];
    },

    createArrow: function(draw, direction, frameRect, options) {
        var length = options.length;
        var angle = options.angle;
        var bendPos = options.bendPos;
        var color = options.color;
        var weight = options.weight;
        var dashArray = options.dashArray;
        var arrowSize = options.arrowSize;
        var tipLength = options.tipLength;

        var angleRad = angle * Math.PI / 180;
        var bend = bendPos / 100;

        var startX, startY, midX, midY, endX, endY;
        var fx = frameRect.x;
        var fy = frameRect.y;
        var fw = frameRect.width;
        var fh = frameRect.height;

        // Fuer Standalone-Pfeile (width=0, height=0) ist fx,fy der Startpunkt
        var isStandalone = (fw === 0 && fh === 0);
        var cx = isStandalone ? fx : fx + fw / 2;
        var cy = isStandalone ? fy : fy + fh / 2;

        switch(direction) {
            case 'top':
                startX = cx;
                startY = isStandalone ? fy : fy;
                midX = startX; midY = startY - length * bend;
                endX = midX + Math.sin(angleRad) * (length * (1 - bend));
                endY = midY - Math.cos(angleRad) * (length * (1 - bend));
                break;
            case 'bottom':
                startX = cx;
                startY = isStandalone ? fy : fy + fh;
                midX = startX; midY = startY + length * bend;
                endX = midX + Math.sin(angleRad) * (length * (1 - bend));
                endY = midY + Math.cos(angleRad) * (length * (1 - bend));
                break;
            case 'left':
                startX = isStandalone ? fx : fx;
                startY = cy;
                midX = startX - length * bend; midY = startY;
                endX = midX - Math.cos(angleRad) * (length * (1 - bend));
                endY = midY + Math.sin(angleRad) * (length * (1 - bend));
                break;
            case 'right':
                startX = fx + fw; startY = cy;
                midX = startX + length * bend; midY = startY;
                endX = midX + Math.cos(angleRad) * (length * (1 - bend));
                endY = midY + Math.sin(angleRad) * (length * (1 - bend));
                break;
            default:
                return null;
        }

        var points = (angle !== 0 && bend > 0 && bend < 1)
            ? [[startX, startY], [midX, midY], [endX, endY]]
            : [[startX, startY], [endX, endY]];

        var pathData = '';
        for (var i = 0; i < points.length; i++) {
            pathData += (i === 0 ? 'M' : 'L') + points[i][0] + ' ' + points[i][1] + ' ';
        }

        var line = draw.path(pathData).fill('none').stroke({ color: color, width: weight });
        if (dashArray) line.attr('stroke-dasharray', dashArray);

        var lastPoint = points[points.length - 1];
        var prevPoint = points[points.length - 2];
        var headPoints = this.calculateArrowHead(lastPoint[0], lastPoint[1], prevPoint[0], prevPoint[1], arrowSize, tipLength);
        var arrowHead = draw.polygon(headPoints).fill(color).stroke({ color: color, width: 1 });

        return draw.group().add(line).add(arrowHead);
    },

    createText: function(draw, text, x, y, options) {
        var fontSize = options.fontSize;
        var color = options.color;
        var lines = options.lines;
        var lineHeight = options.lineHeight;
        var alignX = options.alignX || 'center';
        var alignY = options.alignY || 'center';
        var frameWidth = options.frameWidth || 0;
        var frameHeight = options.frameHeight || 0;
        var group = draw.group();

        var totalHeight = lines.length * lineHeight;
        var startY;

        // Vertikale Ausrichtung
        switch(alignY) {
            case 'top':
                startY = y - frameHeight / 2 + fontSize * 0.8 + 5;
                break;
            case 'bottom':
                startY = y + frameHeight / 2 - totalHeight + fontSize * 0.8 - 5;
                break;
            default: // center
                startY = y - (totalHeight / 2) + (fontSize * 0.8);
        }

        // Text-Anchor fuer horizontale Ausrichtung
        var anchor = alignX === 'left' ? 'start' : (alignX === 'right' ? 'end' : 'middle');
        var textX = x;
        if (alignX === 'left') {
            textX = x - frameWidth / 2 + 5;
        } else if (alignX === 'right') {
            textX = x + frameWidth / 2 - 5;
        }

        for (var i = 0; i < lines.length; i++) {
            var lineY = startY + (i * lineHeight);
            var lineText = lines[i] || ' ';
            var textEl = draw.plain(lineText)
                .font({
                    family: 'Arial, sans-serif',
                    size: fontSize,
                    anchor: anchor
                })
                .fill(color)
                .attr('x', textX)
                .attr('y', lineY)
                .attr('text-anchor', anchor);
            group.add(textEl);
        }
        return group;
    },

    generate: function(state) {
        var s = state;
        var textMetrics = this.measureText(s.text || 'Text', s.fontSize);

        var frameScale = s.frameScale / 100;
        var frameWidth = (textMetrics.width + s.paddingLeft + s.paddingRight) * frameScale;
        var frameHeight = (textMetrics.height + s.paddingTop + s.paddingBottom) * frameScale;

        var minWidth = Math.max(frameWidth, 40);
        var minHeight = Math.max(frameHeight, 30);

        // Pfeil-Raum berechnen inkl. Winkel-Abweichung
        var arrowSpace = 0;
        var arrowSideSpace = 0;
        if (s.arrow !== 'none') {
            var angleRad = Math.abs(s.arrowAngle) * Math.PI / 180;
            var bendFactor = s.arrowBend / 100;
            var angledPart = s.arrowLength * (1 - bendFactor);

            // Hauptrichtung: volle Laenge + Spitze + grosszuegiger Puffer
            arrowSpace = s.arrowLength + s.arrowTipLength + s.arrowSize + 25;

            // Seitliche Abweichung durch Winkel (beruecksichtige Pfeilspitze)
            arrowSideSpace = Math.abs(Math.sin(angleRad) * angledPart) + s.arrowSize + s.arrowTipLength + 15;
        }

        var svgWidth = minWidth;
        var svgHeight = minHeight;
        var offsetX = 0, offsetY = 0;

        switch(s.arrow) {
            case 'left':
                svgWidth += arrowSpace;
                svgHeight = Math.max(svgHeight, minHeight + arrowSideSpace * 2);
                offsetX = arrowSpace;
                offsetY = (svgHeight - minHeight) / 2;
                break;
            case 'right':
                svgWidth += arrowSpace;
                svgHeight = Math.max(svgHeight, minHeight + arrowSideSpace * 2);
                offsetY = (svgHeight - minHeight) / 2;
                break;
            case 'top':
                svgHeight += arrowSpace;
                svgWidth = Math.max(svgWidth, minWidth + arrowSideSpace * 2);
                offsetY = arrowSpace;
                offsetX = (svgWidth - minWidth) / 2;
                break;
            case 'bottom':
                svgHeight += arrowSpace;
                svgWidth = Math.max(svgWidth, minWidth + arrowSideSpace * 2);
                offsetX = (svgWidth - minWidth) / 2;
                break;
        }

        // Puffer um alle Elemente
        var padding = 5;
        svgWidth += padding * 2;
        svgHeight += padding * 2;
        offsetX += padding;
        offsetY += padding;

        var draw = SVG().size(svgWidth, svgHeight).viewbox(0, 0, svgWidth, svgHeight);

        var frameX = offsetX;
        var frameY = offsetY;
        var frameCx = frameX + minWidth / 2;
        var frameCy = frameY + minHeight / 2;

        // Text-Verschiebung basierend auf asymmetrischem Padding
        // Wenn links mehr Padding ist, verschiebt sich der Text nach rechts
        var textOffsetX = (s.paddingLeft - s.paddingRight) / 2;
        var textOffsetY = (s.paddingTop - s.paddingBottom) / 2;
        var textCx = frameCx + textOffsetX;
        var textCy = frameCy + textOffsetY;

        var dashArray = this.getDashArray(s.lineStyle, s.lineWeight);
        var strokeStyle = { color: s.frameColor, weight: s.lineWeight, dashArray: dashArray };

        if (s.shape !== 'none') {
            this.createShape(draw, s.shape, frameX, frameY, minWidth, minHeight, strokeStyle);
        }

        if (s.arrow !== 'none') {
            this.createArrow(draw, s.arrow, {
                x: frameX, y: frameY, width: minWidth, height: minHeight
            }, {
                length: s.arrowLength, angle: s.arrowAngle, bendPos: s.arrowBend,
                color: s.frameColor, weight: s.lineWeight, dashArray: dashArray,
                arrowSize: s.arrowSize, tipLength: s.arrowTipLength
            });
        }

        this.createText(draw, s.text, textCx, textCy, {
            fontSize: s.fontSize, color: s.textColor,
            lines: textMetrics.lines, lineHeight: textMetrics.lineHeight,
            alignX: s.textAlignX || 'center',
            alignY: s.textAlignY || 'center',
            frameWidth: minWidth,
            frameHeight: minHeight
        });

        return draw.svg();
    },

    generateArrowOnly: function(state) {
        var s = state;
        if (s.arrow === 'none') return null;

        var length = s.arrowLength;
        var tipLength = s.arrowTipLength;
        var arrowSize = s.arrowSize;
        var padding = 40;

        // Berechne die tatsaechlichen Endpunkte des Pfeils
        var angleRad = s.arrowAngle * Math.PI / 180;
        var bendFactor = s.arrowBend / 100;
        var straightPart = length * bendFactor;
        var angledPart = length * (1 - bendFactor);

        // Seitliche Ausdehnung durch den Winkel (mit Vorzeichen)
        var sideExtent = Math.sin(angleRad) * angledPart;
        var mainExtent = Math.cos(angleRad) * angledPart;

        // Mindestgroesse fuer die seitliche Ausdehnung
        var minSideSpace = Math.abs(sideExtent) + arrowSize + tipLength + padding;

        var width, height, startX, startY;

        switch(s.arrow) {
            case 'right':
                width = straightPart + mainExtent + tipLength + padding * 2;
                height = Math.max(arrowSize * 4, minSideSpace * 2);
                startX = padding;
                // Bei positivem Winkel geht der Pfeil nach unten, also Start weiter oben
                startY = height / 2 - sideExtent / 2;
                break;
            case 'left':
                width = straightPart + mainExtent + tipLength + padding * 2;
                height = Math.max(arrowSize * 4, minSideSpace * 2);
                startX = width - padding;
                startY = height / 2 + sideExtent / 2;
                break;
            case 'bottom':
                width = Math.max(arrowSize * 4, minSideSpace * 2);
                height = straightPart + mainExtent + tipLength + padding * 2;
                startX = width / 2 - sideExtent / 2;
                startY = padding;
                break;
            case 'top':
                width = Math.max(arrowSize * 4, minSideSpace * 2);
                height = straightPart + mainExtent + tipLength + padding * 2;
                startX = width / 2 + sideExtent / 2;
                startY = height - padding;
                break;
        }

        // Stelle sicher, dass alle Werte positiv sind und genuegend Platz haben
        width = Math.max(width, 100);
        height = Math.max(height, 80);

        var draw = SVG().size(width, height).viewbox(0, 0, width, height);

        // Simuliere einen Rahmen-Punkt als Startpunkt
        var frameRect = {
            x: startX,
            y: startY,
            width: 0,
            height: 0
        };

        this.createArrow(draw, s.arrow, frameRect, {
            length: s.arrowLength, angle: s.arrowAngle, bendPos: s.arrowBend,
            color: s.frameColor, weight: s.lineWeight,
            dashArray: this.getDashArray(s.lineStyle, s.lineWeight),
            arrowSize: s.arrowSize, tipLength: s.arrowTipLength
        });

        return draw.svg();
    }
};

window.SvgGenerator = SvgGenerator;
