﻿#pragma once
#include "efi_reference.h"
#include <efiprot.h>
#include <intrin.h> // Visual Studio kullanıyorsanız
#include "c_defs_uefi_wrapper.h"

static VOID* tx_buf_virt[NUM_TX_DESC];
static VOID* rx_buf_virt[NUM_RX_DESC];
bool is_connected = false;
static UINTN strlen_lwip(const char* str)
{
	const char* s;
	for (s = str; *s; ++s);
	return (s - str);

}

static uint8_t device_mac_addr[6];

// EKLENDİ: netif yapısı GLOBAL olmalıdır!
static struct netif uefi_netif;


typedef struct {
	UINT64 buffer_addr;
	UINT16 length;
	UINT8  cso;
	UINT8  cmd;      // Command byte – set by driver
	UINT8  status;   // Status byte – written by NIC
	UINT8  css;
	UINT16 special;
} __attribute__((packed)) TX_DESC;

typedef struct {
	volatile uint64_t buffer_addr;
	volatile uint16_t length;
	volatile uint16_t csum;
	volatile uint8_t  status;
	volatile uint8_t  errors;
	volatile uint16_t special;
} __attribute__((packed)) RX_DESC;

static TX_DESC* tx_ring_global;
static RX_DESC* rx_ring_global;
static UINT8 tx_buffers_global[NUM_TX_DESC][2048];
static UINT8 rx_buffers_global[NUM_RX_DESC][2048];
static EFI_PHYSICAL_ADDRESS tx_ring_phys = 0;
static EFI_PHYSICAL_ADDRESS rx_ring_phys = 0;
static EFI_PHYSICAL_ADDRESS tx_buffers_phys[NUM_TX_DESC];
static EFI_PHYSICAL_ADDRESS rx_buffers_phys[NUM_RX_DESC];
static gNetworkPCICardInformation nic;
static EFI_PCI_IO_PROTOCOL gEfiPciIOProtocol;
static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL* PciIo;


static void GetMacAddr(UINT64 bar, uint8_t* out_mac);
static EFI_STATUS gReadNetworkCardInformation(gNetworkPCICardInformation* nic);
static void InitDescriptorsAndProgramNIC(UINT64 bar);


static EFI_STATUS InitDmaMemory();
static err_t low_level_output(struct netif* netif, struct pbuf* p);
void uefi_lwip_input(struct netif* netif);
err_t uefi_netif_init(struct netif* netif);
static void PREPARE_LWIP_NETIF();
static bool start_tcp_client();
static void debug_tcp_connect(ip4_addr_t ipaddr, ip4_addr_t netmask, ip4_addr_t gw, ip4_addr_t target_ip);
static void DumpRxState();
EFI_STATUS gReadNetworkCardInformation(gNetworkPCICardInformation* nic)
{
	Status = uefi_call_wrapper(BS->LocateProtocol, 3, &gEfiPciRootBridgeIoProtocolGuid, NULL, (VOID**)&PciIo);
	if (EFI_ERROR(Status)) { Print(L"PCI protocol bulunamadi\n"); return Status; }

	for (UINTN bus = 0; bus < 256; bus++)
		for (UINTN dev = 0; dev < 32; dev++)
			for (UINTN func = 0; func < 8; func++) {
				UINT64 addr = EFI_PCI_ADDRESS(bus, dev, func, 0);
				UINT16 vendor, device;
				UINT8 class, subclass;

				PciIo->Pci.Read(PciIo, EfiPciWidthUint16, addr, 1, &vendor);
				if (vendor == 0xFFFF) continue;

				PciIo->Pci.Read(PciIo, EfiPciWidthUint16, addr + 2, 1, &device);
				PciIo->Pci.Read(PciIo, EfiPciWidthUint8, addr + 0x0B, 1, &class);
				PciIo->Pci.Read(PciIo, EfiPciWidthUint8, addr + 0x0A, 1, &subclass);

				if (class == 0x02) { // Network Controller
					nic->VENDOR_CODE = vendor;
					nic->DEVICE_CODE = device;
					nic->BUS = bus;
					nic->DEV = dev;
					nic->FUNC = func;



					UINT32 bar_low = 0;
					UINT32 bar_high = 0;
					UINT64 final_bar = 0;

					// 1. Önce BAR0'ın (0x10) sadece ilk 32 bitini oku
					PciIo->Pci.Read(PciIo, EfiPciWidthUint32, addr + 0x10, 1, &bar_low);

					// 2. Kontrol et: Bu adres 64-bit mi? (Bit 2 kontrolü: 0x04 maskesi)
					if ((bar_low & 0x04)) {
						// Evet, 64-bit. O zaman üst 32 biti BAR1'den (0x14) oku
						PciIo->Pci.Read(PciIo, EfiPciWidthUint32, addr + 0x14, 1, &bar_high);

						// İki parçayı birleştir ve alt 4 biti (nitelik bitleri) temizle
						// Maskeyi (UINT64) tipine zorlayarak üst bitlerin silinmesini engelle
						final_bar = (((UINT64)bar_high << 32) | (bar_low & ~(UINT64)0xF));
					}
					else {
						// Hayır, 32-bit bir adres. Sadece alt bitleri temizle
						final_bar = (UINT64)(bar_low & ~0xF);
					}

					nic->BAR0_ADDRESS = final_bar;

					Print(L"Network device bulundu: BUS %d DEV %d FUNC %d, bar0-addr:0x%lx\n", bus, dev, func, nic->BAR0_ADDRESS);
					return EFI_SUCCESS;
				}
			}
	return EFI_NOT_FOUND;
}

