using BizHawk.Common.NumberExtensions;
using System;

namespace BizHawk.Emulation.Common.Components.LR35902
{
	public partial class LR35902
	{
		public void Read_Func(ushort dest, ushort src_l, ushort src_h)
		{
			Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8));
		}

		// speical read for POP AF that always clears the lower 4 bits of F 
		public void Read_Func_F(ushort dest, ushort src_l, ushort src_h)
		{
			Regs[dest] = (ushort)(ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)) & 0xF0);
		}

		public void Write_Func(ushort dest_l, ushort dest_h, ushort src)
		{
			WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h]) << 8), (byte)Regs[src]);
		}

		public void TR_Func(ushort dest, ushort src)
		{
			Regs[dest] = Regs[src];
		}

		public void ADD16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
		{
			int Reg16_d = Regs[dest_l] | (Regs[dest_h]  << 8);
			int Reg16_s = Regs[src_l] | (Regs[src_h] << 8);

			Reg16_d += Reg16_s;

			FlagC = Reg16_d.Bit(16);

			ushort ans_l = (ushort)(Reg16_d & 0xFF);
			ushort ans_h = (ushort)((Reg16_d & 0xFF00) >> 8);

			// redo for half carry flag
			Reg16_d = Regs[dest_l] | ((Regs[dest_h] & 0x0F) << 8);
			Reg16_s = Regs[src_l] | ((Regs[src_h] & 0x0F) << 8);

			Reg16_d += Reg16_s;

			FlagH = Reg16_d.Bit(12);
			FlagN = false;

			Regs[dest_l] = ans_l;
			Regs[dest_h] = ans_h;
		}

		public void ADD8_Func(ushort dest, ushort src)
		{
			int Reg16_d = Regs[dest];
			Reg16_d += Regs[src];

			FlagC = Reg16_d.Bit(8);
			FlagZ = (Reg16_d & 0xFF) == 0;

			ushort ans = (ushort)(Reg16_d & 0xFF);

			// redo for half carry flag
			Reg16_d = Regs[dest] & 0xF;
			Reg16_d += (Regs[src] & 0xF);

			FlagH = Reg16_d.Bit(4);

			FlagN = false;

			Regs[dest] = ans;
		}

		public void SUB8_Func(ushort dest, ushort src)
		{
			int Reg16_d = Regs[dest];
			Reg16_d -= Regs[src];

			FlagC = Reg16_d.Bit(8);
			FlagZ = (Reg16_d & 0xFF) == 0;

			ushort ans = (ushort)(Reg16_d & 0xFF);

			// redo for half carry flag
			Reg16_d = Regs[dest] & 0xF;
			Reg16_d -= (Regs[src] & 0xF);

			FlagH = Reg16_d.Bit(4);
			FlagN = true;

			Regs[dest] = ans;
		}

		public void BIT_Func(ushort bit, ushort src)
		{
			FlagZ = !Regs[src].Bit(bit);
			FlagH = true;
			FlagN = false;
		}

		public void SET_Func(ushort bit, ushort src)
		{
			Regs[src] |= (ushort)(1 << bit);
		}

		public void RES_Func(ushort bit, ushort src)
		{
			Regs[src] &= (ushort)(0xFF - (1 << bit));
		}

		public void ASGN_Func(ushort src, ushort val)
		{
			Regs[src] = val;
		}

		public void SWAP_Func(ushort src)
		{
			ushort temp = (ushort)((Regs[src] << 4) & 0xF0);
			Regs[src] = (ushort)(temp | (Regs[src] >> 4));

			FlagZ = Regs[src] == 0;
			FlagH = false;
			FlagN = false;
			FlagC = false;
		}

		public void SLA_Func(ushort src)
		{
			FlagC = Regs[src].Bit(7);

			Regs[src] = (ushort)((Regs[src] << 1) & 0xFF);

			FlagZ = Regs[src] == 0;
			FlagH = false;
			FlagN = false;		
		}

		public void SRA_Func(ushort src)
		{
			FlagC = Regs[src].Bit(0);

			ushort temp = (ushort)(Regs[src] & 0x80); // MSB doesn't change in this operation

			Regs[src] = (ushort)((Regs[src] >> 1) | temp);

			FlagZ = Regs[src] == 0;
			FlagH = false;
			FlagN = false;
		}

		public void SRL_Func(ushort src)
		{
			FlagC = Regs[src].Bit(0) ? true : false;

			Regs[src] = (ushort)(Regs[src] >> 1);

			FlagZ = Regs[src] == 0;
			FlagH = false;
			FlagN = false;
		}

		public void CPL_Func(ushort src)
		{
			Regs[src] = (ushort)((~Regs[src]) & 0xFF);

			FlagH = true;
			FlagN = true;
		}

		public void CCF_Func(ushort src)
		{
			FlagC = !FlagC;
			FlagH = false;
			FlagN = false;
		}

		public void SCF_Func(ushort src)
		{
			FlagC = true;
			FlagH = false;
			FlagN = false;
		}

		public void AND8_Func(ushort dest, ushort src)
		{
			Regs[dest] = (ushort)(Regs[dest] & Regs[src]);

			FlagZ = Regs[dest] == 0;
			FlagC = false;
			FlagH = true;
			FlagN = false;
		}

		public void OR8_Func(ushort dest, ushort src)
		{
			Regs[dest] = (ushort)(Regs[dest] | Regs[src]);

			FlagZ = Regs[dest] == 0;
			FlagC = false;
			FlagH = false;
			FlagN = false;
		}

		public void XOR8_Func(ushort dest, ushort src)
		{
			Regs[dest] = (ushort)(Regs[dest] ^ Regs[src]);

			FlagZ = Regs[dest] == 0;
			FlagC = false;
			FlagH = false;
			FlagN = false;
		}

		public void CP8_Func(ushort dest, ushort src)
		{
			int Reg16_d = Regs[dest];
			Reg16_d -= Regs[src];

			FlagC = Reg16_d.Bit(8);
			FlagZ = (Reg16_d & 0xFF) == 0;

			// redo for half carry flag
			Reg16_d = Regs[dest] & 0xF;
			Reg16_d -= (Regs[src] & 0xF);

			FlagH = Reg16_d.Bit(4);

			FlagN = true;
		}

		public void RRC_Func(ushort src)
		{
			bool imm = src == Aim;
			if (imm) { src = A; }

			FlagC = Regs[src].Bit(0);

			Regs[src] = (ushort)((FlagC ? 0x80 : 0) | (Regs[src] >> 1));

			FlagZ = imm ? false : (Regs[src] == 0);
			FlagH = false;
			FlagN = false;
		}

		public void RR_Func(ushort src)
		{
			bool imm = src == Aim;
			if (imm) { src = A; }

			ushort c = (ushort)(FlagC ? 0x80 : 0);

			FlagC = Regs[src].Bit(0);

			Regs[src] = (ushort)(c | (Regs[src] >> 1));

			FlagZ = imm ? false : (Regs[src] == 0);
			FlagH = false;
			FlagN = false;
		}

		public void RLC_Func(ushort src)
		{
			bool imm = src == Aim;
			if (imm) { src = A; }

			ushort c = (ushort)(Regs[src].Bit(7) ? 1 : 0);
			FlagC = Regs[src].Bit(7);

			Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c);

			FlagZ = imm ? false : (Regs[src] == 0);
			FlagH = false;
			FlagN = false;
		}

		public void RL_Func(ushort src)
		{
			bool imm = src == Aim;
			if (imm) { src = A; }

			ushort c = (ushort)(FlagC ? 1 : 0);
			FlagC = Regs[src].Bit(7);

			Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c);

			FlagZ = imm ? false : (Regs[src] == 0);
			FlagH = false;
			FlagN = false;
		}

		public void INC8_Func(ushort src)
		{
			int Reg16_d = Regs[src];
			Reg16_d += 1;

			FlagZ = (Reg16_d & 0xFF) == 0;

			ushort ans = (ushort)(Reg16_d & 0xFF);

			// redo for half carry flag
			Reg16_d = Regs[src] & 0xF;
			Reg16_d += 1;

			FlagH = Reg16_d.Bit(4);
			FlagN = false;

			Regs[src] = ans;
		}

		public void DEC8_Func(ushort src)
		{
			int Reg16_d = Regs[src];
			Reg16_d -= 1;

			FlagZ = (Reg16_d & 0xFF) == 0;

			ushort ans = (ushort)(Reg16_d & 0xFF);

			// redo for half carry flag
			Reg16_d = Regs[src] & 0xF;
			Reg16_d -= 1;

			FlagH = Reg16_d.Bit(4);
			FlagN = true;

			Regs[src] = ans;
		}

		public void INC16_Func(ushort src_l, ushort src_h)
		{
			int Reg16_d = Regs[src_l] | (Regs[src_h] << 8);

			Reg16_d += 1;

			Regs[src_l] = (ushort)(Reg16_d & 0xFF);
			Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8);
		}

		public void DEC16_Func(ushort src_l, ushort src_h)
		{
			int Reg16_d = Regs[src_l] | (Regs[src_h] << 8);

			Reg16_d -= 1;

			Regs[src_l] = (ushort)(Reg16_d & 0xFF);
			Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8);
		}

		public void ADC8_Func(ushort dest, ushort src)
		{
			int Reg16_d = Regs[dest];
			int c = FlagC ? 1 : 0;

			Reg16_d += (Regs[src] + c);

			FlagC = Reg16_d.Bit(8);
			FlagZ = (Reg16_d & 0xFF) == 0;

			ushort ans = (ushort)(Reg16_d & 0xFF);

			// redo for half carry flag
			Reg16_d = Regs[dest] & 0xF;
			Reg16_d += ((Regs[src] & 0xF) + c);

			FlagH = Reg16_d.Bit(4);
			FlagN = false;

			Regs[dest] = ans;
		}

		public void SBC8_Func(ushort dest, ushort src)
		{
			int Reg16_d = Regs[dest];
			int c = FlagC ? 1 : 0;

			Reg16_d -= (Regs[src] + c);

			FlagC = Reg16_d.Bit(8);
			FlagZ = (Reg16_d & 0xFF) == 0;

			ushort ans = (ushort)(Reg16_d & 0xFF);

			// redo for half carry flag
			Reg16_d = Regs[dest] & 0xF;
			Reg16_d -= ((Regs[src] & 0xF) + c);

			FlagH = Reg16_d.Bit(4);
			FlagN = true;

			Regs[dest] = ans;
		}

		// DA code courtesy of AWJ: http://forums.nesdev.com/viewtopic.php?f=20&t=15944
		public void DA_Func(ushort src)
		{
			byte a = (byte)Regs[src];

			if (!FlagN)
			{  // after an addition, adjust if (half-)carry occurred or if result is out of bounds
				if (FlagC || a > 0x99) { a += 0x60; FlagC = true; }
				if (FlagH || (a & 0x0f) > 0x09) { a += 0x6; }
			}
			else
			{  // after a subtraction, only adjust if (half-)carry occurred
				if (FlagC) { a -= 0x60; }
				if (FlagH) { a -= 0x6; }
			}

			a &= 0xFF;

			Regs[src] = a;

			FlagZ = a == 0; 
			FlagH = false;
		}

		// used for signed operations
		public void ADDS_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
		{
			int Reg16_d = Regs[dest_l];
			int Reg16_s = Regs[src_l];

			Reg16_d += Reg16_s;

			ushort temp = 0;

			// since this is signed addition, calculate the high byte carry appropriately
			if (Reg16_s.Bit(7))
			{
				if (((Reg16_d & 0xFF) >= Regs[dest_l]))
				{
					temp = 0xFF;
				}
				else
				{
					temp = 0;
				}
			}
			else
			{
				temp = (ushort)(Reg16_d.Bit(8) ? 1 : 0);
			}

			ushort ans_l = (ushort)(Reg16_d & 0xFF);

			// JR operations do not effect flags
			if (dest_l != PCl)
			{
				FlagC = Reg16_d.Bit(8);

				// redo for half carry flag
				Reg16_d = Regs[dest_l] & 0xF;
				Reg16_d += Regs[src_l] & 0xF;

				FlagH = Reg16_d.Bit(4);
				FlagN = false;
				FlagZ = false; 			
			}

			Regs[dest_l] = ans_l;
			Regs[dest_h] += temp;
			Regs[dest_h] &= 0xFF;

		}
	}
}
