﻿using System.IO;
using unwind.core.disasm;

namespace unwind.core.helpers
{
    public class X86PrettyPrinter : X86DisasemblerBase
    {
        private TextWriter @out;

        public X86PrettyPrinter(TextWriter @out)
        {
            this.@out = @out;
        }

        public override void PushR32(R32 r)
        {
            @out.WriteLine("push {0}", r);
        }

        public override void PopR32(R32 r)
        {
            @out.WriteLine("pop {0}", r);
        }

        public override void Push(byte imm8)
        {
            @out.WriteLine("push {0:X}h", SignExtend(imm8));
        }

        public override void Push(int imm32)
        {
            @out.WriteLine("push {0:X}h", imm32);
        }

        public override void Test(R32 x, R32 y)
        {
            @out.WriteLine("test {0},{1}", x, y);
        }

        public override void Mov(R32 b, byte disp)
        {
            @out.WriteLine("mov EAX,dword ptr [{0}+{1:X}h]", b, disp);
        }

        public override void Mov(R32 r, R32 b, byte disp)
        {
            @out.WriteLine("mov {0},dword ptr [{1}+{2:X}h]", r, b, disp);
        }

        public override void MovR32Imm32(R32 x, int i)
        {
            @out.WriteLine("mov {0},dword ptr [{1:X}h]", x, i);
        }

        public override void MovIndirect(R32 x, R32 y)
        {
            @out.WriteLine("mov {0},dword ptr [{1}]", x, y);
        }

        public override void Mov(Destination d, R32 x, R32 y)
        {
            @out.WriteLine("mov {0},{1}", x, y);
        }

        public override void MovIndirect(int imm32)
        {
            @out.WriteLine("mov EAX,dword ptr [{0:X}h]", imm32);
        }

        public override void MovIndirect(R32 r, byte disp)
        {
            @out.WriteLine("mov dword ptr [ESP+{0:X}h],{1}", disp, r);
        }

        public override void Mov(R32 r, Scale s, R32 b, R32 i)
        {
            @out.WriteLine("mov {0},dword ptr [{1}+{2}]", r, b, i);
        }

        public override void Alu(ALU a, int imm32)
        {
            @out.WriteLine("{0} EAX,{1:X}h", a.ToString().ToLower(), imm32);
        }

        public override void Jump(Jump j, int rel32)
        {
            if (rel32 > 0)
            {
                @out.WriteLine("{0} EIP+{1:X}h", j, rel32);
            }
            else
            {
                @out.WriteLine("{0} EIP-{1:X}h", j, -rel32);
            }
        }

        public override void Jump(Jump j, sbyte rel8)
        {
            if (rel8 > 0)
            {
                @out.WriteLine("{0} EIP+{1:X}h", j, rel8);
            }
            else
            {
                @out.WriteLine("{0} EIP-{1:X}h", j, -rel8);
            }
        }

        public override void Alu(ALU a, Destination d, R32 x, R32 y)
        {
            @out.WriteLine("{0} {1},{2}", a.ToString().ToLower(), x, y);
        }

        public override void Alu(ALU a, Destination d, R32 r, R32 b, byte imm8)
        {
            if (d == Destination.R)
            {
                @out.WriteLine("{0} {1},dword ptr [{2}+{3:X}h]", a.ToString().ToLower(), r, b, imm8);
            }
            else
            {
                @out.WriteLine("{0} dword ptr [{1}+{2:X}h],{3}", a.ToString().ToLower(), b, imm8, r);
            }
        }

        public override void Alu(ALU a, R32 r, byte imm8)
        {
            @out.WriteLine("{0} {1},{2:X}h", a.ToString().ToLower(), r, SignExtend(imm8));
        }

        public override void Alu(ALU a, Scale s, R32 b, byte disp, byte imm8)
        {
            @out.WriteLine("{0} dword ptr [{1}+{2:X}h],{3:X}h", a.ToString().ToLower(), b, disp, SignExtend(imm8));
        }

        public override void Cmp(int imm32)
        {
            @out.WriteLine("cmp EAX,{0:X}h", imm32);
        }

        public override void Ret(short imm16)
        {
            @out.WriteLine("ret {0}", imm16);
        }

        public override void Lea(R32 r, Scale s, R32 b, int imm8)
        {
            if (s == Scale.X1)
            {
                @out.WriteLine("lea {0},[{1}+{2:X}h]", r, b, imm8);
            }
            else
            {
                @out.WriteLine("lea {0},[{1}+{2:X}h*{3}]", r, b, imm8, (1 << (int)s));
            }
        }

        public override void Lea(R32 r, Scale s, R32 i, R32 b)
        {
            @out.WriteLine("lea {0},[{1}+{2}*{3}]", r, b, i, (1 << (int) s));
        }

        public override void Dec(R32 r)
        {
            @out.WriteLine("dec {0}", r);
        }
    }
}
