using SFML.Graphics;
using SFML.System;
using FluidSim.Core;
using FluidSim.Components;

namespace FluidSim.Tests
{
    public abstract class Scenario
    {
        protected const float AmbientPressure = 101325f;
        protected const float AmbientTemperature = 300f;
        public float Throttle { get; set; }

        public abstract void Initialize(int sampleRate);
        public abstract float Process();
        public abstract void Draw(RenderWindow target);

        protected Color PressureColor(float pressurePa)
        {
            float bar = pressurePa / 1e5f;
            byte r, g, b;
            if (bar < 1f)
            {
                float f = Math.Clamp(bar, 0f, 1f);
                r = 0; g = (byte)(255 * f); b = (byte)(255 * (1 - f));
            }
            else
            {
                float f = Math.Min((bar - 1f) / 9f, 1f);
                r = (byte)(255 * f); g = (byte)(255 * (1 - f)); b = 0;
            }
            return new Color(r, g, b);
        }

        protected Color TemperatureColor(float t)
        {
            t = Math.Clamp(t, 0f, 2000f);
            byte r, g, b;
            if (t < AmbientTemperature)
            {
                float f = t / AmbientTemperature;
                r = 0; g = (byte)(255 * f); b = (byte)(255 * (1 - f));
            }
            else
            {
                float f = (t - AmbientTemperature) / (2000f - AmbientTemperature);
                r = (byte)(255 * f); g = (byte)(255 * (1 - f)); b = 0;
            }
            return new Color(r, g, b);
        }

        protected void DrawVolume(RenderWindow target, Volume0D volume,
                                  float centerX, float topY, float width, float height)
        {
            var rect = new RectangleShape(new Vector2f(width, height))
            {
                FillColor = PressureColor(volume.Pressure),
                Position = new Vector2f(centerX - width / 2f, topY)
            };
            target.Draw(rect);
            var border = new RectangleShape(new Vector2f(width, height))
            {
                FillColor = Color.Transparent,
                OutlineColor = Color.White,
                OutlineThickness = 1f,
                Position = rect.Position
            };
            target.Draw(border);
        }

        protected void DrawCylinder(RenderWindow target, Cylinder cylinder,
                                    float centerX, float topY, float width, float maxHeight)
        {
            float fraction = cylinder.PistonFraction;
            float currentHeight = maxHeight * fraction;
            var wall = new RectangleShape(new Vector2f(width, maxHeight))
            {
                FillColor = new Color(60, 60, 60),
                Position = new Vector2f(centerX - width / 2f, topY)
            };
            target.Draw(wall);
            var gas = new RectangleShape(new Vector2f(width, currentHeight))
            {
                FillColor = PressureColor(cylinder.Pressure),
                Position = new Vector2f(centerX - width / 2f, topY)
            };
            target.Draw(gas);
            var piston = new RectangleShape(new Vector2f(width, 4f))
            {
                FillColor = Color.White,
                Position = new Vector2f(centerX - width / 2f, topY + currentHeight)
            };
            target.Draw(piston);
            float valveW = 6f, valveH = 10f, valveY = topY + 4f;
            var iv = new RectangleShape(new Vector2f(valveW, valveH))
            {
                FillColor = cylinder.IntakeValveArea > 0f ? Color.Green : Color.Red,
                Position = new Vector2f(centerX - width / 2f - valveW - 2f, valveY)
            };
            target.Draw(iv);
            var ev = new RectangleShape(new Vector2f(valveW, valveH))
            {
                FillColor = cylinder.ExhaustValveArea > 0f ? Color.Green : Color.Red,
                Position = new Vector2f(centerX + width / 2f + 2f, valveY)
            };
            target.Draw(ev);
        }

        protected void DrawPipe(RenderWindow target, PipeSystem pipeSystem, int pipeIndex,
                                float pipeCenterY, float pipeStartX, float pipeEndX)
        {
            int start = pipeSystem.GetPipeStart(pipeIndex);
            int end = pipeSystem.GetPipeEnd(pipeIndex);
            int n = end - start;
            if (n < 2) return;

            float pipeLen = pipeEndX - pipeStartX;
            float dx = pipeLen / (n - 1);
            float baseRadius = 25f;

            var centers = new float[n];
            var radii = new float[n];
            var temps = new float[n];
            for (int i = 0; i < n; i++)
            {
                int cell = start + i;
                float p = pipeSystem.GetCellPressure(cell);
                float rho = pipeSystem.GetCellDensity(cell);
                temps[i] = p / MathF.Max(rho * 287f, 1e-12f);
                float dev = MathF.Tanh((p - AmbientPressure) / AmbientPressure * 0.5f);
                radii[i] = baseRadius * (1f + dev * 2f);
                if (radii[i] < 2f) radii[i] = 2f;
                centers[i] = pipeStartX + i * dx;
            }

            int segments = 8;
            var va = new VertexArray(PrimitiveType.TriangleStrip);
            for (int i = 0; i < n; i++)
            {
                float x = centers[i], r = radii[i];
                Color col = TemperatureColor(temps[i]);
                va.Append(new Vertex(new Vector2f(x, pipeCenterY - r), col));
                va.Append(new Vertex(new Vector2f(x, pipeCenterY + r), col));
                if (i < n - 1)
                {
                    for (int s = 1; s <= segments; s++)
                    {
                        float t = s / (float)segments;
                        float xi = centers[i] + (centers[i + 1] - centers[i]) * t;
                        float ri = radii[i] + (radii[i + 1] - radii[i]) * t;
                        float Ti = temps[i] + (temps[i + 1] - temps[i]) * t;
                        Color colS = TemperatureColor(Ti);
                        va.Append(new Vertex(new Vector2f(xi, pipeCenterY - ri), colS));
                        va.Append(new Vertex(new Vector2f(xi, pipeCenterY + ri), colS));
                    }
                }
            }
            target.Draw(va);
        }
    }
}