using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Core.Video;

namespace Desktop
{
    public class VramViewerForm : Form
    {
        private readonly SmsVdp _vdp;
        private readonly System.Windows.Forms.Timer _refreshTimer;
        private readonly Bitmap _vramBitmap;
        private readonly int[] _pixelData;

        public VramViewerForm(SmsVdp vdp)
        {
            _vdp = vdp;
            this.Text = "VRAM Viewer (512 Tiles)";

            // 256x128 native resolution, scaled up by 2 for visibility!
            this.ClientSize = new Size(512, 256);
            this.DoubleBuffered = true;
            this.FormBorderStyle = FormBorderStyle.FixedToolWindow;

            _vramBitmap = new Bitmap(256, 128, PixelFormat.Format32bppArgb);
            _pixelData = new int[256 * 128];

            // Update the window 10 times a second so it doesn't drain CPU power
            _refreshTimer = new System.Windows.Forms.Timer();
            _refreshTimer.Interval = 100;
            _refreshTimer.Tick += (s, e) => UpdateVramImage();
            _refreshTimer.Start();
        }

        private void UpdateVramImage()
        {
            if (_vdp == null) return;

            // Loop through all 512 tiles in VRAM
            for (int tile = 0; tile < 512; tile++)
            {
                // Calculate grid position (32 columns, 16 rows)
                int tileX = (tile % 32) * 8;
                int tileY = (tile / 32) * 8;
                int tileAddress = tile * 32;
                // Decode the 8x8 pixels
                for (int y = 0; y < 8; y++)
                {
                    byte bp0 = _vdp.VRAM[tileAddress + (y * 4) + 0];
                    byte bp1 = _vdp.VRAM[tileAddress + (y * 4) + 1];
                    byte bp2 = _vdp.VRAM[tileAddress + (y * 4) + 2];
                    byte bp3 = _vdp.VRAM[tileAddress + (y * 4) + 3];

                    for (int x = 0; x < 8; x++)
                    {
                        int shift = 7 - x;
                        int colourIndex = ((bp0 >> shift) & 1) | (((bp1 >> shift) & 1) << 1) |
                                         (((bp2 >> shift) & 1) << 2) | (((bp3 >> shift) & 1) << 3);

                        // Use the Sprite Palette (Offset 16) so characters and enemies are colored correctly
                        int colour = _vdp.GetRgbColour(16, colourIndex);
                        //int r = (smsColor & 0x03) * 85;
                        //int g = ((smsColor >> 2) & 0x03) * 85;
                        //int b = ((smsColor >> 4) & 0x03) * 85;

                        // Background color (Index 0) is technically transparent for sprites, so we render it as magenta
                        //if (colourIndex == 0) { int r = 255; int g = 0; int b = 255; }

                        _pixelData[((tileY + y) * 256) + (tileX + x)] = colourIndex == 0 ? (255 << 24) | (255 << 16) | (0 << 8) | 255 : colour;
                    }
                }
            }

            // Blast the array into the Bitmap
            var data = _vramBitmap.LockBits(new Rectangle(0, 0, 256, 128), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            Marshal.Copy(_pixelData, 0, data.Scan0, _pixelData.Length);
            _vramBitmap.UnlockBits(data);

            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            // Draw it chunky and pixel-perfect
            e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
            e.Graphics.DrawImage(_vramBitmap, new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height));
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            _refreshTimer.Stop();
            base.OnFormClosing(e);
        }
    }
}