#pragma once
#include <atomic>
#include <chrono>
#include <functional>
#include <utility>
#include <vector>
#include <vulkan/vulkan_core.h>

#include "fiko/core/temel.hpp"

typedef struct VmaAllocator_T* VmaAllocator;
typedef struct VmaAllocation_T* VmaAllocation;

namespace fiko {

class Pencere;

// ============================================================================
// VulkanAygit — UYGULAMA-GLOBAL Vulkan altyapısı (MDI: tek örnek, tüm pencereler
// paylaşır). instance + fiziksel/mantıksal aygıt + kuyruk + VMA + komut havuzu +
// paylaşılan render pass'ler (swapchain formatı + offscreen viewport) + sampler +
// tek-seferlik/toplu transfer + emekli tampon geri dönüşümü + device-lost yönetimi.
// Pencere-BAŞINA olan her şey (surface/swapchain/framebuffer/present) PencereYuzeyi'nde.
// ============================================================================
class VulkanAygit {
public:
  // Ctor bir Pencere alır: device SEÇİMİ present-uyumlu kuyruk ailesi ister →
  // geçici surface oluşturulur, aygıt seçildikten sonra yıkılır (surface pencere-
  // başına, PencereYuzeyi kendi kalıcısını kurar). Grafik+present aynı aile varsayılır.
  explicit VulkanAygit(Pencere& ilkPencere);
  ~VulkanAygit();

  VulkanAygit(const VulkanAygit&) = delete;
  VulkanAygit& operator=(const VulkanAygit&) = delete;

  // ---- Çekirdek Vulkan tanıtıcıları ----
  VkInstance instance() const { return instance_; }
  VkPhysicalDevice fizikselAygit() const { return fizikselAygit_; }
  VkDevice aygit() const { return aygit_; }
  VkQueue grafikKuyruk() const { return grafikKuyruk_; }
  u32 grafikAile() const { return grafikAile_; }
  VmaAllocator ayirici() const { return ayirici_; }
  VkCommandPool komutHavuzu() const { return komutHavuzu_; }
  const char* aygitAdi() const { return aygitAdi_; }
  bool dxInterop() const { return dxInterop_; }
  bool anizotropiVar() const { return anizotropiVar_; }
  bool dokuDizisiVar() const { return dokuDizisiVar_; }
  f32 enBuyukAnizotropi() const { return enBuyukAnizotropi_; }

  // ---- Paylaşılan render pass'ler ----
  // Swapchain geçişi: format-parametreli, lazy + cache (tüm pencereler aynı format).
  VkRenderPass swapchainGecis(VkFormat format);
  VkRenderPass viewportGecis() const { return vpGecis_; }
  VkSampler viewportOrnekleyici() const { return vpOrnekleyici_; }

  // ---- Kare sayacı + emekli tampon geri dönüşümü (global) ----
  // Monoton kare sayacı: PencereYuzeyi::kareBasla her kare ilerletir (GpuTampon
  // ping-pong ayni-kare guard'ı + emekli süzme buna bakar).
  u64 kareSayac() const { return kareSayac_; }
  void kareIlerlet();  // kareSayac_++ + emekli tamponları süz (fence beklendikten sonra)
  // Son kare ilerlemesinden bu yana geçen süre (ms; kare yoksa -1). Yavaş transfer
  // teşhisi: fence beklemesi UZUNSA ve son kare AZ önceyse, transfer uçuştaki karenin
  // arkasında kuyrukta bekliyordur (sunum geri-basıncı) — T73'ün açık sorusu.
  f64 sonKaredenBeriMs() const;
  void tamponEmekliEt(VkBuffer tampon, VmaAllocation tahsis);



  // ---- WSI İZ HALKASI (teşhis; davranışa dokunmaz) ----
  // Resize'da GERÇEK VK_ERROR_DEVICE_LOST alınıyor (A maddesi) ve doğrulama katmanı
  // AÇIKKEN hiç tekrarlanmıyor = zamanlama yarışı. Yarışı görebilmek için son N WSI
  // olayı (acquire/present/recreate + kare/görsel indeksleri) halkada tutulur ve aygıt
  // kaybı işaretlenince LOGA DÖKÜLÜR. Tahsis yok, kilit yok — sıcak yol güvenli.
  void izKaydet(const char* etiket, int kare, int gorsel, int sonuc);
  void izDok();  // aygitKaybiIsaretle otomatik çağırır
  void aygitHatasiDok();  // VK_EXT_device_fault raporu (destekliyse)