static void ProgramNicRegisters(UINT64 bar) {
	// TX ring
	mmio_write32(bar, 0x3800, (uint32_t)(tx_ring_phys & 0xFFFFFFFF));   // TDBAL
	mmio_write32(bar, 0x3804, (uint32_t)(tx_ring_phys >> 32));          // TDBAH
	mmio_write32(bar, 0x3808, NUM_TX_DESC * sizeof(TX_DESC));           // TDLEN
	mmio_write32(bar, 0x3810, 0);                                       // TDH
	mmio_write32(bar, 0x3818, 0);                                       // TDT

	// RX ring
	mmio_write32(bar, 0x2800, (uint32_t)(rx_ring_phys & 0xFFFFFFFF));   // RDBAL
	mmio_write32(bar, 0x2804, (uint32_t)(rx_ring_phys >> 32));          // RDBAH
	mmio_write32(bar, 0x2808, NUM_RX_DESC * sizeof(RX_DESC));           // RDLEN
	mmio_write32(bar, 0x2810, 0);                                       // RDH
	mmio_write32(bar, 0x2818, NUM_RX_DESC - 1);                         // RDT (başlangıçta tüm ring boş)

	// MTA (Multicast Table Array) temizle
	for (int i = 0; i < 128; i++)
		mmio_write32(bar, 0x5200 + (i * 4), 0);
}

EFI_STATUS NIC_INITIALIZE(EFI_HANDLE ImageHandle)
{
	EFI_STATUS Status;
	UINTN HandleCount;
	EFI_HANDLE* HandleBuffer;
	UINT64 Supported = -1;

	// 1. Sistemdeki tüm PCI IO protokolüne sahip aygıtları bul
	Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer);

	if (EFI_ERROR(Status)) return Status;

	for (UINTN i = 0; i < HandleCount; i++) {
		EFI_PCI_IO_PROTOCOL* PciIoTmp;
		Status = gBS->OpenProtocol(HandleBuffer[i], &gEfiPciIoProtocolGuid, (VOID**)&PciIoTmp, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);

		if (EFI_ERROR(Status)) continue;

		// 2. Doğru kartı bul (i219-V Vendor ID: 0x8086)
		UINT32 PciId[2]; // Vendor ve Device ID
		PciIoTmp->Pci.Read(PciIoTmp, EfiPciWidthUint32, 0, 1, PciId);

		if ((PciId[0] & 0xFFFF) == 0x8086) { // Intel
			// Burada Device ID kontrolü de yapabilirsin (0x15B8 vb.)
			PciIo = PciIoTmp;
			Print(L"Kart bulundu ve PciIo baglandi.\n");
			break;
		}
	}


	// Sadece 'Get' yerine 'Supported' sorgusu yapalım
	// 3. parametreye 0 vermek yerine tum maskeyi sorguluyoruz
	Status = PciIo->GetAttributes(PciIo, EfiPciIoAttributeOperationSupported, 0, &Supported);

	if (EFI_ERROR(Status)) {
		Print(L"Desteklenen ozellikler sorgulanamadi: %r\n", Status);
		return Status;
	}

	// Sadece donanimin destekledigi nitelikleri acmaya zorla
	UINT64 EnableMask = Supported & (EFI_PCI_IO_ATTRIBUTE_MEMORY | EFI_PCI_IO_ATTRIBUTE_BUS_MASTER);

	Print(L"Desteklenen: 0x%lx, Aktif Edilecek: 0x%lx\n", Supported, EnableMask);

	Status = PciIo->GetAttributes(PciIo, EfiPciIoAttributeOperationEnable, EnableMask, NULL);


	gReadNetworkCardInformation(&nic);

	uint64_t bar = nic.BAR0_ADDRESS;
	GetMacAddr(bar, device_mac_addr);
	uint32_t val;

	// 1. ADIM: Donanım Kimliği Kontrolü
	// QEMU e1000e genellikle 0x10D3 veya 0x100E ID'sini kullanır.
	// i219-V ise 0x15B8, 0x15D7 gibi ID'lere sahiptir.
	BOOLEAN isQemu = (nic.DEV == 0x10D3 || nic.DEV == 0x100E);

	// 2. ADIM: PCI Config Ayarları (Hepsinde ortak)
	// D0 Modu ve Command Register (Bus Master/Memory Space) işlemleri burada yapılmalı.

	// 3. ADIM: Kritik Register Müdahalesi (Sadece Fiziksel i219 için)
	// SWSM (0x0014) - Sadece gerçek donanımda var.
	val = mm_read32(bar + 0x0014);
	mm_write32(bar + 0x0014, val | 0x1);

	// FEXTNVM6 (0x5B10) - SMBus kilidini açar.
	val = mm_read32(bar + 0x5B10);
	mm_write32(bar + 0x5B10, val | 0x1);

	mm_write32(bar + 0x0410, 0x0060200A);

	// 4. ADIM: Global Reset (Her iki ortamda da güvenli bit: 26)
	// Bit 26 (RST) hem e1000e hem i219 için standart reset bitidir.
	uint32_t ctrl = mm_read32(bar + 0x0000);
	mm_write32(bar + 0x0000, ctrl | (1 << 26));

	// 5. ADIM: Sonsuz Döngü Korumalı Bekleme (Timeout)
	// Donmayı engelleyen en kritik kısım burasıdır.
	int timeout = 100000;
	while ((mm_read32(bar + 0x0000) & (1 << 26)) && --timeout > 0) {
		StallEBS_LWIP(10, 3700);
	}

	if (timeout == 0) {
		Print(L"Hata: Reset zaman asimi! Kart cevap vermiyor.\n");
		return EFI_DEVICE_ERROR;
	}

	// 6. ADIM: Reset sonrası kartı yapılandır ve Link'i zorla aç
	uint32_t final_ctrl = mm_read32(bar + 0x0000);
	final_ctrl |= (1 << 6);  // SLU (Set Link Up) - BU ÇOK KRİTİK!
	final_ctrl |= (1 << 5);  // ASDE (Auto-Speed Detection)
	mm_write32(bar + 0x0000, final_ctrl);

	uint32_t tctl = mm_read32(bar + 0x0400);
	tctl |= (1 << 1); // EN
	tctl |= (1 << 3); // PSP (pad short packets)
	mm_write32(bar + 0x0400, tctl);

	// --- LOOPBACK EKLEMESİ BURAYA ---
	uint32_t rctl = mm_read32(bar + 0x0100); // 0x0100 = RCTL Register
	rctl |= (1 << 1);    // EN: Receiver Enable
	rctl |= (1 << 15);   // BAM: Broadcast Accept Mode (DHCP için ŞART)
	rctl |= (1 << 4);    // MPE: Multicast Promiscuous (IPv6 ve bazı DHCP senaryoları için)
	rctl |= (1 << 3);    // UPE: Unicast Promiscuous (Debug için her şeyi alalım)
	// Boyut ayarı: 2048 byte (Bit 17:16 = 00) - Sizin buffer'larınızla uyumlu
	rctl &= ~(3 << 16);

	mm_write32(bar + 0x0100, rctl);

	StallEBS_LWIP(100000, 3700); // 100 ms bekle
	rctl &= ~(1 << 0);  // RST'yi temizle
	mm_write32(bar + 0x0100, rctl);
	// --------------------------------

	// 7. ADIM: Link'in fiziksel olarak oturması için bekleme (Stall)
	Print(L"Link oturmasi bekleniyor (1.5 sn)...\n");

	StallEBS_LWIP(1500000, 3700); // 100 ms bekle

	// 8. ADIM: Gerçek Durum Kontrolü
	uint32_t final_status = mm_read32(bar + 0x0008);
	Print(L"Son Durum STATUS: 0x%08x\n", final_status);

	if (final_status & (1 << 1)) { // STATUS_LU kontrolü
		Print(L"Link Basariyla Kuruldu!\n");
		
	}
	else {
		Print(L"Hata: Link kurulamadi. Kabloyu kontrol edin.\n");
		return EFI_DEVICE_ERROR;
	}

	//InitDescriptorsAndProgramNIC(bar);

	InitDmaMemory();
	StallEBS_LWIP(3000000,3700);
	ProgramNicRegisters(bar);
	StallEBS_LWIP(3000000, 3700);

	PREPARE_LWIP_NETIF();
	StallEBS_LWIP(3000000, 3700);

	if (final_status != 0 && final_status != 0xFFFFFFFF) { Status = EFI_SUCCESS; }
	gBS->FreePool(HandleBuffer);

	if (final_status != 0 && final_status != 0xFFFFFFFF) { Status = EFI_SUCCESS; }
	else { Status = EFI_DEVICE_ERROR; }
}

