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

namespace MonoGame.FourEngine.Raycast
{
    partial class RaycastComponent
    {
        public override void OnDraw(DrawEventArgs args)
        {
            Batch.Begin(
                samplerState: SamplerState.PointClamp,
                sortMode: SpriteSortMode.Immediate,
                rasterizerState: RasterizerState.CullNone,
                depthStencilState: DepthStencilState.None,
                blendState: BlendState.AlphaBlend);

            // Draw floors
            if (_hasFloor)
            {
                _floorCanvas.SetData(_floorProjection);
                Batch.Draw(_floorCanvas, new Rectangle(0, _floorHeight, Width, _floorHeight), _floorCanvas.Bounds, Color.White);
                Batch.Draw(FloorShadow, new Vector2(0, _floorHeight), Color.White);
            }

            // Draw bottom walls
            var sortedWidth = WallLayers[0].DestinationRect
                .Select((r, x) => new { X = x, H = r.Height })
                .OrderBy(r => r.H)
                .Select(x => x.X)
                .ToArray();

            var rect = Rectangle.Empty;
            var previousRect = Rectangle.Empty;

            foreach (var x in sortedWidth)
            {
                // Draw upper walls
                for (var i = WallLayers.Length - 1; i >= 0; i--)
                {
                    if (!WallLayers[i].IsEmpty[x])
                    {
                        rect = WallLayers[i].DestinationRect[x];
                        Batch.Draw(Map.Layers[i].TileSet.Texture, rect, WallLayers[i].SourceRect[x], WallLayers[i].Color[x]);
                    }
                }

                // Draw semiblocked objects
                while (_objectStack.Any() && _objectStack.Peek() is int o)
                {
                    if (previousRect.Height <= _objectOnScreen[o].Height && rect.Height > _objectOnScreen[o].Height)
                    {
                        DrawObject();
                    }
                    else
                    {
                        break;
                    }
                }
                previousRect = rect;
            }

            // Draw other objects
            while (_objectStack.Any() && _objectStack.Peek() is int o)
            {
                DrawObject();
            }
            Batch.End();

            _drawn = true;
        }

        private void DrawObject()
        {
            var o = _objectStack.Dequeue();
            var obj = Objects[o];
            var destinationRect = _objectOnScreen[o];
            var sourceRect = new Rectangle(obj.Sprite.Size * obj.Frame, obj.Sprite.Size);

            if (obj.Sprite.Size.X != WallTextureSize || obj.Sprite.Size.Y != WallTextureSize)
            {
                var width = (int)(destinationRect.Width * (float)obj.Sprite.Size.X / WallTextureSize);
                var height = (int)(destinationRect.Height * (float)obj.Sprite.Size.Y / WallTextureSize);

                // Align by center
                var x = destinationRect.X + (destinationRect.Width - width) / 2;

                // Align by bottom
                var y = destinationRect.Bottom - height;

                destinationRect = new Rectangle(x, y,
                    width,
                    height);
            }

            if (destinationRect.Width > 0 && destinationRect.Height > 0)
                obj.Draw(Batch, destinationRect, sourceRect, _objectColor[o]);
        }
    }
}