namespace X86Disassembler.X86.Handlers.Nop;
using Operands;
/// 
/// Handler for multi-byte NOP instructions (0x0F 0x1F ...)
/// These are used for alignment and are encoded as NOP operations with specific memory operands
/// 
public class MultiByteNopHandler : InstructionHandler
{
    // NOP variant information (ModR/M byte, expected bytes pattern, and operand creation info)
    private static readonly (byte ModRm, byte[] ExpectedBytes, RegisterIndex BaseReg, RegisterIndex? IndexReg, int Scale)[] NopVariants =
    {
        // 8-byte NOP: 0F 1F 84 00 00 00 00 00 (check longest patterns first)
        (0x84, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00 }, RegisterIndex.A, RegisterIndex.A, 1),
        
        // 7-byte NOP: 0F 1F 80 00 00 00 00
        (0x80, new byte[] { 0x00, 0x00, 0x00, 0x00 }, RegisterIndex.A, null, 0),
        
        // 6-byte NOP: 0F 1F 44 00 00 00
        (0x44, new byte[] { 0x00, 0x00, 0x00 }, RegisterIndex.A, RegisterIndex.A, 1),
        
        // 5-byte NOP: 0F 1F 44 00 00
        (0x44, new byte[] { 0x00, 0x00 }, RegisterIndex.A, RegisterIndex.A, 1),
        
        // 4-byte NOP: 0F 1F 40 00
        (0x40, new byte[] { 0x00 }, RegisterIndex.A, null, 0),
        
        // 3-byte NOP: 0F 1F 00
        (0x00, Array.Empty(), RegisterIndex.A, null, 0)
    };
    /// 
    /// Initializes a new instance of the MultiByteNopHandler class
    /// 
    /// The instruction decoder that owns this handler
    public MultiByteNopHandler(InstructionDecoder decoder)
        : base(decoder)
    {
    }
    /// 
    /// Checks if this handler can decode the given opcode
    /// 
    /// The opcode to check
    /// True if this handler can decode the opcode
    public override bool CanHandle(byte opcode)
    {
        // Multi-byte NOPs start with 0x0F
        if (opcode != 0x0F)
        {
            return false;
        }
        // Check if we have enough bytes to read the second opcode
        if (!Decoder.CanReadByte())
        {
            return false;
        }
        // Check if the second byte is 0x1F (part of the multi-byte NOP encoding)
        byte secondByte = Decoder.PeakByte();
        return secondByte == 0x1F;
    }
    /// 
    /// Decodes a multi-byte NOP instruction
    /// 
    /// The opcode of the instruction
    /// The instruction object to populate
    /// True if the instruction was successfully decoded
    public override bool Decode(byte opcode, Instruction instruction)
    {
        // Set the instruction type
        instruction.Type = InstructionType.Nop;
        // Read the second byte (0x1F)
        Decoder.ReadByte();
        // Check if we have enough bytes to read the ModR/M byte
        if (!Decoder.CanReadByte())
        {
            return false;
        }
        
        // Check if we have an operand size prefix (0x66)
        bool hasOperandSizePrefix = Decoder.HasOperandSizeOverridePrefix();
        
        // Determine the size of the operand
        int operandSize = hasOperandSizePrefix ? 16 : 32;
        
        // Read the ModR/M byte
        byte modRm = Decoder.ReadByte();
        
        // Default memory operand parameters
        RegisterIndex baseReg = RegisterIndex.A;
        RegisterIndex? indexReg = null;
        int scale = 0;
        
        // Try to find a matching NOP variant (we check longest patterns first)
        foreach (var (variantModRm, expectedBytes, variantBaseReg, variantIndexReg, variantScale) in NopVariants)
        {
            // Skip if ModR/M doesn't match
            if (variantModRm != modRm)
            {
                continue;
            }
            // Check if we have enough bytes for this pattern
            if (!Decoder.CanRead(expectedBytes.Length))
            {
                continue;
            }
            
            // Create a buffer to read the expected bytes
            byte[] buffer = new byte[expectedBytes.Length];
            
            // Read the bytes into the buffer without advancing the decoder position
            for (int i = 0; i < expectedBytes.Length; i++)
            {
                if (!Decoder.CanReadByte())
                {
                    break;
                }
                buffer[i] = Decoder.PeakByte(i);
            }
            
            // Check if the expected bytes match
            bool isMatch = true;
            for (int i = 0; i < expectedBytes.Length; i++)
            {
                if (buffer[i] != expectedBytes[i])
                {
                    isMatch = false;
                    break;
                }
            }
            
            // If we found a match, use it and stop checking
            if (isMatch)
            {
                baseReg = variantBaseReg;
                indexReg = variantIndexReg;
                scale = variantScale;
                
                // Consume the expected bytes
                for (int i = 0; i < expectedBytes.Length; i++)
                {
                    Decoder.ReadByte();
                }
                
                break;
            }
        }
        
        // Create the appropriate structured operand based on the NOP variant
        if (indexReg.HasValue && scale > 0)
        {
            // Create a scaled index memory operand (e.g., [eax+eax*1])
            instruction.StructuredOperands = 
            [
                OperandFactory.CreateScaledIndexMemoryOperand(
                    indexReg.Value, 
                    scale, 
                    baseReg, 
                    0, 
                    operandSize)
            ];
        }
        else
        {
            // Create a simple base register memory operand (e.g., [eax])
            instruction.StructuredOperands = 
            [
                OperandFactory.CreateBaseRegisterMemoryOperand(
                    baseReg, 
                    operandSize)
            ];
        }
        
        return true;
    }
}