void GetMacAddr(UINT64 bar, uint8_t* out_mac) {
	// 1. EEPROM/NVM'in hazır olup olmadığını kontrol et (EECD Register)
	// i219 ve e1000e'de EE_PRES (Bit 8) Flash'ın varlığını ve okunduğunu gösterir
	uint32_t eecd = mm_read32(bar + 0x0010);
	if (!(eecd & (1 << 8))) {
		Print(L"Uyari: EEPROM henüz hazir degil, veriler hatali olabilir!\n");
	}

	// 2. RAL ve RAH oku
	uint32_t ral = mm_read32(bar + 0x5400);
	uint32_t rah = mm_read32(bar + 0x5404);

	// 3. Verileri dışarıdan gelen adrese yaz (Dizi kopyalama)
	out_mac[0] = (uint8_t)(ral & 0xFF);
	out_mac[1] = (uint8_t)((ral >> 8) & 0xFF);
	out_mac[2] = (uint8_t)((ral >> 16) & 0xFF);
	out_mac[3] = (uint8_t)((ral >> 24) & 0xFF);
	out_mac[4] = (uint8_t)(rah & 0xFF);
	out_mac[5] = (uint8_t)((rah >> 8) & 0xFF);

	Print(L"Gercek MAC Adresi: %02x:%02x:%02x:%02x:%02x:%02x\n", out_mac[0], out_mac[1], out_mac[2], out_mac[3], out_mac[4], out_mac[5]);
}

static void InitDescriptorsAndProgramNIC(UINT64 bar)
{
	// Fiziksel pointer atamaları (UEFI pointer==physical mapping varsayımı)
	tx_ring_phys = (EFI_PHYSICAL_ADDRESS)(UINTN)&tx_ring_global[0];
	rx_ring_phys = (EFI_PHYSICAL_ADDRESS)(UINTN)&rx_ring_global[0];

	for (int i = 0; i < NUM_TX_DESC; i++) {
		tx_buffers_phys[i] = (EFI_PHYSICAL_ADDRESS)(UINTN)&tx_buffers_global[i][0];
		tx_ring_global[i].buffer_addr = (UINT64)tx_buffers_phys[i];
		tx_ring_global[i].status = 0;
		tx_ring_global[i].length = 0;
	}
	for (int i = 0; i < NUM_RX_DESC; i++) {
		rx_buffers_phys[i] = (EFI_PHYSICAL_ADDRESS)(UINTN)&rx_buffers_global[i][0];
		rx_ring_global[i].buffer_addr = (UINT64)rx_buffers_phys[i];
		rx_ring_global[i].status = 0;
		rx_ring_global[i].length = 0;
	}

	// Register yazımlarını SafeMmio ile yap:
	mmio_write32(bar, TDBAL, (uint32_t)(tx_ring_phys & 0xFFFFFFFF));
	mmio_write32(bar, TDBAH, (uint32_t)(tx_ring_phys >> 32));
	mmio_write32(bar, TDLEN, NUM_TX_DESC * sizeof(TX_DESC));
	mmio_write32(bar, TDH, 0);
	mmio_write32(bar, TDT, 0);

	mmio_write32(bar, RDBAL, (uint32_t)(rx_ring_phys & 0xFFFFFFFF));
	mmio_write32(bar, RDBAH, (uint32_t)(rx_ring_phys >> 32));
	mmio_write32(bar, RDLEN, NUM_RX_DESC * sizeof(RX_DESC));
	mmio_write32(bar, RDH, 0);
	mmio_write32(bar, RDT, NUM_RX_DESC - 1);

	// MTA clear - burayı da SafeMmioRead/Write ile yapabiliriz veya doğrudan yaz.
	for (int i = 0; i < 128; i++) {
		mmio_write32(bar, 0x5200 + (i * 4), 0);
	}
}

