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


// Good references http://ref.x86asm.net/
// http://sandpile.org
// http://en.wikipedia.org/wiki/X86_architecture

namespace Lomont.Binary.X86
{
    /// <summary>
    /// Represent a single x86 instruction as would be decoded from a binary.
    /// This also contains metadata for the opcode for statistics gathering and analysis.
    /// </summary>
    public class X86Instruction
    {
        /// <summary>
        /// Create a new x86 instruction
        /// </summary>
        public X86Instruction()
        {
            Data = new List<byte>();
            Definition = null;
            Prefixes = new List<Prefix>();
            // todo- delay create these fields? would likely be better, especially the third one?
            // Use Lazy<T> if possible to make implementation clean
            SourceOperand = new X86Operand();
            DestinationOperand = new X86Operand();
            ThirdOperand = new X86Operand();
        }

        /// <summary>
        /// The x86 instruction definition. This definition is the template used to 
        /// decode this instruction.
        /// </summary>
        public X86InstructionDefinition Definition { get; set; }

        /// <summary>
        /// The bit size of the operation or register.
        /// </summary>
        public enum DataSize
        {
            // NOTE: The value must be the size in bytes

            /// <summary>
            /// 0 bits (0 bytes)
            /// </summary>
            None = 0,

            /// <summary>
            /// 8 bits (1 byte)
            /// </summary>
            Byte = 1,
            /// <summary>
            /// 16 bits (2 bytes)
            /// </summary>
            Word = 2,
            /// <summary>
            /// 32 bits (4 bytes)
            /// </summary>
            Dword = 4,
            /// <summary>
            /// Segment, offset for 16:32 bit values
            /// </summary>
            Segm16m32 = 6,
            /// <summary>
            /// 64 bits (8 bytes)
            /// </summary>
            Qword = 8,
            /// <summary>
            /// 80 bit (10 bytes)
            /// </summary>
            ExtendedReal = 10,
            /// <summary>
            /// 128 bits (16 bytes)
            /// </summary>
            DoubleQword = 16
        }


        /// <summary>
        /// The start address of the instruction
        /// </summary>
        public ulong StartAddress { get; set; }
        
        /// <summary>
        /// The data bytes making up the instruction
        /// </summary>
        public List<byte> Data { get; set; }

        /// <summary>
        /// The mnemonic of this instruction
        /// </summary>
        public X86Mnemonic Mnemonic { get {if (Definition == null) return X86Mnemonic.UNKNOWN; return Definition.Mnenomic;} }

        /// <summary>
        /// Instruction prefixes, up to 4, one from each group
        /// See Intel instruction set docs Vol 2A, section 2.1.
        /// These are in the order seen in memory. Todo - pack 
        /// into uint with accessors for storage size?
        /// </summary>
        public List<Prefix> Prefixes { get; set; }

        /// <summary>
        /// Get a text representation of this instruction
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        { // todo- make opcodes UPPERCASE and all other letters lower, or vice versa, for easy parsing.
            if (ThirdOperand.OperandFormat != X86Operand.Format.Empty)
                return String.Format("{0} {2},{1},{3}", Mnemonic, SourceOperand, DestinationOperand,ThirdOperand);
            if ((SourceOperand.OperandFormat != X86Operand.Format.Empty) && (DestinationOperand.OperandFormat != X86Operand.Format.Empty))
                return String.Format("{0} {2},{1}",Mnemonic, SourceOperand, DestinationOperand);
            if (SourceOperand.OperandFormat != X86Operand.Format.Empty)
                return String.Format("{0} {1}",Mnemonic, SourceOperand);
            if (DestinationOperand.OperandFormat != X86Operand.Format.Empty)
                return String.Format("{0} {1}", Mnemonic, DestinationOperand);
            return String.Format("{0}", Mnemonic);
        }

        /// <summary>
        /// The length in bytes of the instruction
        /// </summary>
        public int Length {get {return Data.Count;}}

        /// <summary>
        /// REX byte for 64 bit instruction, 0 if not present
        /// </summary>
        public byte REX { get; set; }

        /// <summary>
        /// true if this instruction has an operand size override prefix
        /// </summary>
        public bool OperandSizeOverridden { get; set; }

        /// <summary>
        /// true if this instruction has an address size override prefix
        /// </summary>
        public bool AddressSizeOverriden { get; set; }

        /// <summary>
        /// Set to null, unless there is a segment register override
        /// </summary>
        public X86Register SegmentOverride { get; set; }

        /// <summary>
        /// The source operand
        /// </summary>
        public X86Operand SourceOperand { get; set; }
        /// <summary>
        /// The destination operand
        /// </summary>
        public X86Operand DestinationOperand { get; set; }
        /// <summary>
        /// The third operand, sometimes required
        /// </summary>
        public X86Operand ThirdOperand { get; set; }

