// Copyright © Selmo Technology GmbH. All Rights Reserved

using SelmoStudio.Hmi.Server.SelmoHmi.Sequence.Model.Dto;
using System.Drawing;
using System.Globalization;

namespace SelmoStudio.Hmi.Server.Shared.Model.Diagram
{
    internal class DiagramElementCreator
    {
        private static readonly Color ColorStep = Color.FromArgb(255, 120, 120, 120);
        private static readonly Color ColorTimer = Color.FromArgb(255, 180, 180, 120);
        private static readonly Color ColorJump = Color.FromArgb(255, 69, 107, 226);
        private static readonly Color ColorJumpAbsolute = Color.FromArgb(255, 4, 50, 146);
        private static readonly Color ColorDecision = Color.FromArgb(255, 127, 81, 161);
        private static readonly Color ColorLoop = Color.FromArgb(255, 160, 20, 40);
        private static readonly Color ColorCrossSequenceSender = Color.FromArgb(255, 241, 151, 32);
        private static readonly Color ColorCrossSequenceReceiver = Color.FromArgb(255, 34, 139, 34);
        private static readonly Color ColorComment = Color.FromArgb(255, 0, 153, 0);
        private static readonly Color ColorText = Color.FromArgb(255, 255, 255, 255);
        private static readonly Color ColorConnector = Color.FromArgb(255, 128, 128, 128);

        private static readonly int ConnectorStrokeWidth = 3;

        private static readonly int ActiveStepFrameMargin = 10;
        private static readonly string ActiveStepFrameHexColor = "#53ff1a";
        private static readonly int ActiveStepFrameStrokeWidth = 2;

        internal static string GetElement(LogicDiagramStep step, bool isActiveStep)
        {
            // TODO use the colors defined in the LogicLayerConstants
            var colorHex = ToHex(GetColor(step.StepType));
            var textColorHex = ToHex(ColorText);

            var scaleFactor = CalculateScaleFactor(step);

            var pathG = step.Geometry;
            var path = $"<svg x=\"{step.DiagramPositionX.ToString(CultureInfo.InvariantCulture)}\" " +
                $"y=\"{step.DiagramPositionY.ToString(CultureInfo.InvariantCulture)}\">" +
                $"<g><path  d=\"{pathG}\" fill=\"{colorHex}\" " +
                $"transform=\"scale({scaleFactor.factorX.ToString(CultureInfo.InvariantCulture)}," +
                $"{scaleFactor.factorY.ToString(CultureInfo.InvariantCulture)})\" />" +
                $"{GenerateTextTag(step, textColorHex)}" +
                $"</g></svg>" +
                $"{(isActiveStep ? $"{GetActiveStepFrame(step)}" : string.Empty)}";

            return path;
        }

        private static string GetActiveStepFrame(LogicDiagramStep step)
        {
            return $"<svg x=\"{(step.DiagramPositionX - ActiveStepFrameMargin).ToString(CultureInfo.InvariantCulture)}\"" +
                $" y=\"{(step.DiagramPositionY - ActiveStepFrameMargin).ToString(CultureInfo.InvariantCulture)}\" >" +
                $"><rect width=\"{(step.SizeX + 2 * ActiveStepFrameMargin).ToString(CultureInfo.InvariantCulture)}\"" +
                $"height=\"{(step.SizeY + 2 * ActiveStepFrameMargin).ToString(CultureInfo.InvariantCulture)}\" " +
                $" rx=\"4\" ry=\"4\" fill=\"transparent\" stroke=\"{ActiveStepFrameHexColor}\" " +
                $"stroke-width=\"{ActiveStepFrameStrokeWidth}\"/></svg>";
        }