static EFI_STATUS InitDmaMemory()
{
	Print(L"DMA buffer'lari hazirlaniyor ve map'leniyor...\n");
	EFI_STATUS Status;

	VOID* tx_ring_virt = NULL;
	VOID* rx_ring_virt = NULL;
	VOID* tx_mapping = NULL;
	VOID* rx_mapping = NULL;
	UINTN tx_ring_size = NUM_TX_DESC * sizeof(TX_DESC);
	UINTN rx_ring_size = NUM_RX_DESC * sizeof(RX_DESC);

	// -----------------------
	// TX RING (COMMON BUFFER)
	// -----------------------
	Status = PciIo->AllocateBuffer(
		PciIo,
		AllocateAnyPages,
		EfiBootServicesData,
		EFI_SIZE_TO_PAGES(tx_ring_size),
		&tx_ring_virt,
		0
	);
	Print(L"TX ring buffer'i ayirma durumu: %r\n", Status);
	if (EFI_ERROR(Status)) {
		Print(L"TX ring buffer'i ayiramadi: %r\n", Status);
		return Status;
	}

	// sıfırla
	SetMem(tx_ring_virt, tx_ring_size, 0);

	Status = PciIo->Map(
		PciIo,
		EfiPciIoOperationBusMasterCommonBuffer,
		tx_ring_virt,
		&tx_ring_size,
		&tx_ring_phys,    // GLOBAL kullanılıyor (önceden gölgeleniyordu)
		&tx_mapping
	);
	if (EFI_ERROR(Status)) {
		Print(L"TX ring buffer'i map'lenemedi: %r\n", Status);
		return Status;
	}

	tx_ring_global = (TX_DESC*)tx_ring_virt;

	// -----------------------
	// RX RING (COMMON BUFFER)
	// -----------------------
	Status = PciIo->AllocateBuffer(
		PciIo,
		AllocateAnyPages,
		EfiBootServicesData,
		EFI_SIZE_TO_PAGES(rx_ring_size),
		&rx_ring_virt,
		0
	);
	if (EFI_ERROR(Status)) {
		Print(L"RX ring buffer'i ayiramadi: %r\n", Status);
		return Status;
	}

	// sıfırla
	SetMem(rx_ring_virt, rx_ring_size, 0);

	Status = PciIo->Map(
		PciIo,
		EfiPciIoOperationBusMasterCommonBuffer,
		rx_ring_virt,
		&rx_ring_size,
		&rx_ring_phys,    // GLOBAL kullanılıyor
		&rx_mapping
	);
	if (EFI_ERROR(Status)) {
		Print(L"RX map fail: %r size:%lu addr:%p\n", Status, rx_ring_size, rx_ring_virt);
		return Status;
	}

	rx_ring_global = (RX_DESC*)rx_ring_virt;

	// -----------------------
	// TX BUFFERS (COMMON BUFFER)
	// -----------------------
	for (int i = 0; i < NUM_TX_DESC; i++) {
		VOID* buf = NULL;

		Status = PciIo->AllocateBuffer(
			PciIo,
			AllocateAnyPages,
			EfiBootServicesData,
			EFI_SIZE_TO_PAGES(2048),
			&buf,
			0
		);
		if (EFI_ERROR(Status)) {
			Print(L"TX buffer %d ayiramadi: %r\n", i, Status);
			return Status;
		}

		// sıfırla
		SetMem(buf, 2048, 0);

		VOID* mapping = NULL;
		UINTN buf_size = 2048;
		Status = PciIo->Map(
			PciIo,
			EfiPciIoOperationBusMasterCommonBuffer,
			buf,
			&buf_size,
			&tx_buffers_phys[i],
			&mapping
		);
		if (EFI_ERROR(Status)) {
			Print(L"TX buffer %d map'lenemedi: %r\n", i, Status);
			return Status;
		}

		Print(L"TX buffer %d: virt=%p, phys=0x%lx\n", i, buf, tx_buffers_phys[i]);
		tx_buf_virt[i] = buf;
		tx_ring_global[i].buffer_addr = (UINT64)tx_buffers_phys[i];
		tx_ring_global[i].status = 0;
		tx_ring_global[i].length = 0;
		tx_ring_global[i].cmd = 0;
	}

	// -----------------------
	// RX BUFFERS (COMMON BUFFER)
	// -----------------------
	for (int i = 0; i < NUM_RX_DESC; i++) {
		VOID* buf = NULL;

		Status = PciIo->AllocateBuffer(
			PciIo,
			AllocateAnyPages,
			EfiBootServicesData,
			EFI_SIZE_TO_PAGES(2048),
			&buf,
			0
		);
		if (EFI_ERROR(Status)) {
			Print(L"RX buffer %d ayiramadi: %r\n", i, Status);
			return Status;
		}

		// sıfırla
		SetMem(buf, 2048, 0);

		VOID* mapping = NULL;
		UINTN buf_size = 2048;
		Status = PciIo->Map(
			PciIo,
			EfiPciIoOperationBusMasterCommonBuffer,
			buf,
			&buf_size,
			&rx_buffers_phys[i],
			&mapping
		);
		if (EFI_ERROR(Status)) {
			Print(L"RX buffer %d map'lenemedi: %r\n", i, Status);
			return Status;
		}

		rx_buf_virt[i] = buf;
		rx_ring_global[i].buffer_addr = (UINT64)rx_buffers_phys[i];
		rx_ring_global[i].status = 0;
		rx_ring_global[i].length = 0;
	}
	Print(L"DMA buffer'lari basariyla hazirlandi ve map'lendi.\n");

	return EFI_SUCCESS;
}

