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

namespace Lomont.Binary.X86
    {

    /// <summary>
    /// Represent an Intel processor mode. This includes bit sizes,
    /// protected/real mode, 16/32/64 bit, etc.
    /// </summary>
    public class X86ProcessorMode
        {
        /// <summary>
        /// Operation modes dictate how some instructions are encoded and default
        /// sizes for addresses and operands.
        /// In hardware:
        /// The processor is in RealAddress mode after power up. PE flag in CR0 then sets 
        /// real mode or protected mode. The VM flag in the EFLAGS is set on a per-process 
        /// basis to select between protected and virtual 8086 mode. The LMA bit selects the 
        /// two 64 bit modes, collectively called IA-32e. In IA-32e mode, the sub mode is 
        /// selected by the CS.L bit between 64-bit or compatibility modes.
        /// Finally, SMM mode is entered whenever the chip receives a SMI (pin signal), and 
        /// can happen from real address, protected, virtual-8086, or IA-32e modes. Upon
        /// executing the RSM instruction, the previous mode is restored.
        /// </summary>
        public enum OperationModes
            {
            /// <summary>
            /// A 64 bit mode.
            /// This is technically a sub mode of IA-32e mode. This is a mode to run 
            /// 64 bit code.
            /// </summary>
            SixtyFourBit,
            /// <summary>
            /// A 32 bit mode.
            /// This is the most common mode of the processor. Provides hardware support
            /// for processes, and allows running of processes tagged with Virtual8086
            /// </summary>
            Protected,
            /// <summary>
            /// A 32 bit mode.
            /// A backwards compatibility mode used to run old 16 bit code from the 8086 
            /// while in Protected mode.
            /// </summary>
            Virtual8086,
            /// <summary>
            /// A 16 bit mode.
            /// This is the original 8086 mode, the processor mode upon power up. 
            /// It runs 16 bit code and is often used to change state to Protected or
            /// SystemManagement.
            /// </summary>
            RealAddress,
            /// <summary>
            /// A 32 bit mode.
            /// Known as SMM, this mode is rare and used provides a transparent mechanism to 
            /// for implementing platform specific functions. See the Intel manuals for more
            /// </summary>
            SystemManagement,
            /// <summary>
            /// A 64 bit mode.
            /// This is technically a sub mode of IA-32e mode, which is a mode to run 32 bit
            /// code under a 64 bit operating system.
            /// </summary>
            Compatability
            }

        /// <summary>
        /// The operation mode of the disassembler. Default is SixtyFourBitMode.
        /// </summary>
        public OperationModes OperationMode { get; set; }

        public enum MemoryModels
            {

            }
        /// <summary>
        /// Default memory operand
        /// </summary>
        public MemoryModels MemoryModel { get; set; }

        public enum BitMode
            {
            Bit16,
            Bit32,
            Bit64
            }
        /// <summary>
        /// Default addressing field bitSize. 
        /// This is on a per code segment basis, set in the segment descriptor D flag.
        /// When D flag set, the mode is 32 bit by default. When in real address, virtual 8086, or SMM mode, 
        /// the default sizes are 16 bits. In 64 bit mode default address is 64 bits and default operand is 32 bits.
        /// See Ch 3.6, 3.7 in Intel manual, volume 1 
        /// </summary>
        public BitMode AddressingMode { get; set; }

        /// <summary>
        /// Default operand field bitSize
        /// This is on a per code segment basis, set in the segment descriptor D flag.
        /// When D flag set, the mode is 32 bit by default. When in real address, virtual 8086, or SMM mode, 
        /// the default sizes are 16 bits. In 64 bit mode default address is 64 bits and default operand is 32 bits.
        /// See Ch 3.6, 3.7 in Intel manual, volume 1 
        /// </summary>
        public BitMode OperandMode { get; set; }

        public X86ProcessorMode()
            {
            OperationMode = OperationModes.SixtyFourBit; // the default mode
            // todo - move processor info to another struct or class, set in here
            AddressingMode = BitMode.Bit32; // todo - name these better, and set/read them nicely with other fields.
            OperandMode = BitMode.Bit32;
            }

        /// <summary>
        /// Select the operand size based on system settings and if 
        /// the instruction has an operand override
        /// Implement table 3.3, volume 1.
        /// </summary>
        /// <param name="hasOverride"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException">Unknown state</exception>
        /// <exception cref="NotImplementedException">Unfinished</exception>
        public X86Instruction.DataSize OperandSize(bool hasOperandOverride)
            {
            switch (OperationMode)
                {
                case OperationModes.RealAddress:
                case OperationModes.Virtual8086:
                case OperationModes.SystemManagement:
                    return X86Instruction.DataSize.Word;
                case OperationModes.Protected:
                case OperationModes.Compatability:
                    if (hasOperandOverride ^ (OperandMode == BitMode.Bit32))
                        return X86Instruction.DataSize.Dword;
                    else
                        return X86Instruction.DataSize.Word;
                case OperationModes.SixtyFourBit:
                    throw new NotImplementedException("64 operand override not finished");
                default:
                    throw new ArgumentOutOfRangeException("Unknown operation mode " + OperationMode);
                }
            }

        /// <summary>
        /// Select the address size based on system settings and if 
        /// the instruction has an address override
        /// Implement table 3.3, volume 1.
        /// </summary>
        /// <param name="hasOverride"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException">Unknown state</exception>
        /// <exception cref="NotImplementedException">Unfinished</exception>
        public X86Instruction.DataSize AddressSize(bool hasAddressOverride)
            {
            switch (OperationMode)
                {
                case OperationModes.RealAddress:
                case OperationModes.Virtual8086:
                case OperationModes.SystemManagement:
                    return X86Instruction.DataSize.Word;
                case OperationModes.Protected:
                case OperationModes.Compatability:
                    if (hasAddressOverride ^ (OperandMode == BitMode.Bit32))
                        return X86Instruction.DataSize.Dword;
                    else
                        return X86Instruction.DataSize.Word;

                case OperationModes.SixtyFourBit:
                    throw new NotImplementedException("64 operand override not finished");
                default:
                    throw new ArgumentOutOfRangeException("Unknown operation mode " + OperationMode);
                }
            }
        }
    }