  // ---- ADRES İZLEME (VK_EXT_device_address_binding_report) ----
  // Sürücü, GPU bellek aralıklarının hangi NESNEYE bağlandığını/çözüldüğünü raporlar.
  // `device_fault`in verdiği hatalı adresi (READ_INVALID) NESNEYE bağlamanın tek yolu.
  // PAHALI (her tahsis/serbest bırakmada geri çağrım) → VARSAYILAN KAPALI; yalnız
  // teşhis için `FIKO_ADRES_IZ=1` ortam değişkeniyle ya da bu bayrakla açılır.
  // Cihaz OLUŞTURULMADAN ÖNCE ayarlanmalıdır (uzantı orada açılır).
  static bool& adresIzleme();
  void adresKaydet(u64 taban, u64 boyut, int nesneTur, u64 nesne, bool bagla);
  void adresAra(u64 adres);  // hatalı adresi izde ara ve LOGLA
  void adresElcisiKur();


  // ---- Tek-seferlik + toplu transfer (staging upload) ----
  void anindaGonder(const std::function<void(VkCommandBuffer)>& kayit);
  bool topluYuklemeAktif() const { return topluAktif_; }
  void topluYuklemeBasla();
  void topluYuklemeBitir();
  void topluKopyaKaydet(VkBuffer kaynak, VmaAllocation kaynakTahsis, VkBuffer hedef, u64 bayt);

  // AÇILIŞTAN SONRAKİ İLK GÖVDE ~1 SANİYE (ÖLÇÜLDÜ 07-21, başsız oturum): 24 vertekslik
  // kutuda `topluYuklemeBitir` submit 0.0 ms + fence 997.7 ms; ikinci gövde 3 ms.
  // ÇÜRÜTÜLEN HİPOTEZ: "sürücünün ilk transfer ilklemesi" — ana döngüde ilk kareden
  // sonra yapılan gerçek bir 256B staging transferi 0 ms sürdü, buna rağmen ilk gövde
  // yine 995 ms aldı (ısınma yolu bu yüzden KALDIRILDI, ölü kod bırakılmadı).
  // Kalan açıklama: transfer, acquire semaforunu bekleyen İLK KAREnin arkasına giriyor
  // (aynı oturumda ilk kare de 920 ms sürüyor) = T56 sunum geri-basıncı. Başsız/DWM'siz
  // oturumda sunum periyodu ~1 sn olduğundan bu ölçüm ORTAM ARTEFAKTI olabilir →
  // gerçek masaüstünde RUNTIME TEST gerekiyor (bkz. fikatia-runtime-test-2026-07-21.md).

  // ---- Device-idle + device-lost yönetimi ----
  // Timeout'lu device-idle: VK_SUCCESS dışında false + aygitKayip_ işaretlenir.
  // `nerede`: teşhis etiketi — 100 ms'yi aşan beklemeler kimin yüzünden diye loglanır
  // (ÖLÇÜLDÜ 07-21: resize'da kare ~945 ms "bitmiyor"; acquire suçsuz, şüphe device-idle).
  bool bekle(const VkFence* citler = nullptr, u32 citSayi = 0, const char* nerede = "?");
  bool aygitKayipMi() const { return aygitKayip_; }
  void aygitKaybiIsaretle(const char* nerede, VkResult sonuc);

  // Sonsuz bekleme yasak: makul üst sınır (GPU hung ise VK_TIMEOUT döner).
  static constexpr u64 bekleZamanAsimi = 2'000'000'000ull;  // 2 sn (ns)