static uint16_t tx_tail = 0;

EFI_STATUS e1000_transmit_packet(UINT64 bar, UINT8* packet_data, UINT16 length) {
	// 1. İlgili descriptor'ı seç

	// 2. KOPYALAMA İŞLEMİ
	// ÖNEMLİ: Kopyalama yapılacak adres (dest), işlemcinin erişebildiği HOST adresidir.
	// Eğer 'tx_buffers_host' dizisinde bu adresleri saklıyorsan onu kullanmalısın.
	void* dest = tx_buf_virt[tx_tail];

	if (dest == NULL || packet_data == NULL) {
		Print(L"Hata: Kopyalama adresi veya paket verisi NULL!\n");
		return EFI_INVALID_PARAMETER;
	}

	// Seçenek A: Standart memcpy (Eğer kütüphane bağlıysa)
	// memcpy(dest, packet_data, length);

	// Seçenek B: UEFI Standart CopyMem (EBS öncesi en güvenli yol)
	gBS->CopyMem(dest, packet_data, length);

	// 3. Descriptor Ayarları
	tx_ring_global[tx_tail].length = length;
	Print(L"TX: Paket kopyalandi, uzunluk=%d, index=%d\n", tx_ring_global[tx_tail].length, tx_tail);

	// Paket içeriğinin ilk 16 byte'ını yazdır
	unsigned char* pkt_data = (unsigned char*)(uintptr_t)tx_ring_global[tx_tail].buffer_addr;
	Print(L"Data (ilk 16B): ");
	for (int i = 0; i < 16 && i < tx_ring_global[tx_tail].length; i++) {
		// %02x formatı her zaman 2 hane yazdırılmasını sağlar (Örn: 0A, FF)
		Print(L"%02x ", pkt_data[i]);
	}
	Print(L"\n");

	tx_ring_global[tx_tail].status = 0;
	tx_ring_global[tx_tail].cmd = (0x01 | 0x02 | 0x08); // EOP, IFCS, RS

	// 4. Kartı Tetikle
	tx_tail = (tx_tail + 1) % NUM_TX_DESC;
	mmio_write32(bar, 0x3818, tx_tail); // TDT (Transmit Descriptor Tail)

	return EFI_SUCCESS;
}

// Daha önce InitDmaMemory'de map ettiğin host adreslerini kullanacağız
// Eğer rx_buffers_global statik bir diziyse (gNetworkCard.h'daki gibi):

static uint16_t rx_current = 0;

EFI_STATUS e1000_receive_packet(UINT64 bar, UINT8* out_buffer, UINT16* out_length) {
	RX_DESC* desc = &rx_ring_global[rx_current];
	if (!(desc->status & 0x01)) {
		Print(L"Yeni paket yok, RX_DESC[%d] status=0x%02x\n", rx_current, desc->status);
		return EFI_NOT_READY;
	}
	*out_length = desc->length;
	if (*out_length > 2048) {
		Print(L"Hata: Alinan paket boyutu cok buyuk! length=%d\n", *out_length);
		return EFI_DEVICE_ERROR; }

	UINT8* src = (UINT8*)rx_buf_virt[rx_current];
	gBS->CopyMem(out_buffer, src, *out_length);

	// Descriptor'ı donanıma geri ver
	desc->status = 0;
	uint16_t old_rx = rx_current;
	rx_current = (rx_current + 1) % NUM_RX_DESC;
	// RDT = son kullanılabilir descriptor (bir önceki)
	uint16_t rdt_val = (rx_current == 0) ? (NUM_RX_DESC - 1) : (rx_current - 1);
	mmio_write32(bar, RDT, rdt_val);

	return EFI_SUCCESS;
}

static EFI_STATUS SEND_TEST_PACKET() {
	uint8_t my_packet[64];
	UINTN payload_len = strlen_lwip("Merhaba Dunya!"); // 16
	UINTN header_len = 14;
	UINTN packet_len = header_len + payload_len;       // 30
	UINTN padded_len = (packet_len < 60) ? 60 : packet_len; // minimum 60 (CRC hariç)

	memset(&my_packet[0], 0xFF, 6);
	memcpy(&my_packet[6], device_mac_addr, 6);
	my_packet[12] = 0xBE;
	my_packet[13] = 0xEF;
	gBS->CopyMem(&my_packet[14], "Merhaba Dunya!", payload_len);
	if (padded_len > packet_len)
		memset(&my_packet[packet_len], 0, padded_len - packet_len);

	Print(L"Gönderiliyor (gerçek uzunluk=%d, padded=%d)\n", packet_len, padded_len);
	return e1000_transmit_packet(nic.BAR0_ADDRESS, my_packet, (UINT16)padded_len);
}

