using X86Disassembler.X86.Operands;
namespace X86Disassembler.X86;
using System.Text;
using System.Collections.Generic;
/// 
/// Core x86 instruction disassembler
/// 
public class Disassembler
{
    // The buffer containing the code to disassemble
    private readonly byte[] _codeBuffer;
    // The length of the buffer
    private readonly int _length;
    // The base address of the code
    private readonly ulong _baseAddress;
    // Segment override prefixes
    private static readonly byte[] SegmentOverridePrefixes = {0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65};
    /// 
    /// Initializes a new instance of the Disassembler class
    /// 
    /// The buffer containing the code to disassemble
    /// The base address of the code
    public Disassembler(byte[] codeBuffer, ulong baseAddress)
    {
        _codeBuffer = codeBuffer;
        _length = codeBuffer.Length;
        _baseAddress = baseAddress;
    }
    /// 
    /// Checks if a byte is a segment override prefix
    /// 
    /// The byte to check
    /// True if the byte is a segment override prefix
    private bool IsSegmentOverridePrefix(byte b)
    {
        return Array.IndexOf(SegmentOverridePrefixes, b) >= 0;
    }
    /// 
    /// Gets the segment override name for a prefix byte
    /// 
    /// The prefix byte
    /// The segment override name
    private string GetSegmentOverrideName(byte prefix)
    {
        return prefix switch
        {
            0x26 => "es",
            0x2E => "cs",
            0x36 => "ss",
            0x3E => "ds",
            0x64 => "fs",
            0x65 => "gs",
            _ => string.Empty
        };
    }
    /// 
    /// Disassembles the code buffer and returns the disassembled instructions
    /// 
    /// A list of disassembled instructions
    public List Disassemble()
    {
        List instructions = new List();
        // Create an instruction decoder
        InstructionDecoder decoder = new InstructionDecoder(_codeBuffer, _length);
        // Decode instructions until the end of the buffer is reached
        while (true)
        {
            int position = decoder.GetPosition();
            // Check if we've reached the end of the buffer
            if (!decoder.CanReadByte())
            {
                break;
            }
            // If no special case applies, decode normally
            Instruction? instruction = decoder.DecodeInstruction();
            if (instruction != null)
            {
                // Adjust the instruction address to include the base address
                instruction.Address += _baseAddress;
                // Add the instruction to the list
                instructions.Add(instruction);
            }
            else
            {
                // If decoding failed, create a dummy instruction for the unknown byte
                byte unknownByte = decoder.ReadByte();
                Instruction dummyInstruction = new Instruction
                {
                    Address = _baseAddress + (uint) position,
                    Type = InstructionType.Unknown,
                    StructuredOperands = [OperandFactory.CreateImmediateOperand(unknownByte, 8),]
                };
                instructions.Add(dummyInstruction);
            }
        }
        return instructions;
    }
}