using System;
using System.IO;
using System.Numerics;
using Core;
using Raylib_cs;

namespace Parsons.SteamDeck
{
    class Program
    {
        static void Main(string[] args)
        {
            Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
            Raylib.InitWindow(1280, 800, "Parsons Master System 2026");
            Raylib.InitAudioDevice();

            //Raylib.SetAudioStreamBufferSizeDefault(1024);

            AudioStream audioStream = Raylib.LoadAudioStream(44100, 32, 1);
            Raylib.PlayAudioStream(audioStream);

            SmsMachine machine = new SmsMachine();
            RaylibAudioPlayer audioPlayer = new RaylibAudioPlayer();
            machine.AudioProcessor.AudioDevice = audioPlayer;

            Raylib.SetTargetFPS(60);

            Image img = Raylib.GenImageColor(256, 192, Color.Black);
            Texture2D vdpTexture = Raylib.LoadTextureFromImage(img);
            byte[] pixelBuffer = new byte[256 * 192 * 4];

            bool isMenuState = true;
            string[] embeddedRoms = GetEmbeddedRoms();
            int selectedRomIndex = 0;
            bool buttonLock = false;

            while (!Raylib.WindowShouldClose())
            {
                if (isMenuState)
                {
                    // --- MENU INPUT LOGIC ---
                    if (!buttonLock)
                    {
                        if (Raylib.IsKeyDown(KeyboardKey.S) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceDown))
                        {
                            selectedRomIndex++;
                            if (selectedRomIndex >= embeddedRoms.Length) selectedRomIndex = 0;
                            buttonLock = true;
                        }
                        else if (Raylib.IsKeyDown(KeyboardKey.W) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceUp))
                        {
                            selectedRomIndex--;
                            if (selectedRomIndex < 0) selectedRomIndex = embeddedRoms.Length - 1;
                            buttonLock = true;
                        }
                        else if (Raylib.IsKeyDown(KeyboardKey.O) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown))
                        {
                            string targetRom = embeddedRoms[selectedRomIndex];
                            machine.LoadCartridge(LoadEmbeddedRomBytes(targetRom));
                            machine.VideoProcessor.IsGameGear = targetRom.EndsWith(".gg", StringComparison.OrdinalIgnoreCase);
                            isMenuState = false;
                        }
                    }

                    if (Raylib.IsKeyUp(KeyboardKey.S) && Raylib.IsKeyUp(KeyboardKey.W) &&
                        Raylib.IsGamepadButtonUp(0, GamepadButton.LeftFaceDown) && Raylib.IsGamepadButtonUp(0, GamepadButton.LeftFaceUp))
                    {
                        buttonLock = false;
                    }

                    // --- SCROLLING MENU DRAW LOGIC ---
                    Raylib.BeginDrawing();
                    Raylib.ClearBackground(new Color(20, 20, 40, 255));

                    Raylib.DrawText("PARSONS MASTER SYSTEM", 50, 50, 40, Color.White);
                    Raylib.DrawText("Select: D-Pad   Start: 'A' Button   Return to Menu: Select + Start", 50, 100, 20, Color.LightGray);

                    // THE FIX: Sliding Window Camera Math
                    int maxVisibleItems = 16;
                    int startIndex = selectedRomIndex - (maxVisibleItems / 2);
                    if (startIndex < 0) startIndex = 0;
                    if (startIndex > embeddedRoms.Length - maxVisibleItems) startIndex = Math.Max(0, embeddedRoms.Length - maxVisibleItems);

                    for (int i = 0; i < maxVisibleItems && (startIndex + i) < embeddedRoms.Length; i++)
                    {
                        int actualIndex = startIndex + i;
                        string cleanName = embeddedRoms[actualIndex].Replace("Parsons.Steamdeck.ROMS.", "");

                        Color textColor = (actualIndex == selectedRomIndex) ? Color.Yellow : Color.White;
                        string prefix = (actualIndex == selectedRomIndex) ? ">> " : "   ";

                        Raylib.DrawText(prefix + cleanName, 80, 160 + (i * 35), 20, textColor);
                    }