static int RECEIVE_TEST_PACKET() {
	UINT8 rx_buffer[2048];
	UINT16 packet_len = 0;
	static BOOLEAN first_no_packet = TRUE;
	EFI_STATUS Status = e1000_receive_packet(nic.BAR0_ADDRESS, rx_buffer, &packet_len);
	// Her döngüde "Yeni paket var mı?" diye soruyoruz
	if (Status == EFI_SUCCESS) {
		// Paket geldi! 
		// Burada LwIP kullanacaksan: pbuf_alloc ile yer açıp paket verisini kopyalayıp
		// netif->input(p, netif) şeklinde yukarı katmana göndermelisin.
		//Print(L"Paket alindi, boyutu: %d\n", packet_len);
		first_no_packet = FALSE;
		Print(L"Paket alindi, boyutu: %d\n", packet_len);
		return (int)packet_len;
	}
	else {
		Print(L"Yeni paket yok, durum: %r\n", Status);
	}

	if (first_no_packet) {
		Print(L"Henüz yeni paket yok.\n");
		first_no_packet = FALSE;
	}

	return 0;
}

static void PrintNICRegisterStates(bool ShouldPrint) {
	if (ShouldPrint)
	{

			uint64_t bar = nic.BAR0_ADDRESS;

			for (int i = 0; i < 4 && i < NUM_RX_DESC; ++i)
			{
				Print(L"RX_DESC[%d]: status=0x%02x length=%d buf=0x%lx\n",i,rx_ring_global[i].status,rx_ring_global[i].length,(rx_ring_global[i].buffer_addr));
			};

			// NIC registers (RDBAL, RDBAH, RDLEN, RDH, RDT)
			uint32_t rdbal = mm_read32(bar + RDBAL);
			uint32_t rdbah = mm_read32(bar + RDBAH);
			uint32_t rdlen = mm_read32(bar + RDLEN);
			uint32_t rdh = mm_read32(bar + RDH);
			uint32_t rdt = mm_read32(bar + RDT);
			Print(L"NIC REG: RDBAL=0x%08x RDBAH=0x%08x RDLEN=0x%08x RDH=%u RDT=%u\n", rdbal, rdbah, rdlen, rdh, rdt);
		
	}
}

static void PREPARE_LWIP_NETIF() {

	// 1. ÖNCE lwip_init() çağrılmalı. 
	lwip_init();

	// 2. IP Yapılandırması
	ip4_addr_t ipaddr, netmask, gw, target_ip;
	//IP4_ADDR(&ipaddr, 192, 168, 0, 13);
	//IP4_ADDR(&netmask, 255, 255, 255, 0);
	//IP4_ADDR(&gw, 192, 168, 0, 1);
	IP4_ADDR(&ipaddr, 0, 0, 0, 0);
	IP4_ADDR(&netmask, 0, 0, 0, 0);
	IP4_ADDR(&gw, 0, 0, 0, 0);

	if (netif_add(&uefi_netif, &ipaddr, &netmask, &gw, NULL, uefi_netif_init, ethernet_input) == NULL) {
		Print(L"Hata: netif_add basarisiz!\n");
		return;
	}

	netif_set_default(&uefi_netif);
	netif_set_up(&uefi_netif);
	netif_set_link_up(&uefi_netif);
	Print(L"Network arayuzu hazir, DHCP baslatiliyor...\n");
	StallEBS_LWIP(5000000,3700);


	DumpRxState(false);

	err_t dhcp_start_r = dhcp_start(&uefi_netif);	StallEBS_LWIP(6000000, 3700);

	if (dhcp_start_r!=ERR_OK)
	{
		Print(L"DHCP BASLATILAMADI!\n");
		return;
	}
	else
	{
		Print(L"DHCP baslatildi, IP aliniyor...\n");
	}
	struct dhcp dhcpx;

	dhcp_inform(&uefi_netif);
	dhcp_set_struct(&uefi_netif, &dhcpx);

	UINTN wait_time = 0;
	UINTN dbg_tick = 0;
	UINT8 rx_buffer[2048];
	UINT16 packet_len = 0;
	StallEBS_LWIP(6000000,3700);

	while (!dhcp_supplied_address(&uefi_netif) && wait_time < 1500)
	{
		// RX poll
		sys_check_timeouts();
		uefi_lwip_input(&uefi_netif);

		if ((dbg_tick++ % 10) == 0)
		{
			uint64_t bar = nic.BAR0_ADDRESS;
			PrintNICRegisterStates(false);
		}

		Print(L"DHCP: IP var mi?: %d.%d.%d.%d\n",
			ip4_addr1(&uefi_netif.ip_addr),
			ip4_addr2(&uefi_netif.ip_addr),
			ip4_addr3(&uefi_netif.ip_addr),
			ip4_addr4(&uefi_netif.ip_addr));

		StallEBS_LWIP(1000, 3700); // 0.1 saniye
		wait_time ++;
	}

	if (!dhcp_supplied_address(&uefi_netif))
	{
		Print(L"DHCP IP alinamadi!\n");
	}

	//debug_tcp_connect(ipaddr, netmask, gw, ipaddr);
}




static err_t low_level_output(struct netif* netif, struct pbuf* p) {
	// 1. Geçici bir buffer oluşturun (e1000 descriptor boyutu kadar)
	// Statik olması hızı artırır ancak re-entrancy'e dikkat edilmeli
	static uint8_t send_buffer[2048];

	if (p->tot_len > 2048) {
		Print(L"Hata: Gönderilecek paket çok büyük! Uzunluk: %d\n", p->tot_len);
		return ERR_BUF; // Paket çok büyükse gönderilemez
	}

	// 2. pbuf zincirindeki tüm parçaları tek bir düz (linear) buffer'a kopyalayın
	// LwIP'nin bu fonksiyonu parçalı pbuf'ları birleştirir.
	pbuf_copy_partial(p, send_buffer, p->tot_len, 0);

	// 3. Kendi yazdığınız TX fonksiyonunu çağırın
	// Not: nic.BAR0_ADDRESS'e erişim için global nic yapısını kullanın
	EFI_STATUS status = e1000_transmit_packet(nic.BAR0_ADDRESS, send_buffer, (UINT16)p->tot_len);

	if (status == EFI_SUCCESS) {
		// LINK_OUTPUT_DEBUG_DATA(p); // İsteğe bağlı: Gönderilen paketi hex dump yapın
		Print(L"Paket gönderildi, uzunluk: %d\n", p->tot_len);
		return ERR_OK;
	}
	else {
		Print(L"Hata: Paket gönderilemedi, durum: %r\n", status);
		return ERR_IF; // Interface hatası
	}
}

