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

namespace Cosmos.Compiler.Assembler.X86 {
    public abstract class InstructionWithDestinationAndSource : InstructionWithDestination, IInstructionWithSource {
        public ElementReference SourceRef {
            get;
            set;
        }

        public RegistersEnum? SourceReg
        {
            get;
            set;
        }

        public uint? SourceValue
        {
            get;
            set;
        }

        public bool SourceIsIndirect {
            get;
            set;
        }

        public int SourceDisplacement {
            get;
            set;
        }

        public bool SourceEmpty
        {
            get;
            set;
        }
        protected string GetSourceAsString() {
            string xDest = "";
            if ((SourceValue.HasValue || SourceRef != null) &&
                SourceIsIndirect &&
                SourceReg != null) {
                throw new Exception("[Scale*index+base] style addressing not supported at the moment");
            }
            if (SourceRef != null) {
                xDest = SourceRef.ToString();
            } else {
                if (SourceReg != null) {
                    xDest = Registers.GetRegisterName(SourceReg.Value);
                } else {
                    if (SourceValue.HasValue)
                        xDest = "0x" + SourceValue.GetValueOrDefault().ToString("X").ToUpperInvariant();
                }
            }
            if (SourceDisplacement != 0) {
                xDest += " + " + SourceDisplacement;
            }
            if (SourceIsIndirect) {
                return "[" + xDest + "]";
            } else {
                return xDest;
            }
        }

        public override bool IsComplete( Cosmos.Compiler.Assembler.Assembler aAssembler )
        {
            if (SourceRef != null) {
                ulong xAddress;
                return base.IsComplete(aAssembler) && SourceRef.Resolve(aAssembler, out xAddress);
            }
            return base.IsComplete(aAssembler);
        }

        public override void UpdateAddress( Cosmos.Compiler.Assembler.Assembler aAssembler, ref ulong aAddress )
        {
            if (SourceRef != null) {
                SourceValue = 0xFFFFFFFF;
            }
            base.UpdateAddress(aAssembler, ref aAddress);
        }

        public override byte[] GetData(Cosmos.Compiler.Assembler.Assembler aAssembler) {
            if (SourceRef != null) {
                ulong xAddress = 0;
                if (!SourceRef.Resolve(aAssembler, out xAddress)) {
                    throw new Exception("Cannot resolve SourceRef!");
                }
                SourceValue = (uint)xAddress;
            }
            return base.GetData(aAssembler);
        }
        public override void WriteText( Cosmos.Compiler.Assembler.Assembler aAssembler, System.IO.TextWriter aOutput )
        {
            aOutput.Write(mMnemonic);
            String destination=this.GetDestinationAsString();
            if (!destination.Equals("")){
                aOutput.Write(" ");
                aOutput.Write(destination);
                string source = this.GetSourceAsString();
                if (!(SourceEmpty && source.Equals(""))){
                    aOutput.Write(", ");
                    aOutput.Write(source);
                 }
            }
        }
    }
}