        /// <summary>
        /// The size of the displacement field for this instruction, or None if none.
        /// </summary>
        public DataSize DisplacementSize { get; set; }

        /// <summary>
        /// Does this instruction have a displacement? Value reads from the DisplacementSize field
        /// // todo - read from operands?
        /// </summary>
        public bool HasDisplacement { get { return DisplacementSize != DataSize.None; } }

        /// <summary>
        /// The size of the immediate field for this instruction, or None if none.
        /// TODO - remove? ENTER opcode has two immediate operands of different size. Needs refactored.
        /// </summary>
        public DataSize ImmediateSize {get; set; }

        /// <summary>
        /// Does this instruction have an immediate value? Value reads from ImmediateSize field.
        /// todo - read from operands?
        /// </summary>
        public bool HasImmediate { get { return ImmediateSize != DataSize.None; } }

        /// <summary>
        /// Instruction prefixes, in 4 groups from Intel documentation.
        /// Todo - move to tables section, or out of instruction.
        /// </summary>
        public enum Prefix
        {
            None = 0x00, // prefix value denoting no prefix

            // group 1
            LOCK  = 0xF0,  // thread locking
            // repeat can only be used with string and I/O instructions:
            //     MOVS, CMPS, SCAS, LODS, STOS, INS, OUTS
            REPNZ = 0xF2,  // same as REPNE, also mandatory prefix for some instructions
            REPZ  = 0xF3,  // or REP or REPE, also mandatory prefix for some instructions
            
            // group 2
            OverrideCS = 0x2E, // reserved if used with branch instruction - see below
            OverrideSS = 0x36, // reserved if used with branch instruction
            OverrideDS = 0x3E, // reserved if used with branch instruction - see below
            OverrideES = 0x26, // reserved if used with branch instruction
            OverrideFS = 0x64, // reserved if used with branch instruction
            OverrideGS = 0x65, // reserved if used with branch instruction
            BranchNo   = 0x2E, // branch hint not taken
            BranchYes  = 0x3E, // branch hint taken

            // group 3
            // only legal when used with branch instructions Jcc
            OperandSizeOverride = 0x66, // also used sometimes as mandatory prefix for some instructions

            // group 4
            // only legal when used with branch instructions Jcc
            AddressSizeOverride = 0x67 
        } // Prefix

        /// <summary>
        /// Does this instruction have a ModRM byte?
        /// </summary>
        public bool HasModRM { get {return ModRM != null;}  }
        /// <summary>
        /// Does this instruction have a SIB?
        /// </summary>
        public bool HasSIB { get { return SIB != null; } }

        /// <summary>
        /// Set the ModRM byte
        /// </summary>
        /// <param name="val"></param>
        public void SetModRM(byte value)
        {
            ModRM = new ModRMHolder(value);
        }

        /// <summary>
        /// Set the SIB value
        /// </summary>
        /// <param name="value"></param>
        public void SetSIB(byte value)
        {
            SIB = new SIBHolder(value);
        }

        /// <summary>
        /// null or the ModRM byte
        /// </summary>
        public ModRMHolder ModRM { get; private set; }

        /// <summary>
        /// Represent the ModRM value
        /// </summary>
        public class ModRMHolder
        {
            public ModRMHolder(byte val)
            {
                Value = val;
            }

            /// <summary>
            /// The mod field of the ModRM byte, valid if HasModRM true
            /// </summary>
            public byte Mod { get { return (byte)(Value >> 6);  } }

            /// <summary>
            /// The reg field of the ModRM byte, valid if HasModRM true
            /// </summary>
            public byte Reg { get { return (byte)((Value >> 3)&7);  } }

            /// <summary>
            /// The R/M field of the ModRM byte, valid if HasModRM true
            /// </summary>
            public byte RM { get { return (byte)(Value&7);  } }

            /// <summary>
            /// The byte value of the ModR/M byte
            /// </summary>
            public byte Value { get; private set; }
        }

        /// <summary>
        /// Represent the SIB value
        /// </summary>
        public class SIBHolder
        {
            public SIBHolder(byte val)
            {
                Value = val;
            }

            /// <summary>
            /// SIB Scale value
            /// </summary>
            public byte Scale { get { return (byte)(Value >> 6); } }
            
            /// <summary>
            /// SIB Index value
            /// </summary>
            public byte Index { get { return (byte)((Value >> 3) & 7); } }
            
            /// <summary>
            /// SIB Base value
            /// </summary>
            public byte Base { get { return (byte)(Value & 7); } }

            /// <summary>
            /// The byte value of the SIB byte
            /// </summary>
            public byte Value { get; private set; }
        }

        /// <summary>
        /// null or the SIB value
        /// </summary>
        public SIBHolder SIB { get; private set; }
    }
}
