using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cosmos.Compiler.Assembler;
using Cosmos.Compiler.Assembler.X86;

namespace Cosmos.Compiler.XSharp {
    public class MemoryAction {
        public uint? Value;
        public readonly RegistersEnum? Register;
        public readonly ElementReference Reference;
        public readonly int Displacement;
        
        public override string ToString() {
            if (Value.HasValue) {
                return Value.Value.ToString();
            } else {
                if (Reference != null) {
                    return Reference.ToString();
                }
                return Registers.GetRegisterName(Register.Value);
            }
        }

        public static implicit operator MemoryAction(UInt32 aValue) {
            return new MemoryAction(aValue);
        }

        public static implicit operator MemoryAction(Register aRegister) {
            return new MemoryAction(aRegister.GetId());
        }

        public static MemoryAction operator ++(MemoryAction aTarget) {
            aTarget.ApplyToDest(new Inc());
            return null;
        }

        public static MemoryAction operator --(MemoryAction aTarget) {
            aTarget.ApplyToDest(new Dec());
            return null;
        }

        public bool IsIndirect { get; set; }
        // For registers
        public MemoryAction(RegistersEnum aRegister)
        {
            Register = aRegister;
        }

        public MemoryAction(RegistersEnum aRegister, int aDisplacement)
            : this(aRegister)
        {
            Displacement = aDisplacement;
        }
        // This form used for reading memory - Addresses are passed in
        public MemoryAction(ElementReference aValue, int aDisplacement)
            : this(aValue) {
            Displacement = aDisplacement;
        }
        public MemoryAction(ElementReference aValue) {
            Reference = aValue;
        }

        // For constants/literals
        public MemoryAction(UInt32 aValue, int aDisplacement)
            : this(aValue) {
            Displacement = aDisplacement;
        }

        public MemoryAction(UInt32 aValue) {
            Value = aValue;
        }

        public byte Size { get; set; }

        public static string SizeToString(byte aSize) {
            switch (aSize) {
                case 8:
                    return "byte";
                case 16:
                    return "word";
                case 32:
                    return "dword";
                case 64:
                    return "qword";
                default:
                    throw new Exception("Invalid size: " + aSize);
            }
        }

        //TODO: Put memory compare here - will later have to limit it to the size
        // variant and if possible to self usage, ie no assignments. May however result 
        // in too many class variants to be worth while.
        public void Compare(UInt32 aValue) {
            Value = aValue;
            ApplyToDestAndSource(new Compare());
        }

        private void ApplyToDest(InstructionWithDestinationAndSize aDest) {
            aDest.Size = Size;
            aDest.DestinationReg = Register;
            aDest.DestinationRef = Reference;
            aDest.DestinationIsIndirect = IsIndirect;
            aDest.DestinationDisplacement = Displacement;
        }

        private void ApplyToDestAndSource(InstructionWithDestinationAndSourceAndSize aInstruction) {
            aInstruction.Size = Size;
            aInstruction.DestinationReg = Register;
            aInstruction.DestinationRef = Reference;
            aInstruction.DestinationIsIndirect = IsIndirect;
            aInstruction.DestinationDisplacement = Displacement;
            aInstruction.SourceValue = Value.GetValueOrDefault();
        }
    }
}
