using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;

namespace AlienAttack.MonoGame.Things.Stars;

internal sealed class StarLayer
{
    private readonly Random _rng;
    private readonly int _count;
    //private readonly float _speed;
    private readonly Vector2 _velocity; // pixels/sec
    private readonly float _minSize, _maxSize;
    private readonly float _minAlpha, _maxAlpha;

    private int _w, _h;
    private readonly Star[] _stars;

    public StarLayer(Random rng, int count, Vector2 velocityPxPerSec, float minSize, float maxSize, float minAlpha, float maxAlpha)
    {
        _rng = rng;
        _count = count;
        //_speed = speedPxPerSec;
        _velocity = velocityPxPerSec;
        _minSize = minSize;
        _maxSize = maxSize;
        _minAlpha = minAlpha;
        _maxAlpha = maxAlpha;

        _stars = new Star[_count];
    }

    public void Initialize(int screenWidth, int screenHeight)
    {
        _w = screenWidth;
        _h = screenHeight;

        for (int i = 0; i < _stars.Length; i++)
            _stars[i] = CreateStar(randomY: true);
    }

    public void OnResize(int screenWidth, int screenHeight)
    {
        // Keep existing stars but clamp/wrap them into the new bounds.
        _w = screenWidth;
        _h = screenHeight;

        for (int i = 0; i < _stars.Length; i++)
        {
            var s = _stars[i];
            s.Pos.X = Wrap(s.Pos.X, _w);
            s.Pos.Y = Wrap(s.Pos.Y, _h);
            _stars[i] = s;
        }
    }

    public void Update(float dt)
    {
        for (int i = 0; i < _stars.Length; i++)
        {
            var s = _stars[i];

            // Move down (positive Y). Add slight horizontal drift if you want:
            // s.Pos.X += s.DriftX * dt;

            //s.Pos.Y += _speed * dt;
            s.Pos += _velocity * dt;

            // Wrap X continuously so diagonal drift never runs out of stars
            if (s.Pos.X < -s.Size) s.Pos.X = _w + s.Size;
            if (s.Pos.X > _w + s.Size) s.Pos.X = -s.Size;

            if (s.Pos.Y >= _h + s.Size)
            {
                // Respawn at top; keep it entering from above
                s = CreateStar(randomY: false);
                s.Pos.Y = -s.Size;
            }

            // Optional subtle twinkle:
            s.TwinkleT += dt * s.TwinkleSpeed;

            _stars[i] = s;
        }
    }

    public void Draw(SpriteBatch sb, Texture2D pixel)
    {
        for (int i = 0; i < _stars.Length; i++)
        {
            var s = _stars[i];

            float twinkle = 1f;
            if (s.TwinkleSpeed > 0f)
                twinkle = 0.85f + 0.15f * (float)Math.Sin(s.TwinkleT);

            var color = Color.White * (s.Alpha * twinkle);

            // Draw as a scaled 1x1 pixel (super fast)
            sb.Draw(pixel, s.Pos, null, color, 0f, Vector2.Zero, s.Size, SpriteEffects.None, 0f);
        }
    }

    private Star CreateStar(bool randomY)
    {
        float x = (float)_rng.NextDouble() * _w;
        float y = randomY ? (float)_rng.NextDouble() * _h : 0f;

        float size = Lerp(_minSize, _maxSize, (float)_rng.NextDouble());
        float alpha = Lerp(_minAlpha, _maxAlpha, (float)_rng.NextDouble());

        // Twinkle: keep it more common on “near” layers by setting a nonzero speed range
        float twinkleSpeed = Lerp(0f, 6f, (float)_rng.NextDouble()) * 0.4f; // tweak or set 0 for none

        return new Star
        {
            Pos = new Vector2(x, y),
            Size = size,
            Alpha = alpha,
            TwinkleSpeed = twinkleSpeed,
            TwinkleT = (float)_rng.NextDouble() * MathF.PI * 2f
        };
    }

    private static float Wrap(float v, float max)
    {
        if (max <= 0) return 0;
        v %= max;
        if (v < 0) v += max;
        return v;
    }

    private static float Lerp(float a, float b, float t) => a + (b - a) * t;
}