using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lomont.Binary.X86
    {
    /// <summary>
    /// Represent an x86 register
    /// </summary>
    public class X86Register
        {

        // todo - where is a good place for this?
        // todo - can remove?
        public enum RegisterType
            {
            r8,  // 8 bit
            r16, // 16 bit
            r32, // 32 bit
            r64, // 64 bit 
            mmx, // MMX register
            xmm  // SSE (XMM) register
            }

        /// <summary>
        /// Bit length of the register. Todo - use enum
        /// </summary>
        public int BitSize { get; private set; }
        /// <summary>
        /// Is it legal in 32 bit mode?
        /// </summary>
        public bool Available32 { get; private set; }
        /// <summary>
        /// What is the register name
        /// </summary>
        public RegisterName Name { get; private set; }

        /// <summary>
        /// Create an X86 register.
        /// </summary>
        /// <param name="bitSize">The number of bits</param>
        /// <param name="availableIn32Bit">Is this allowed in 32 bit mode?</param>
        /// <param name="name">The name of the register</param>
        protected X86Register(int bitSize, bool availableIn32Bit, RegisterName name)
            {
            BitSize = bitSize;
            Available32 = availableIn32Bit;
            Name = name;
            }
        /// <summary>
        /// 
        /// </summary>
        private X86Register()
            { // hide this to make people use the static register info
            }

        public override string ToString()
            {
            return Name.ToString();
            }

        // all the common registers. todo - finish?
        public static X86Register AL = new X86Register(8, true, RegisterName.AL);
        public static X86Register AH = new X86Register(8, true, RegisterName.AH);
        public static X86Register AX = new X86Register(16, true, RegisterName.AX);
        public static X86Register EAX = new X86Register(32, true, RegisterName.EAX);
        public static X86Register RAX = new X86Register(64, false, RegisterName.RAX);

        public static X86Register BL = new X86Register(8, true, RegisterName.BL);
        public static X86Register BH = new X86Register(8, true, RegisterName.BH);
        public static X86Register BX = new X86Register(16, true, RegisterName.BX);
        public static X86Register EBX = new X86Register(32, true, RegisterName.EBX);
        public static X86Register RBX = new X86Register(64, false, RegisterName.RBX);

        public static X86Register CL = new X86Register(8, true, RegisterName.CL);
        public static X86Register CH = new X86Register(8, true, RegisterName.CH);
        public static X86Register CX = new X86Register(16, true, RegisterName.CX);
        public static X86Register ECX = new X86Register(32, true, RegisterName.ECX);
        public static X86Register RCX = new X86Register(64, false, RegisterName.RCX);

        public static X86Register DL = new X86Register(8, true, RegisterName.DL);
        public static X86Register DH = new X86Register(8, true, RegisterName.DH);
        public static X86Register DX = new X86Register(16, true, RegisterName.DX);
        public static X86Register EDX = new X86Register(32, true, RegisterName.EDX);
        public static X86Register RDX = new X86Register(64, false, RegisterName.RDX);

        public static X86Register MMX0 = new X86Register(64, true, RegisterName.MMX0);
        public static X86Register MMX1 = new X86Register(64, true, RegisterName.MMX1);
        public static X86Register MMX2 = new X86Register(64, true, RegisterName.MMX2);
        public static X86Register MMX3 = new X86Register(64, true, RegisterName.MMX3);
        public static X86Register MMX4 = new X86Register(64, true, RegisterName.MMX4);
        public static X86Register MMX5 = new X86Register(64, true, RegisterName.MMX5);
        public static X86Register MMX6 = new X86Register(64, true, RegisterName.MMX6);
        public static X86Register MMX7 = new X86Register(64, true, RegisterName.MMX7);

        public static X86Register XMM0 = new X86Register(128, true, RegisterName.XMM0);
        public static X86Register XMM1 = new X86Register(128, true, RegisterName.XMM1);
        public static X86Register XMM2 = new X86Register(128, true, RegisterName.XMM2);
        public static X86Register XMM3 = new X86Register(128, true, RegisterName.XMM3);
        public static X86Register XMM4 = new X86Register(128, true, RegisterName.XMM4);
        public static X86Register XMM5 = new X86Register(128, true, RegisterName.XMM5);
        public static X86Register XMM6 = new X86Register(128, true, RegisterName.XMM6);
        public static X86Register XMM7 = new X86Register(128, true, RegisterName.XMM7);

        public static X86Register XMM8 = new X86Register(128, false, RegisterName.XMM8);
        public static X86Register XMM9 = new X86Register(128, false, RegisterName.XMM9);
        public static X86Register XMM10 = new X86Register(128, false, RegisterName.XMM10);
        public static X86Register XMM11 = new X86Register(128, false, RegisterName.XMM11);
        public static X86Register XMM12 = new X86Register(128, false, RegisterName.XMM12);
        public static X86Register XMM13 = new X86Register(128, false, RegisterName.XMM13);
        public static X86Register XMM14 = new X86Register(128, false, RegisterName.XMM14);
        public static X86Register XMM15 = new X86Register(128, false, RegisterName.XMM15);

        public static X86Register SP = new X86Register(16, true, RegisterName.SP);
        public static X86Register ESP = new X86Register(32, true, RegisterName.ESP);
        public static X86Register RSP = new X86Register(64, false, RegisterName.RSP);

        public static X86Register BP = new X86Register(16, true, RegisterName.BP);
        public static X86Register EBP = new X86Register(32, true, RegisterName.EBP);
        public static X86Register RBP = new X86Register(64, false, RegisterName.RBP);

        public static X86Register SI = new X86Register(16, true, RegisterName.SI);
        public static X86Register ESI = new X86Register(32, true, RegisterName.ESI);
        public static X86Register RSI = new X86Register(64, false, RegisterName.RSI);

        public static X86Register DI = new X86Register(16, true, RegisterName.DI);
        public static X86Register EDI = new X86Register(32, true, RegisterName.EDI);
        public static X86Register RDI = new X86Register(64, false, RegisterName.RDI);

        public static X86Register FLAGS = new X86Register(16, true, RegisterName.FLAGS);
        public static X86Register EFLAGS = new X86Register(32, true, RegisterName.EFLAGS);
        public static X86Register RFLAGS = new X86Register(64, false, RegisterName.RFLAGS);

        public static X86Register CS = new X86Register(16, true, RegisterName.CS);
        public static X86Register SS = new X86Register(16, true, RegisterName.SS);
        public static X86Register DS = new X86Register(16, true, RegisterName.DS);
        public static X86Register ES = new X86Register(16, true, RegisterName.ES);
        public static X86Register FS = new X86Register(16, true, RegisterName.FS);
        public static X86Register GS = new X86Register(16, true, RegisterName.GS);

        public static X86Register ST = new X86Register(80, true, RegisterName.ST);
        public static X86Register ST0 = new X86Register(80, true, RegisterName.ST0);
        public static X86Register ST1 = new X86Register(80, true, RegisterName.ST1);
        public static X86Register ST2 = new X86Register(80, true, RegisterName.ST2);
        public static X86Register ST3 = new X86Register(80, true, RegisterName.ST3);
        public static X86Register ST4 = new X86Register(80, true, RegisterName.ST4);
        public static X86Register ST5 = new X86Register(80, true, RegisterName.ST5);
        public static X86Register ST6 = new X86Register(80, true, RegisterName.ST6);
        public static X86Register ST7 = new X86Register(80, true, RegisterName.ST7);

        /// <summary>
        /// Try to parse the register. Return false on fail
        /// </summary>
        /// <param name="text"></param>
        /// <param name="register"></param>
        /// <returns></returns>
        public static bool TryParse(string text, out X86Register register)
            {
            register = null; // todo - look up honestly?
            RegisterName  name;
            if (Enum.TryParse(text, out name))
                {
                register = Registers[(int)name];
                return true;
                }
            return false;
            }

        /// <summary>
        /// The set of registers, must be in same order as enumerated type
        /// </summary>
        static readonly X86Register[] Registers = 
        {
            AL,
            AH,
            AX,
            EAX,
            RAX,
            BL,
            BH,
            BX,
            EBX,
            RBX,
            CL,
            CH,
            CX,
            ECX,
            RCX,
            DL,
            DH,
            DX,
            EDX,
            RDX,

            CS, SS, DS, ES, FS, GS, // segments
            SI, ESI, RSI, DI, EDI, RDI, SP, ESP, RSP, BP, EBP, RBP,
            FLAGS, EFLAGS, RFLAGS,

            MMX0, MMX1, MMX2, MMX3, MMX4, MMX5, MMX6, MMX7,

            XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
            XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,

            ST, ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, // floating point regs

            //GDTR, LDTR, IDTR, // system table pointer registers
            //// todo - also add task register. Name?
            //// todo - MSR registers?

            // R0,R1,R2,R3,R4,R5,R6,R7, // todo - are these aliases? are they legal?
            //R8, R9, R10, R11, R12, R13, R14, R15, // 64 bit
            //R8D, R9D, R10D, R11D, R12D, R13D, R14D, R15D,  // dword = 32 bit
            //R8W, R9W, R10W, R11W, R12W, R13W, R14W, R15W, // word 16 bit lower
            //R8B, R9B, R10B, R11B, R12B, R13B, R14B, R15B, // byte 8 bit lowest
            
            //CR0, CR1, CR2, CR3, CR4, // control registers
            //TR4, TR5, TR6, TR7, // test registers
            //DR0, DR1, DR2, DR3, DR6, DR7, // debug registers
            //IP, EIP, RIP, // instruction pointer

            //MXCSR // XMM control register

        };


        /// <summary>
        /// The possible registers to use in instructions.
        /// </summary>
        public enum RegisterName
            {
            AL,
            AH,
            AX,
            EAX,
            RAX,
            BL,
            BH,
            BX,
            EBX,
            RBX,
            CL,
            CH,
            CX,
            ECX,
            RCX,
            DL,
            DH,
            DX,
            EDX,
            RDX,

            CS, SS, DS, ES, FS, GS, // segments
            SI, ESI, RSI, DI, EDI, RDI, SP, ESP, RSP, BP, EBP, RBP,
            FLAGS, EFLAGS, RFLAGS,


            MMX0, MMX1, MMX2, MMX3, MMX4, MMX5, MMX6, MMX7,


            XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
            XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,

            ST, ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, // floating point regs. Note ST = ST0

            GDTR, LDTR, IDTR, // system table pointer registers
            // todo - also add task register. Name?
            // todo - MSR registers?


            // R0,R1,R2,R3,R4,R5,R6,R7, // todo - are these aliases? are they legal?
            R8, R9, R10, R11, R12, R13, R14, R15, // 64 bit
            R8D, R9D, R10D, R11D, R12D, R13D, R14D, R15D,  // dword = 32 bit
            R8W, R9W, R10W, R11W, R12W, R13W, R14W, R15W, // word 16 bit lower
            R8B, R9B, R10B, R11B, R12B, R13B, R14B, R15B, // byte 8 bit lowest

            CR0, CR1, CR2, CR3, CR4, // control registers
            TR4, TR5, TR6, TR7, // test registers
            DR0, DR1, DR2, DR3, DR6, DR7, // debug registers
            IP, EIP, RIP, // instruction pointer


            MXCSR // XMM control register

            }
        }
    }