void uefi_lwip_input(struct netif* netif) {
	UINT8 rx_buffer[2048];
	UINT16 packet_len = 0;

	// Tek bir 'if' yerine 'while' kullanarak tüm kuyruğu temizleyin
	if (e1000_receive_packet(nic.BAR0_ADDRESS, rx_buffer, &packet_len) == EFI_SUCCESS) {
		struct pbuf* p = pbuf_alloc(PBUF_RAW, packet_len, PBUF_POOL);
		if (p != NULL) {
			pbuf_take(p, rx_buffer, packet_len);
			if (netif->input(p, netif) != ERR_OK) {
				Print(L"ERROR WHILE NETIF->INPUT\n");
				pbuf_free(p);
			}
		}
		else {
			Print(L"PBUF IS NULL!");
		}
	}
}

err_t uefi_netif_init(struct netif* netif) {
	netif->name[0] = 'e';
	netif->name[1] = '0';
	
	// IP katmanından gelen paketleri Ethernet katmanına (ARP ekleyerek) iletir
	netif->output = etharp_output;

	// Ethernet paketlerini doğrudan karta yazan senin fonksiyonun
	netif->linkoutput = low_level_output;

	netif->mtu = 1500;

	// Donanım (MAC) adresi uzunluğu ve kopyalanması
	netif->hwaddr_len = 6;
	uefi_memcpy(netif->hwaddr, device_mac_addr, 6);

	// KRİTİK BAYRAKLAR:
	// 1. NETIF_FLAG_BROADCAST: DHCP Discover paketleri için ŞART.
	// 2. NETIF_FLAG_ETHARP: ARP tablosu tutulması için ŞART.
	// 3. NETIF_FLAG_LINK_UP: Fiziksel bağlantının varlığını belirtir.
	// 4. NETIF_FLAG_UP: LwIP'nin bu arayüzü "aktif" görmesini sağlar.
	// 5. NETIF_FLAG_IGMP: Multicast desteği (tavsiye edilir).
	netif->flags = NETIF_FLAG_BROADCAST |
		NETIF_FLAG_ETHARP |
		NETIF_FLAG_LINK_UP |
		NETIF_FLAG_UP |
		NETIF_FLAG_IGMP;

	return ERR_OK;
}




err_t connect_callback(void* arg, struct tcp_pcb* tpcb, err_t err) {
	char* msg = "UEFI'den selamlar!";
	tcp_write(tpcb, msg, strlen_lwip(msg), TCP_WRITE_FLAG_COPY);
	tcp_output(tpcb); // Paketi hemen gönder
	Print(L"TCP bağlantısı kuruldu, mesaj gönderildi.\n");
	is_connected = TRUE;
	return ERR_OK;
}
err_t error_callback(void* arg, err_t err) {
	Print(L"TCP bağlantı hatası: %d\n", err);
	return ERR_OK;
}

//OUT OF USE FOR NOW , BUT KEEP FOR REFERENCE USING-> debug_tcp_connect FUNCTION
static bool start_tcp_client() { 
	struct tcp_pcb* pcb = tcp_new();
	StallEBS_LWIP(50000, 3700);

	ip4_addr_t remote_ip;
	IP4_ADDR(&remote_ip, 192, 168, 0, 13);
	tcp_err(pcb, connect_callback);

	err_t tcp_conn_r = tcp_connect(pcb, &remote_ip, 80, connect_callback);
	StallEBS_LWIP(50000, 3700);

	Print(L"TCP connect sonucu: %d\n", (s8_t*)tcp_conn_r);

	// Döngüye hemen girin, return true; ile çıkmayın!
	while (1) {
		sys_check_timeouts();
		StallEBS_LWIP(1, 3700);
	}
	// return true; // Burası gereksiz
}

struct pbuf* read_packet()
{


	// Örnek basit polling: current_rx_desc gösteriyor beklenen descriptor
	RX_DESC* desc = &rx_ring_global[rx_current];

	if (!(desc->status & 0x01)) {
		// DD set değil, paket yok
		return NULL;
	}

	uint16_t len = desc->length;
	if (len == 0 || len > 2048) {
		// hata veya geçersiz
		desc->status = 0;
		// return descriptor to NIC
	
		//mm_write32(nic.BAR0_ADDRESS + RDT, rx_current);
		//rx_current = (rx_current + 1) % NUM_RX_DESC;
		// 1. Önce yazılım tarafındaki okuma göstergemizi (pointer) bir sonraki index'e kaydırıyoruz.
		// Çünkü şu anki index'teki paketi okuduk ve işimizi bitirdik.
		rx_current = (rx_current + 1) % NUM_RX_DESC;

		// 2. RDT (Receive Descriptor Tail) değerini hesaplıyoruz.
		// NIC'e "benim şu an beklediğim index'in bir gerisine kadar tüm kuyruğu kullanabilirsin" diyoruz.
		// Eğer rx_current 0 olduysa (başa sardıysa), bir gerisi kuyruğun en sonudur (NUM_RX_DESC - 1).
		uint16_t rdt_val = (rx_current == 0) ? (NUM_RX_DESC - 1) : (rx_current - 1);

		// 3. Hesapladığımız kuyruk bitiş noktasını donanıma bildiriyoruz.
		mm_write32(nic.BAR0_ADDRESS + RDT, rdt_val);
		return NULL;
	}

	struct pbuf* p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

	void* src = (void*)(UINTN)rx_ring_global[rx_current].buffer_addr;
	pbuf_take(p, src, len);