  // GEÇİCİ VK_TIMEOUT ≠ aygıt kaybı. Ölçüldü (07-21, 400 adımlık resize fırtınası):
  // tek bir 2 sn'lik fence gecikmesi "GPU aygiti kayip → temiz kapanisa geciliyor"
  // tetikliyor, süreç kapanmıyor ve PENCERE İÇİ DONUYOR (kullanıcı şikayeti birebir).
  // Gerçek hang ancak ART ARDA tolerans kadar zaman aşımıyla anlaşılır; aradaki tek
  // başarılı kare sayacı sıfırlar. VK_ERROR_DEVICE_LOST ise anında kayıptır (tolere yok).
  // true  = tolere edildi, kareyi atla ve devam et
  // false = tolerans aşıldı, aygıt kayıp işaretlendi (eski davranış)
  bool zamanAsimiTolere(const char* nerede, VkResult sonuc);
  void zamanAsimiSifirla() { zamanAsimiUstUste_ = 0; }
  static int& zamanAsimiTolerans();  // AyarSistemi kaydı (uygulama katmanı bağlar)

private:
  void instanceOlustur();
  void hataElcisiKur();  // DEBUG: VK_EXT_debug_utils messenger → gunluk (S2)
  void aygitSec(VkSurfaceKHR geciciYuzey);
  void aygitOlustur();
  void ayiriciOlustur();
  void komutVeTransferOlustur();
  void viewportGecisOlustur();
  void emekliTemizle(bool hepsi);

  VkInstance instance_ = VK_NULL_HANDLE;
  VkDebugUtilsMessengerEXT hataElcisi_ = VK_NULL_HANDLE;  // DEBUG-only (S2)
  VkPhysicalDevice fizikselAygit_ = VK_NULL_HANDLE;
  VkDevice aygit_ = VK_NULL_HANDLE;
  VkQueue grafikKuyruk_ = VK_NULL_HANDLE;
  u32 grafikAile_ = 0;
  char aygitAdi_[256] = {};
  VmaAllocator ayirici_ = nullptr;
  bool dxInterop_ = false;
  bool anizotropiVar_ = false;
  bool dokuDizisiVar_ = false;
  f32 enBuyukAnizotropi_ = 1.0f;

  // Paylaşılan swapchain render pass (ilk pencere formatını belirler, cache'lenir).
  VkRenderPass swapchainGecis_ = VK_NULL_HANDLE;
  VkFormat swapchainGecisFormat_ = VK_FORMAT_UNDEFINED;
  // Offscreen viewport geçişi + sampler (sabit format — kViewport*Format).
  VkRenderPass vpGecis_ = VK_NULL_HANDLE;
  VkSampler vpOrnekleyici_ = VK_NULL_HANDLE;

  VkCommandPool komutHavuzu_ = VK_NULL_HANDLE;
  VkCommandBuffer transferKb_ = VK_NULL_HANDLE;
  VkFence transferCiti_ = VK_NULL_HANDLE;
  bool topluAktif_ = false;
  u32 topluKopyaSayi_ = 0;
  std::vector<std::pair<VkBuffer, VmaAllocation>> topluStaging_;

  struct WsiIz { const char* etiket; int kare; int gorsel; int sonuc; u64 sira; };
  static constexpr u32 kIzSayi = 48;
  WsiIz iz_[kIzSayi]{};
  u64 izSira_ = 0;

  struct EmekliTampon { VkBuffer tampon; VmaAllocation tahsis; u64 kare; };
  std::vector<EmekliTampon> emekli_;
  u64 kareSayac_ = 0;
  std::chrono::steady_clock::time_point sonKareZamani_{};  // teşhis (sonKaredenBeriMs)
  bool aygitKayip_ = false;
  bool aygitHataUzantisi_ = false;  // VK_EXT_device_fault açık mı
  bool adresUzantisi_ = false;      // VK_EXT_device_address_binding_report açık mı
  struct AdresIz { u64 taban, boyut, nesne; int nesneTur; bool bagla; u64 sira; };
  static constexpr u32 kAdresSayi = 512;
  std::vector<AdresIz> adresIz_;
  u64 adresSira_ = 0;
  VkDebugUtilsMessengerEXT adresElci_ = VK_NULL_HANDLE;
  int zamanAsimiUstUste_ = 0;  // art arda tolere edilen VK_TIMEOUT sayısı

  // Emekli süzme toleransı (ucusteKare+1): PencereYuzeyi ile aynı olmalı.
  static constexpr u32 ucusteKare = 2;
};

}  // namespace fiko