                    Raylib.EndDrawing();
                }
                else
                {
                    // --- EMULATOR HOTKEYS ---

                    // If the user holds the "Select" button (or Shift on Keyboard)
                    if (Raylib.IsGamepadButtonDown(0, GamepadButton.MiddleLeft) || Raylib.IsKeyDown(KeyboardKey.LeftShift))
                    {
                        // Select + Start = Return to Menu
                        if (Raylib.IsGamepadButtonPressed(0, GamepadButton.MiddleRight) || Raylib.IsKeyPressed(KeyboardKey.Enter))
                        {
                            isMenuState = true;
                            machine.Reset();
                        }

                        // Select + R1 = Save State
                        if (Raylib.IsGamepadButtonPressed(0, GamepadButton.RightTrigger1) || Raylib.IsKeyPressed(KeyboardKey.F5))
                        {
                            machine.SaveState("steamdeck.state");
                        }

                        // Select + L1 = Load State
                        if (Raylib.IsGamepadButtonPressed(0, GamepadButton.LeftTrigger1) || Raylib.IsKeyPressed(KeyboardKey.F8))
                        {
                            machine.LoadState("steamdeck.state");
                        }
                    }

                    // --- EMULATOR LOGIC ---
                    byte pad = 0xFF;
                    if (Raylib.IsKeyDown(KeyboardKey.Up) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceUp)) pad &= 0xFE;
                    if (Raylib.IsKeyDown(KeyboardKey.Down) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceDown)) pad &= 0xFD;
                    if (Raylib.IsKeyDown(KeyboardKey.Left) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceLeft)) pad &= 0xFB;
                    if (Raylib.IsKeyDown(KeyboardKey.Right) || Raylib.IsGamepadButtonDown(0, GamepadButton.LeftFaceRight)) pad &= 0xF7;
                    if (Raylib.IsKeyDown(KeyboardKey.O) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceDown)) pad &= 0xEF;
                    if (Raylib.IsKeyDown(KeyboardKey.P) || Raylib.IsGamepadButtonDown(0, GamepadButton.RightFaceRight)) pad &= 0xDF;

                    machine.IoBus.Joypad1Keyboard = pad;

                    machine.RunFrame();

                    // C. Flush Audio Buffer to the Sound Card
                    // THE FIX: Do not wait for Raylib to ask! Shove the exact number of 
                    // samples we generated this frame directly into the ring buffer.
                    float[] samples = audioPlayer.GetQueuedSamples();
                    if (samples.Length > 0)
                    {
                        unsafe
                        {
                            fixed (float* p = samples)
                            {
                                Raylib.UpdateAudioStream(audioStream, p, samples.Length);
                            }
                        }
                    }

                    //if (Raylib.IsAudioStreamProcessed(audioStream))
                    //{
                    //    if (audioPlayer.AvailableSamples >= 1024)
                    //    {
                    //        float[] samples = audioPlayer.GetSamples(1024);
                    //        unsafe
                    //        {
                    //            fixed (float* p = samples)
                    //            {
                    //                Raylib.UpdateAudioStream(audioStream, p, 1024);
                    //            }
                    //        }
                    //    }
                    //}

                    int[] frameBuffer = machine.VideoProcessor.FrameBuffer;
                    for (int i = 0; i < frameBuffer.Length; i++)
                    {
                        int argb = frameBuffer[i];
                        pixelBuffer[i * 4 + 0] = (byte)((argb >> 16) & 0xFF);
                        pixelBuffer[i * 4 + 1] = (byte)((argb >> 8) & 0xFF);
                        pixelBuffer[i * 4 + 2] = (byte)(argb & 0xFF);
                        pixelBuffer[i * 4 + 3] = 255;
                    }

                    unsafe
                    {
                        fixed (byte* p = pixelBuffer)
                        {
                            Raylib.UpdateTexture(vdpTexture, p);
                        }
                    }

                    Raylib.BeginDrawing();
                    Raylib.ClearBackground(Color.Black);

                    float scale = Math.Min((float)Raylib.GetScreenWidth() / 256, (float)Raylib.GetScreenHeight() / 192);
                    Rectangle sourceRec = new Rectangle(0, 0, 256, 192);
                    Rectangle destRec = new Rectangle(
                        (Raylib.GetScreenWidth() - (256 * scale)) * 0.5f,
                        (Raylib.GetScreenHeight() - (192 * scale)) * 0.5f,
                        256 * scale, 192 * scale);

                    Raylib.DrawTexturePro(vdpTexture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);

                    Raylib.EndDrawing();
                }
            }

            Raylib.UnloadTexture(vdpTexture);
            Raylib.UnloadAudioStream(audioStream);
            Raylib.CloseAudioDevice();
            Raylib.CloseWindow();
        }

        private static string[] GetEmbeddedRoms()
        {
            // Pulls all embedded files and filters for Master System and Game Gear
            return System.Reflection.Assembly.GetExecutingAssembly()
                .GetManifestResourceNames()
                .Where(r => r.EndsWith(".sms", StringComparison.OrdinalIgnoreCase) ||
                            r.EndsWith(".gg", StringComparison.OrdinalIgnoreCase))
                .ToArray();
        }

        private static byte[] LoadEmbeddedRomBytes(string resourceName)
        {
            using (Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            using (MemoryStream ms = new MemoryStream())
            {
                stream.CopyTo(ms);
                return ms.ToArray();
            }
        }
    }
}