	if (p == NULL) {
		// bellek yok - drop ve geri ver descriptor
		desc->status = 0;
		mm_write32(nic.BAR0_ADDRESS + RDT, rx_current);
		rx_current = (rx_current + 1) % NUM_RX_DESC;
		return NULL;
	}

	if (uefi_netif.input(p, &uefi_netif) != ERR_OK) {
		pbuf_free(p);
	}




	// descriptor temizle ve RDT güncelle
	desc->status = 0;
	mm_write32(nic.BAR0_ADDRESS + RDT, rx_current);
	rx_current = (rx_current + 1) % NUM_RX_DESC;

	return p;
}

static void debug_tcp_connect(ip4_addr_t ipaddr, ip4_addr_t netmask, ip4_addr_t gw, ip4_addr_t target_ip)
{
	Print(L"\n=== TCP CONNECT DEBUG START ===\n");
	struct tcp_pcb* pcb;

	Print(L"DEBUG: IP Config - IP: %d.%d.%d.%d, GW: %d.%d.%d.%d\n",
		(ip4_addr1(&ipaddr)),
		(ip4_addr2(&ipaddr)),
		(ip4_addr3(&ipaddr)),
		(ip4_addr4(&ipaddr)),
		(ip4_addr1(&gw)),
		(ip4_addr2(&gw)),
		(ip4_addr3(&gw)),
		(ip4_addr4(&gw)));


	Print(L"\n=== TCP CONNECT DEBUG ===\n");

	pcb = tcp_new();
	if (pcb == NULL) {
		Print(L"ERR: tcp_new failed\n");
		return;
	}

	Print(L"DEBUG: tcp_new OK, pcb=%p\n", pcb);
	Print(L"DEBUG: uefi_netif state: flags=0x%x, up=%d\n",
		uefi_netif.flags,
		(uefi_netif.flags & NETIF_FLAG_UP) ? 1 : 0);

	tcp_err(pcb, error_callback);
	Print(L"DEBUG: tcp_err callback set\n");

	Print(L"DEBUG: Calling tcp_connect to %d.%d.%d.%d:80...\n",
		(ip4_addr1(&target_ip)),
		(ip4_addr2(&target_ip)),
		(ip4_addr3(&target_ip)),
		(ip4_addr4(&target_ip)));
	gBS->Stall(5000000);

	err_t err = tcp_connect(pcb, &target_ip, 80, connect_callback);
	Print(L"DEBUG: tcp_connect returned: %d\n", (int)err);

	Print(L"DEBUG: Waiting for ARP/SYN...\n");
	for (int i = 0; i < 50; ++i) {
		struct pbuf* p = read_packet();
		if (p != NULL) {
			Print(L"DEBUG: Packet received! len=%d\n", p->tot_len);
			err_t input_err = uefi_netif.input(p, &uefi_netif);
			Print(L"DEBUG: netif.input returned: %d\n", (int)input_err);
			if (input_err != ERR_OK) {
				pbuf_free(p);
			}
		}

		StallEBS_LWIP(100, 3700);

		if (i % 10 == 0) {
			Print(L"DEBUG: tick %d...\n", i);
		}
	}

	int timeout_ms = 1000;
	int waited = 0;
	while (!is_connected && waited < timeout_ms) {
		struct pbuf* p;
		while ((p = read_packet()) != NULL) {
			if (uefi_netif.input(p, &uefi_netif) != ERR_OK) {
				pbuf_free(p);
			}
		}
		Print(L"DEBUG: Packet check done, waited %d ms\n", waited);

		StallEBS_LWIP(1000, 3700);
		waited++;
	}

	if (!is_connected) {
		Print(L"TCP connect failed or timed out\n");
		tcp_abort(pcb);
		return;
	}

	Print(L"TCP connected, normal operation can continue\n");
	Print(L"=== END TCP CONNECT DEBUG ===\n\n");
	tcp_abort(pcb);
}

static void DumpRxState(bool shouldPrint)
{
	if (!shouldPrint) { return; }
	uint64_t bar = nic.BAR0_ADDRESS;
	Print(L"\n--- DUMP RX STATE ---\n");
	uint32_t rdbal = mm_read32(bar + RDBAL);
	uint32_t rdbah = mm_read32(bar + RDBAH);
	uint32_t rdlen = mm_read32(bar + RDLEN);
	uint32_t rdh = mm_read32(bar + RDH);
	uint32_t rdt = mm_read32(bar + RDT);
	Print(L"NIC REG: RDBAL=0x%08x RDBAH=0x%08x RDLEN=0x%08x RDH=%u RDT=%u\n", rdbal, rdbah, rdlen, rdh, rdt);

	Print(L"NUM_RX_DESC(calc) = %u, sizeof(RX_DESC) = %u\n", rdlen / sizeof(RX_DESC), (UINTN)sizeof(RX_DESC));
	for (int i = 0; i < (int)(rdlen / sizeof(RX_DESC)); ++i) {
		Print(L"RX[%02d]: status=0x%02x length=%u buf_phys=0x%lx buf_virt=%p mapped_phys=0x%lx\n",
			i,
			rx_ring_global[i].status,
			rx_ring_global[i].length,
			rx_ring_global[i].buffer_addr,
			rx_buf_virt[i],
			(rx_buffers_phys[i]));
	}
	Print(L"uefi_netif.flags=0x%x hwaddr=%02x:%02x:%02x:%02x:%02x:%02x\n",
		uefi_netif.flags,
		uefi_netif.hwaddr[0], uefi_netif.hwaddr[1], uefi_netif.hwaddr[2],
		uefi_netif.hwaddr[3], uefi_netif.hwaddr[4], uefi_netif.hwaddr[5]);
	Print(L"--- END DUMP ---\n\n");
}

