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 Memory {
        public MemoryAction this[Address aAddress] {
            get {
                var xAddrDirect = aAddress as AddressDirect;
                if (xAddrDirect != null) {
                    if (xAddrDirect.Label != null) {
                        return new MemoryAction(ElementReference.New(xAddrDirect.Label));
                    } else {
                        if (xAddrDirect.Register != null) {
                            return new MemoryAction(xAddrDirect.Register.Value);
                        } else {
                            return new MemoryAction(xAddrDirect.Address);
                        }
                    }
                } else {
                    var xAddrIndirect = aAddress as AddressIndirect;
                    if (xAddrIndirect != null) {
                        if (xAddrIndirect.Reference != null) {
                            return new MemoryAction(xAddrIndirect.Reference, xAddrIndirect.Displacement) { IsIndirect = true };
                        } else {
                            if (xAddrIndirect.Register != null) {
                                return new MemoryAction(xAddrIndirect.Register.Value, xAddrIndirect.Displacement) { IsIndirect = true };
                            } else {
                                return new MemoryAction(xAddrIndirect.Address, xAddrIndirect.Displacement) { IsIndirect = true };
                            }
                        }
                    }
                    throw new Exception("Address type not supported!");
                }
            }
            set {
                if (value.Register!=null) {
                    var xAddrDirect = aAddress as AddressDirect;

                    if (xAddrDirect != null) {
                        if (xAddrDirect.Label != null) {
                            new Move { DestinationRef = ElementReference.New(xAddrDirect.Label), DestinationIsIndirect=true, SourceRef = value.Reference, SourceReg = value.Register, SourceIsIndirect = value.IsIndirect };
                        } else {
                            new Move { DestinationValue = xAddrDirect.Address, SourceValue = value.Value.GetValueOrDefault(), SourceRef = value.Reference, SourceReg = value.Register, SourceIsIndirect = value.IsIndirect };
                        }
                    } else {
                        var xAddrIndirect = aAddress as AddressIndirect;
                        if (xAddrIndirect != null) {
                                new Move { DestinationRef = xAddrIndirect.Reference, DestinationDisplacement = xAddrIndirect.Displacement, DestinationValue = xAddrIndirect.Address, DestinationReg = xAddrIndirect.Register, DestinationIsIndirect = true, SourceValue = value.Value.GetValueOrDefault(), SourceRef = value.Reference, SourceReg = value.Register, SourceIsIndirect = value.IsIndirect };
                        } else {
                            throw new Exception("Invalid Address type!");
                        }
                    }
                } else {
                    var xAddrDirect = aAddress as AddressDirect;
                    if (xAddrDirect != null) {
                        if (xAddrDirect.Label != null) {
                            new Move { DestinationRef = ElementReference.New(xAddrDirect.Label), SourceValue = value.Value.GetValueOrDefault(), SourceRef = value.Reference, SourceReg = value.Register, SourceIsIndirect = value.IsIndirect };
                        } else {
                            new Move { DestinationValue = xAddrDirect.Address, SourceValue = value.Value.GetValueOrDefault(), SourceRef = value.Reference, SourceReg = value.Register, SourceIsIndirect = value.IsIndirect };
                        }
                    } else {
                        var xAddrIndirect = aAddress as AddressIndirect;
                        if (xAddrIndirect != null) {
                            new Move { DestinationRef = xAddrIndirect.Reference, DestinationDisplacement = xAddrIndirect.Displacement, DestinationValue = xAddrIndirect.Address, DestinationReg = xAddrIndirect.Register, DestinationIsIndirect = true, SourceValue = value.Value.GetValueOrDefault(), SourceRef = value.Reference, SourceReg = value.Register, SourceIsIndirect = value.IsIndirect };
                        } else {
                            throw new Exception("Invalid Address type!");
                        }
                    }
                }
            }
        }

        public MemoryAction this[Address aAddress, byte aSize] {
            get {
                var xResult = this[aAddress];
                xResult.Size = aSize;
                if (xResult.Reference != null) {
                    xResult.IsIndirect = true;
                }
                return xResult;
            }
            set {
                // ++ operators return ++
                // Maybe later change ++ etc to return actions?
                if (value != null) {
                    if (value.Register != null) {
                        var xAddrDirect = aAddress as AddressDirect;
                        if (xAddrDirect != null) {
                            if (xAddrDirect.Label != null) {
                                new Move {
                                    DestinationRef = ElementReference.New(xAddrDirect.Label),
                                    DestinationIsIndirect = true,
                                    SourceValue = value.Value.GetValueOrDefault(),
                                    SourceRef = value.Reference,
                                    SourceReg = value.Register,
                                    Size = aSize,
                                    SourceIsIndirect = value.IsIndirect
                                };
                            } else {
                                new Move {
                                    DestinationValue = xAddrDirect.Address,
                                    SourceValue = value.Value.GetValueOrDefault(),
                                    SourceRef = value.Reference,
                                    Size = aSize,
                                    SourceReg = value.Register,
                                    SourceIsIndirect = value.IsIndirect
                                };
                            }
                        } else {
                            var xAddrIndirect = aAddress as AddressIndirect;
                            if (xAddrIndirect != null) {
                                new Move {
                                    DestinationRef = xAddrIndirect.Reference,
                                    DestinationDisplacement = xAddrIndirect.Displacement,
                                    DestinationValue = (xAddrIndirect.Address != 0 ? (uint?)xAddrIndirect.Address : (uint?)null),
                                    DestinationReg = xAddrIndirect.Register,
                                    DestinationIsIndirect = true,
                                    SourceValue = value.Value.GetValueOrDefault(),
                                    Size = aSize,
                                    SourceRef = value.Reference,
                                    SourceReg = value.Register,
                                    SourceIsIndirect = value.IsIndirect
                                };
                            } else {
                                throw new Exception("Invalid Address type!");
                            }
                        }
                    } else {
                        var xAddrDirect = aAddress as AddressDirect;
                        if (xAddrDirect != null) {
                            if (xAddrDirect.Label != null) {
                                new Move {
                                    DestinationRef = ElementReference.New(xAddrDirect.Label),
                                    DestinationIsIndirect=true,
                                    SourceValue = value.Value.GetValueOrDefault(),
                                    SourceRef = value.Reference,
                                    SourceReg = value.Register,
                                    Size = aSize,
                                    SourceIsIndirect = value.IsIndirect
                                };
                            } else {
                                new Move {
                                    DestinationValue = xAddrDirect.Address,
                                    SourceValue = value.Value.GetValueOrDefault(),
                                    SourceRef = value.Reference,
                                    SourceReg = value.Register,
                                    Size = aSize,
                                    SourceIsIndirect = value.IsIndirect
                                };
                            }
                        } else {
                            var xAddrIndirect = aAddress as AddressIndirect;
                            if (xAddrIndirect != null) {
                                new Move {
                                    DestinationRef = xAddrIndirect.Reference,
                                    DestinationDisplacement = xAddrIndirect.Displacement,
                                    DestinationValue = xAddrIndirect.Address,
                                    DestinationReg = xAddrIndirect.Register,
                                    DestinationIsIndirect = true,
                                    Size = aSize,
                                    SourceValue = value.Value.GetValueOrDefault(),
                                    SourceRef = value.Reference,
                                    SourceReg = value.Register,
                                    SourceIsIndirect = value.IsIndirect
                                };
                            } else {
                                throw new Exception("Invalid Address type!");
                            }
                        }
                    }
                }
            }
        }
    }
}