        private static string GenerateTextTag(LogicDiagramStep step, string textColor)
        {
            if (((step.StepType == StepType.Timer || step.StepType == StepType.Decision) && step.Name.Length > 8)
                || step.Name.Length > 12)
            {
                return $@"<foreignObject x=""0"" y=""0"" width=""{step.SizeX.ToString(CultureInfo.InvariantCulture)}""
                            height=""{step.SizeY.ToString(CultureInfo.InvariantCulture)}"">
                            <div xmlns=""http://www.w3.org/1999/xhtml"" style=""width: 100%; height: 100%; display: flex;
                                 align-items: center; justify-content: center; overflow: hidden;"">
                                <div style=""color: {textColor}; word-break: break-word; text-align: left; line-height: 1.2;
                                     width: 80%;"">
                                    {step.Name}
                                </div>
                            </div>
                        </foreignObject>";
            }

            return $"<text x=\"{(step.SizeX / 2).ToString(CultureInfo.InvariantCulture)}\"" +
                $"y=\"{(step.SizeY / 2).ToString(CultureInfo.InvariantCulture)}\"" +
                $" dominant-baseline=\"middle\" text-anchor=\"middle\"" +
                $" fill=\"{textColor}\">{step.Name}</text>";
        }

        private static (double factorX, double factorY) CalculateScaleFactor(LogicDiagramStep step)
        {
            var factorX = step.SizeX / GetOriginalX(step.StepType);
            var factorY = step.SizeY / GetOriginalY(step.StepType);

            return (factorX, factorY);
        }

        private static Dictionary<StepType, double> OriginalXValues = new()
        {
            {StepType.Step, 112 },
            {StepType.Timer, 75 },
            {StepType.Jump, 111.5 },
            {StepType.Decision, 111.5 },
            {StepType.Loop, 111.5 },
        };

        private static double GetOriginalX(StepType stepType)
        {
            if (OriginalXValues.ContainsKey(stepType))
            {
                return OriginalXValues[stepType];
            }
            return 112;
        }

        private static Dictionary<StepType, double> OriginalYValues = new()
        {
            {StepType.Step, 75 },
            {StepType.Timer, 75 },
            {StepType.Jump, 74.5 },
            {StepType.Decision, 74.5 },
            {StepType.Loop, 74.5 },
        };

        private static double GetOriginalY(StepType stepType)
        {
            if (OriginalYValues.ContainsKey(stepType))
            {
                return OriginalYValues[stepType];
            }
            return 74.5;
        }

        internal static string GetConnector(LogicDiagramConnection connection)
        {
            // TODO use the colors defined in the LogicLayerConstants
            var colorHex = ToHex(ColorConnector);

            return $"<path d=\" M {connection.DiagramStartPointX} {connection.DiagramStartPointY} " +
                $"C{connection.DiagramBezierStartPointX},{connection.DiagramBezierStartPointY} ," +
                $"{connection.DiagramBezierEndPointX} {connection.DiagramBezierEndPointY} ," +
                $"{connection.DiagramEndPointX} {connection.DiagramEndPointY}\"\r\n " +
                $"fill=\"none\" stroke=\"{colorHex}\" stroke-width=\"{ConnectorStrokeWidth}\" marker-end=\"url(#{connection.Id})\"/>";
        }

        internal static string GetArrowMarker(LogicDiagramConnection connection)
        {
            var colorHex = ToHex(ColorConnector);

            return $"<marker id=\"{connection.Id}\" markerWidth=\"2\" markerHeight=\"2\" refX=\"1\" refY=\"1\" orient=\"auto\">" +
                $"<path d=\"M 0 0 L 2 1 L 0 2 z\" fill=\"{colorHex}\" /></marker>";
        }

        private static Color GetColor(StepType stepType)
        {
            return stepType switch
            {
                StepType.Step => ColorStep,
                StepType.Decision => ColorDecision,
                StepType.Timer => ColorTimer,
                StepType.Jump => ColorJump,
                StepType.Loop => ColorLoop,
                var s when s == StepType.CrossSequenceSenderSetter || s == StepType.CrossSequenceSenderGetter => ColorCrossSequenceSender,
                var s when s == StepType.CrossSequenceReceiverSetter || s == StepType.CrossSequenceReceiverGetter => ColorCrossSequenceReceiver,
                _ => throw new NotSupportedException("Step type is not supported")
            };
        }

        private static string ToHex(Color c)
        {
            return $"#{c.R:X2}{c.G:X2}{c.B:X2}";
        }
    }
}