using Core.Audio;
using Core.Interfaces;
using Core.Video;

namespace Core.Io
{
    public class SmsIoBus : IIoBus
    {
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
        public SmsVdp VideoProcessor { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
        public SmsApu AudioProcessor { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

        // Joypad State (0xFF means no buttons pressed - the SMS uses Active-Low logic!)
        public byte Joypad1Keyboard = 0xFF;
        public byte Joypad1Gamepad = 0xFF;
        public byte Joypad2State = 0xFF;

        public byte ReadPort(ushort port)
        {
            // The Z80 can output 16-bit port addresses, but the Master System 
            // hardware only physically wires up the bottom 8 bits.
            byte lowerPort = (byte)(port & 0xFF);

            if (lowerPort == 0x7E)
            {
                // VDP V-Counter (Vertical Scanline Position)
                return VideoProcessor.ReadVCounter();
            }
            if (lowerPort == 0x7F)
            {
                // THE FIX: VDP H-Counter (Horizontal Pixel Position)
                return VideoProcessor.ReadHCounter();
            }
            if (lowerPort >= 0x80 && lowerPort <= 0xBF)
            {
                // Even ports (like 0xBE) are Data. Odd ports (like 0xBF) are Control.
                if ((lowerPort & 0x01) == 0) return VideoProcessor.ReadDataPort();
                else return VideoProcessor.ReadControlPort();
            }
            if (lowerPort == 0xDC) return (byte)(Joypad1Keyboard & Joypad1Gamepad);
            
            if (lowerPort == 0xDD) return Joypad2State;

            return 0xFF; // Floating bus
        }

        public void WritePort(ushort port, byte value)
        {
            byte lowerPort = (byte)(port & 0xFF);

            if (lowerPort == 0x7E || lowerPort == 0x7F)
            {
                AudioProcessor.WritePort7F(value);
            }
            // THE FIX: Video Ports are mirrored across the entire 0x80 to 0xBF range!
            else if (lowerPort >= 0x80 && lowerPort <= 0xBF)
            {
                // Even ports are Data, Odd ports are Control
                if ((lowerPort & 0x01) == 0) VideoProcessor.WriteDataPort(value);
                else VideoProcessor.WriteControlPort(value);
            }
            else if (lowerPort <= 0x3F)
            {
                // Port 0x3E is used by the BIOS to enable/disable the cartridge slot
                // We can usually ignore this if we are just directly booting game ROMs!
            }
        }
    }
}